Repository navigation
v1.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 writesdeath-sequence="classic", callsgame.setPatrolAI, and
uses the Godot codegen's tagsitem_blockandbonus_coin, its HUD counter
bonus_coins, its sound triggerplayer-diedand its event
item-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:column
butnameandtype(required,default,validate,pattern,min,
max,minlength,maxlength,range,enum; filter rows with
q: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 with
quantum 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.q
source 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'sstandardextra 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 type
listeddatetimefor every param (only aq:query's takes it) and lacked
time,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,decimaland
string; 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/knowledge
and 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:job
started 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 adefault
datasource 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 now
projects/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 noknowledge/top/minRelevance; the
agent, tool and data sub-tags were missing), and both tools stop offering
q: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, like
endpoint=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 are
q: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 under
quantum start.- The error for
<q:application type="testing">andqtest:tags names
quantum 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 wherequantum 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).