Skip to content

v1.0.0

Choose a tag to compare

@github-actions github-actions released this 25 Sep 00:26
· 183 commits to main since this release

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.md splits 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 of ui:*, 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.md states 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/, with test_spec_ids.py checking 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/blog and projects/helpdesk
    (Core, files and mail), projects/bank-transfer (q:transaction),
    projects/docs-assistant and projects/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.md is 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 .q page with ui:* 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 plan from a schema.sql, change
    history, the /_dev panel, 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 gives default (null when 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 .q that 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) and pick(list) in expressions
    (EXPR-15).

  • The HTML a .q accepts 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", calls game.setPatrolAI, and
    uses the Godot codegen's tags item_block and bonus_coin, its HUD counter
    bonus_coins, its sound trigger player-died and its event
    item-block-hit; the world map moves the sprite id="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}/logs answers failures with an HTTP status and
    a detail instead of 200 and an error key: 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, as d.missing already was (EXPR-16, was G19);
    it used to be null, so a misspelt key rendered nothing. Read an optional
    key with get(d, 'key', default) or test it with 'key' in d.
  • skip_rows on q:data skips 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): model on q:knowledge (the model is chosen on q:llm, IA-2),
    unique on q:set (use operation="unique"), every attribute of q:column
    but name and type (required, default, validate, pattern, min,
    max, minlength, maxlength, range, enum; filter rows with
    q:transform), and q:flash outside a q:action (ACT-3).
  • in compares like == (EXPR-14): '5' in [5] and 5 in ['5'] are true
    — a value from a form now finds its number in a list. in on something that
    is not a list, an object or a text is an error.
  • q:set without type keeps the type of a value that is exactly one
    expression, like q:return and 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, so len() 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, write type="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:set type, operation, scope and validate (date,
    datetime, struct and null were accepted as q:set types and did
    nothing); q:loop type;
    q:param type (inside q:query only the types the query understands —
    number there is now reported where it is written, it failed when the query
    ran); q:invoke method and authType. An unknown authType used 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 a ui:table source: 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 deploy and quantum apps: they talked to a deploy service that was
    never published, so for anyone else they could only fail. Serve an app with
    quantum start behind 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 .q admin 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; a global.yaml with a deployment: 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 by projects/docs-assistant;
    its script inserted the server's reply into the page with innerHTML
    (XSS).
  • projects/quantum-rag: a RAG demo over a stale copy of the guide,
    superseded by projects/docs-assistant.
  • projects/quantum-fighter: only a prebuilt game.html, with no .q
    source to build it from.
  • projects/quantum-tower and its copy examples/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 standard extra needs >= 13). CI
    had been testing a different admin than the one developers run. Starlette
    1.x's TestClient uses httpx2 (pinned alongside; the admin itself still
    calls httpx), 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
    listed datetime for every param (only a q:query's takes it) and lacked
    time, q:invoke method lacked HEAD/OPTIONS, and validate did not
    say a regular expression starting with ^ is accepted (it was an enum, so
    the language server flagged one). test_editor_schemas.py compares 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.x could not be read inside an expression at all. It
    is read as in q:set, everywhere (EXPR-11).
  • q:set type= int, long, numeric, float, double and text — the
    names q:param accepts — convert like integer, number, decimal and
    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 in static/ in the checkout; each writer now uses a
    temporary folder, and the root conftest.py fails 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:schedule and q:thread are 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 with q:job
    started one more queue worker, and every request with q:schedule one 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.q never ran (it queried a default
    datasource with tables nobody created, and read flash before 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 for q:transaction (DB-4): a SQLite
    config, a migration with three accounts and a CHECK (balance >= 0), both
    accounts checked before the transaction, and a quantum test suite in CI.
  • tests/apps/test_ui_parity_script.py failed 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 console counts 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 offering
    q:persist, q:route, q:component basePath/health/metrics/trace and
    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, and tests/conformance/test_editor_schemas.py fails when
    either drifts from the parser.
  • model= on q:llm, q:agent and q:team agents takes expressions, like
    endpoint= and apiKey=; model="{m}" was sent to the server literally
    (IA-1).
  • range= on a q:action param is checked, as on a q:function param
    (ACT-2, FN-1).
  • encoding= on q:data reads 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's default (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
    sessionExpiry may be a date or ISO text (SET-5).
  • The redis message broker (MESSAGE_BROKER_TYPE=redis, quantum mq) could
    not load: its module imported message_broker by a bare name, so asking
    for it said "requires 'redis' package" even with redis installed.
  • A q:action read and wrote an empty application scope of its own:
    application.x set 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-chat works again (every post was a 500): its forms are
    q:actions, its dead runtime patches and a committed session key are gone,
    and a quantum test suite runs in CI.
  • projects/quantum-terminal's download links answered 404 under
    quantum start.
  • The error for <q:application type="testing"> and qtest: tags names
    quantum test as available; it said "coming in a later release" (APP-2).
  • quantum start with 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 stop would kill
    it.
  • A persisted q:knowledge base is stored in the working directory it is
    indexed from (IA-2). The default ./.quantum/knowledge was 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, a persist="false" base no longer made the bases
    after it in-memory too.
  • quantum stop no longer kills a process it did not start (RUN-3): a stale
    .quantum.pid whose 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.pid written 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
    describes quantum start --hot-reload as 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).