Quantum 1.0.0
Quantum 1.0: declarative web applications in XML, with AI and RAG built into the language. No build chain, no JavaScript, no front-end framework.
The road from the first public release (0.9.0) to here was mostly about making that sentence true, and only that sentence:
- What is promised is written down.
SUPPORT_TIERS.mdsplits the language into Core (the web framework: components, state, conditions, loops, functions, parameterised queries and transactions, actions and forms, composition, files, mail, the Core set ofui:*, authentication), AI (q:llm,q:knowledge,q:agent), Experimental and Laboratory, and the engine enforces it: a tag outside Core and AI warns once when it runs (0.10.0, 0.21.0). - What a program means is specified.
SPEC.mdstates the Core and AI as rules with IDs, frozen at 1.0, and every rule is cited by a conformance test that fails if the rule and the runtime disagree (tests/conformance/, withtest_spec_ids.pychecking both directions). Decisions that used to be silent — a missing name, a failed expression, a missing key, an attribute that did nothing — are errors that say where they are (0.11.0 through 1.0). - What is claimed is run. Apps in the repository prove the tiers:
projects/tarefas(the UI engine),projects/blogandprojects/helpdesk(Core, files and mail),projects/bank-transfer(q:transaction),projects/docs-assistantandprojects/shop-agent(AI).quantum test(0.22.0) tests an app in its own language, on a fresh database built from its migrations; the apps' suites run in CI.FEATURE_STATUS.mdis generated by executing every example, never written by hand. - AI you can build on. Answers cite the chunks they came from and say when they are grounded, a relevance floor keeps the model from answering from nothing, answers can stream, and every AI tag has a failure contract (
onerror, time budgets) instead of an exception in the page (0.20.0, 0.22.0). - One page, three renderers. The same
.qpage withui:*is drawn by the browser (quantum start), the terminal (quantum console) and a desktop window (quantum desktop), with forms that know their action's rules, tables from data and a declared responsive layout (0.15.0 through 0.18.0). - Tools around the language.
quantum check(pages, SQL and query fields against the database),migrate planfrom aschema.sql, change history, the/_devpanel, errors that point to the line (0.17.0, 0.19.0). - In English. Code, messages, tests, the SPEC and the documentation.
The changes since 0.22.0 — released as part of 1.0, with no 0.23 of their own — are below.
1.0: changes since 0.22.0
Added
get(x, key, default)in expressions reads a key that may be missing, or a list index that may be out of range, and givesdefault(nullwhen left out) (EXPR-16).quantum start --hot-reload(and--hot-reload-port): the open pages reload when a component or static file is saved, CSS changes restyle without reloading, and a.qthat no longer parses shows its error on the page (DEV-4). The watcher, the WebSocket and the page script existed, but no command started them.random(),random(a, b),chance(p)andpick(list)in expressions (EXPR-15).The HTML a
.qaccepts is a rule (PARSE-4): boolean attributes, a bare&, void elements left open,<inside a quoted attribute value and named HTML entities.CI checks the game projects (snake, tictactoe, kenney-platformer) build to valid JavaScript, serve their game and are committed up to date (Laboratory job).
Breaking
- Laboratory (2D game engine and Godot codegen): the game names are neutral. A game now writes
death-sequence="classic", callsgame.setPatrolAI, and uses the Godot codegen's tagsitem_blockandbonus_coin, its HUD counterbonus_coins, its sound triggerplayer-diedand its eventitem-block-hit; the world map moves the spriteid="player". The 0.22 names for these are no longer accepted. Behaviour is otherwise the same; the games in the repository were migrated and rebuilt. - Admin:
GET /datasources/{id}/logsanswers failures with an HTTP status and adetailinstead of 200 and anerrorkey: 404 for an unknown datasource or a container that no longer exists, 409 for a datasource with no container, 503 when Docker is not available or does not answer.?tail=is unchanged; the datasource logs dialog reads the status. d['missing']on an object without that key is an error that names the key and suggests a similar one, asd.missingalready was (EXPR-16, was G19); it used to benull, so a misspelt key rendered nothing. Read an optional key withget(d, 'key', default)or test it with'key' in d.skip_rowsonq:dataskips the first lines of the file, before the header, as the name says; it used to read the first line as the header and then drop data rows (DATA-1).- Attributes that were accepted and did nothing are parse errors that say so (PARSE-3):
modelonq:knowledge(the model is chosen onq:llm, IA-2),uniqueonq:set(useoperation="unique"), every attribute ofq:columnbutnameandtype(required,default,validate,pattern,min,max,minlength,maxlength,range,enum; filter rows withq:transform), andq:flashoutside aq:action(ACT-3). incompares like==(EXPR-14):'5' in [5]and5 in ['5']are true — a value from a form now finds its number in a list.inon something that is not a list, an object or a text is an error.q:setwithouttypekeeps the type of a value that is exactly one expression, likeq:returnand props (SET-5):value="{[1, 2]}"stores the list,value="{len(x)}"the number,value="{dateAdd('h', 8)}"the date. It used to store text, solen()counted characters and{n + 1}on a stored number failed. Text with several parts ("{n} items") and literals ("007") are still text. To keep the old behaviour, writetype="string"— the blog's and the admin's login pages now do, for the session's expiry.- A value outside an attribute's list is a parse error with the line (PARSE-5):
q:settype,operation,scopeandvalidate(date,datetime,structandnullwere accepted asq:settypes and did nothing);q:looptype;q:paramtype(insideq:queryonly the types the query understands —numberthere is now reported where it is written, it failed when the query ran);q:invokemethodandauthType. An unknownauthTypeused to send the request with no credentials; an unknown form-field type was plain text. q:loop items=over something that is not a list is an error (LOOP-6), in a statement and in markup, like aui:tablesource:items="{5}"looped once over"5", and in markup a missing value drew zero rows. Give a value that may not exist yet a list first:<q:set name="session.cart" type="array" value="{session.cart}" default="[]"/>.q:invoke responseFormat="json"on a response that is not JSON is a failure (INV-2), with the Content-Type and the start of the body; it used to succeed with{"error", "text"}as the value.
Removed
quantum deployandquantum apps: they talked to a deploy service that was never published, so for anyone else they could only fail. Serve an app withquantum startbehind your own server (DEPLOYMENT.md).- The examples built on third-party game art that cannot be redistributed; the games that remain use the Kenney assets (CC0).
- The Codecov upload from CI: it needed a repository token and was failing (429) without one; the coverage floor is enforced in CI by
coverage report --fail-under. - The admin's deploy feature, which lived only in the legacy FastAPI backend (
quantum_admin/backend; the.qadmin that ships never had it) and deployed nothing real: DeployService, the deploy pipeline with versions and rollback, the per-project CI/CD retry, auto-deploy on a webhook push, the cloud integrations (AWS, Kubernetes, Azure, GCP), the deploy-only environment fields and the deploy screens. The generic Docker features for datasources stay; webhooks still record events; aglobal.yamlwith adeployment:block and an admin database with deploy columns still load. projects/quantum-landing: a marketing page that promised what does not ship (version 1.0.0, one-command deploy, the game engine as a feature) and linked to demos that no longer exist.projects/llm-demo: an LLM chat superseded byprojects/docs-assistant; its script inserted the server's reply into the page withinnerHTML(XSS).projects/quantum-rag: a RAG demo over a stale copy of the guide, superseded byprojects/docs-assistant.projects/quantum-fighter: only a prebuiltgame.html, with no.qsource to build it from.projects/quantum-towerand its copyexamples/tower_defense.q: the game called runtime functions that do not exist (scheduleOnce,createSprite,attachBehavior,removeClickable,clickedSprite), so no wave came and no tower could be built, and eight of its images and sounds were never committed.
Fixed
- The admin's pinned stack is current: fastapi 0.109 (January 2024) -> 0.141, with starlette 1.x, pydantic 2.13, pydantic-settings 2.15, uvicorn 0.53, httpx 0.28 and websockets 16 (uvicorn's
standardextra needs >= 13). CI had been testing a different admin than the one developers run. Starlette 1.x's TestClient useshttpx2(pinned alongside; the admin itself still callshttpx), and the pytest filter for the old Starlette's anyio deprecation is gone: 0 warnings without it. quantum console: after a pause in a search-as-you-type field, the redrawn field got the focus with its text selected, so the next key replaced what had been typed ("b", pause, "a" searched "a"). It keeps the text, with the cursor at the end (UI-12). The console tests wait for the search of what was typed, not for a page load; the browser test waits for the URL of the whole text.- The editor schemas offer the values the parser accepts:
q:param typelisteddatetimefor every param (only aq:query's takes it) and lackedtime,q:invoke methodlackedHEAD/OPTIONS, andvalidatedid not say a regular expression starting with^is accepted (it was an enum, so the language server flagged one).test_editor_schemas.pycompares every closed list the parser checks with the schema. - In HTML content, an expression that uses a scope variable inside something larger —
{session.visits + 1},{session.n > 0},{session.user.name}— rendered empty although the variable existed (it was read as the session key "visits + 1");cookie.xcould not be read inside an expression at all. It is read as inq:set, everywhere (EXPR-11). q:set type=int,long,numeric,float,doubleandtext— the namesq:paramaccepts — convert likeinteger,number,decimalandstring; they were accepted and converted nothing (type="int"stored the text "7") (ERR-1).- The test suite no longer writes into the repository. It left
test_data/quantum_test.db,quantum_jobs.db,logs/,.quantum/knowledgeand page bundles instatic/in the checkout; each writer now uses a temporary folder, and the rootconftest.pyfails the run, naming the files, when a test writes into the repository (git status, ignored files included, before and after). - The job threads of
q:job,q:scheduleandq:threadare the process's and stop when asked. The server builds a service container per request, and each built its own job executor: every request to a page withq:jobstarted one more queue worker, and every request withq:scheduleone more scheduler running the same schedule again, none of them ever stopped. The executor is now shared per job database; its workers wait on a stop signal instead of sleeping,stop_workers()/shutdown()join them (a job in progress finishes first), and they are shut down when the process exits. components/bank_transfer_demo.qnever ran (it queried adefaultdatasource with tables nobody created, and readflashbefore it existed) and moved money wrongly: an overdraft or a transfer to an account that does not exist debited one side and credited nobody. It is nowprojects/bank-transfer, the proof app forq:transaction(DB-4): a SQLite config, a migration with three accounts and aCHECK (balance >= 0), both accounts checked before the transaction, and aquantum testsuite in CI.tests/apps/test_ui_parity_script.pyfailed now and then under-n auto: a pilot's pause could return while a button's Pressed message was still bubbling to the app, so the test read the page before the click's page load had started (measured: 1 click in 40).quantum consolecounts finished page loads (pages_loaded), and the console tests wait for the load their own action caused (tests/console_pilot.py) — no sleeps, no retries.- The editor tooling describes the language the parser accepts: quantum-lsp's Core and AI tags have exactly the attributes the parser reads and does not refuse (vscode-quantum's q:llm had no
knowledge/top/minRelevance; the agent, tool and data sub-tags were missing), and both tools stop offeringq:persist,q:route,q:component basePath/health/metrics/traceand the other removed attributes. vscode-quantum's Core/AI schema and grammar are generated from quantum-lsp (scripts/generate-editor-schemas.py), its snippets all parse, andtests/conformance/test_editor_schemas.pyfails when either drifts from the parser. model=onq:llm,q:agentandq:teamagents takes expressions, likeendpoint=andapiKey=;model="{m}"was sent to the server literally (IA-1).range=on aq:actionparam is checked, as on aq:functionparam (ACT-2, FN-1).encoding=onq:datareads the file (or response) in that encoding; every file was read as UTF-8 (DATA-1).- A tool argument the model leaves out takes its
q:param'sdefault(IA-4). - The PARSE-3 test checks that a field is read by the code that runs its node, not only that its name appears somewhere in the runtime.
- A date in the session came back shifted by the server's UTC offset (Flask read a date without a timezone as UTC): it comes back the same date, and
sessionExpirymay be a date or ISO text (SET-5). - The redis message broker (
MESSAGE_BROKER_TYPE=redis,quantum mq) could not load: its module importedmessage_brokerby a bare name, so asking for it said "requires 'redis' package" even with redis installed. - A
q:actionread and wrote an empty application scope of its own:application.xset in an action was lost and could not be read there; actions share the server's application scope, as pages do (EXEC-3). projects/quantum-chatworks again (every post was a 500): its forms areq:actions, its dead runtime patches and a committed session key are gone, and aquantum testsuite runs in CI.projects/quantum-terminal's download links answered 404 underquantum start.- The error for
<q:application type="testing">andqtest:tags namesquantum testas available; it said "coming in a later release" (APP-2). quantum startwith hot reload: stopping the reload server no longer leaves its event loop and sockets behind ("RuntimeError: Event loop is closed" printed later); it uses the current websockets API.- Reconfiguring logging closes the previous log files instead of leaking them; the websocket transport closes its event loop on shutdown.
- Admin: no deprecation warnings (datetime.utcnow, Pydantic class Config, FastAPI on_event); an unreachable duplicate GET /datasources/{id}/logs route was removed; the deploy health check and the project server start/stop no longer leak a connection or a child process.
services:modules are no longer re-executed when the registry is reloaded; their classes stay the same objects.- Dates bound to SQLite use explicit adapters (the same ISO text), instead of the default ones Python 3.12 deprecated.
- The test suite runs with no warnings and no flaky failures (17 runs in a row, shuffled): a test left pytest's own PID where
quantum stopwould kill it. - A persisted
q:knowledgebase is stored in the working directory it is indexed from (IA-2). The default./.quantum/knowledgewas handed to ChromaDB as a relative path, which it keys one store per process by, so a process that indexed bases from two directories wrote both to the first (and failed with "Failed to get segments" once it was gone — a flaky CI test). In one component, apersist="false"base no longer made the bases after it in-memory too. quantum stopno longer kills a process it did not start (RUN-3): a stale.quantum.pidwhose PID the system had given to another program made it kill that program. The file records each process's start time, and a PID whose process started at another time is left alone (the stale file is removed, exit 1). A.quantum.pidwritten by 0.22 or earlier has no start time: stop refuses it once, and the server is stopped by hand.
Documented
- The docs build fails on a link to a page that does not exist (
ignoreDeadLinks: false), and CI builds the docs on every push and pull request, not only when publishing them. - The Hot Reload page described
quantum dev, configuration keys, environment variables and partial updates that never existed; it now describesquantum start --hot-reloadas it works. - SPEC gains LOOP-5 (loop types), SET-3 (q:set operation and scope) and SET-4 (q:set validation), and extends DB-1, ACT-3 (q:flash), DATA-1, INV-1, FILE-1, MAIL-1 and IA-1/2/4 — behaviour that already existed; every rule is exercised by a test that cites it.
- Every Core and AI tag has a checked example in the guide; the guide explains using one component inside another (q:import, props, q:slot).