{"owner":"QuipNetwork","repo":"xq-rs","hasSkills":true,"totalSkillsCount":1,"totalTokensCount":8340,"categories":["root-instruction"],"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md"],"skills":{"AGENTS.md":"# AGENTS.md\n\nThis file is the authoritative, tool-neutral source of truth for AI coding assistants\nand human contributors working in this repository. It holds repo-wide conventions for\nany collaborator.\n\nTool-specific agent config files (`CLAUDE.md`, etc.) are not checked into git -- each\ndeveloper manages their own locally. Claude Code does not read `AGENTS.md` natively, so\nClaude Code users should add a gitignored `CLAUDE.md` (or `CLAUDE.local.md`) whose first\nline is `@AGENTS.md` to auto-load this file. Personal, repository-specific overrides go\nin `AGENTS.local.md` (gitignored), imported at the end of this file and silently skipped\nfor anyone who does not have one.\n\nThe **XQuad Toolchain** is a hardware-agnostic quantum VM and SDK: a problem is\nexpressed once in XQVM bytecode and executed on any supported quantum backend\n(annealers, gate-based chips, etc.). Think LLVM for quantum computing. The codebase\nis dual-language: a Rust core (VM, assembler, bytecode, CLI) with Python interfaces\n(reference VM, constraint programming DSL, solver adapters, FFI bindings).\n\nYou are a senior engineer with deep expertise in Rust 2024 edition and Python 3.13+,\nspecializing in compiler engineering, systems programming, and high-performance\nquantum computing SDKs. You emphasize memory safety, zero-cost abstractions, and\ncross-language correctness.\n\n## Quick-Reference Commands\n\n```sh\n# Full suite (what CI runs)\nmake all              # fmt + lint + test (Rust + Python)\nmake xquad            # bootstrap local dev: Python venv + install xquad CLI\nmake install-hooks    # point git at .githooks/ pre-commit hook\n\n# Preflight (run locally exactly what CI enforces; N/A a language you didn't touch)\nmake preflight         # preflight-rs + preflight-py + preflight-parity + preflight-docs + preflight-policy\nmake preflight-rs      # fmt, taplo, clippy, rustdoc, deny (root + pallet fixture), unit/integration/doc tests\nmake preflight-py      # taplo, ruff format + lint, pytest, uv.lock freshness\nmake preflight-parity  # opcode parity, conformance, example smoke\nmake preflight-docs    # generated-doc freshness + docs drift + README length + prose (needs vale)\nmake preflight-release # crate packaging dry-run + five Python dists (needs maturin/twine/uv; not in `preflight`)\n\n# Rust\nmake fmt              # cargo fmt + taplo fmt + ruff format\nmake lint             # lint-clippy + lint-doc + lint-deny-rs + lint-py + fmt-check\nmake lint-clippy      # cargo clippy --workspace --all-targets --all-features -- -D warnings\nmake lint-doc         # RUSTDOCFLAGS=\"-D warnings\" cargo doc --workspace --all-features --no-deps\nmake lint-deny-rs     # cargo deny check\nmake lint-deny-fixture # cargo deny over fixtures/pallet-xqvm, with its own deny.toml\nmake test             # test-unit-rs + test-integ-rs + test-doc + test-py\nmake test-unit-rs     # cargo nextest run --workspace --all-features --lib\nmake test-integ-rs    # cargo nextest run --workspace --exclude xquad-conformance --all-features --test '*'\nmake test-doc         # cargo test --doc --workspace --all-features\nmake test-miri        # cargo +nightly miri test --workspace --all-features\nmake deps             # install rustup components + pinned cargo tools\nmake deps-miri        # install nightly + miri\n\n# Single Rust test by name\ncargo nextest run --workspace -E 'test(my_test_name)'\ncargo test --workspace my_test_name\n\n# Python\nmake deps-py          # uv sync: editable installs, xqffi built from current Rust\nmake fmt-py           # ruff format across all Python packages\nmake fmt-check-py     # ruff format --check\nmake lint-py          # ruff check across all Python packages\nmake test-py          # pytest xqvm_py/tests xqcp/tests xqsa/tests xquad/tests\nmake check-uv-lock     # uv lock --check -- fails if uv.lock is stale against pyproject.toml\nmake check-xqffi-fresh # uv sync --extra dwave + import xquad -- asserts the xqffi cdylib is\n                       # fresh; mutates .venv/, re-run `make deps-py` afterwards\nmake repl             # Python REPL with xqffi + workspace packages\n\n# Cross-language\nmake opcode-parity    # opcode-parity-rs + opcode-parity-py\nmake conformance      # conformance-rs + conformance-py\nmake example-smoke    # run examples on both interpreters, check valid == 1\n\n# Documentation\nmake build-docs            # mdbook build\nmake regen-docs            # regenerate generated opcode and example book pages\nmake check-docs-generated  # assert generated docs match regenerated output\nmake check-docs-drift      # guard book prose, SUMMARY.md coverage, and page links\nmake check-docs-mermaid    # assert book diagrams rendered (needs make build-docs first)\nmake check-docs-readme     # guard published package READMEs against the 100-line limit\nmake check-docs-prose      # Vale over the handwritten book pages. Needs vale on PATH:\n                           # `brew install vale`; pinned by VALE_VERSION in the Makefile\nmake serve-docs            # mdbook serve --open\n\n# Changelog (CHANGELOG.md is gitignored; cliff.toml + git history is source of truth)\nmake changelog                              # generate CHANGELOG.md (preview unreleased)\nmake changelog-release VERSION=v0.2.0       # preview a tag's release notes (renders exactly one section)\nmake render-changelog                       # render-only validation (lint smoke)\nmake check-release-notes                    # regression guard: every release renders exactly one section\n\n# Versions and branches (docs/guide/gitflow-protocol.md is normative)\nmake list-version-sites                     # every place a release version is written\nmake set-version VERSION=0.4.1-dev          # write them all; then cargo check && uv lock\nmake check-branch-containment               # last release tag in dev, origin/main in release/* (no-op elsewhere)\n```\n\n## Shared Conventions\n\n### License Header\n\nEvery new source file must begin with the AGPL license header. Use `//` comments for Rust, `#` comments for Python. In Zed, the `agpl` snippet (`.zed/snippets.json`) inserts the Rust header automatically.\n\n```\nCopyright (C) 2026 Postquant Labs Incorporated\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License\nalong with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nSPDX-License-Identifier: AGPL-3.0-or-later\n```\n\n### DCO Sign-Off\n\nCommits must be signed off: `git commit -s` (DCO requirement from `CONTRIBUTING.md`).\n\n### Branching\n\nTwo long-lived branches. [`docs/guide/gitflow-protocol.md`](docs/guide/gitflow-protocol.md) is normative for routing, releases, betas and the back-merge; do not restate it. Before 1.0:\n\n- **`main`** is the non-breaking line, at the next patch's `-dev` version. Everything lands by merge request, except the version bump that reopens it after a release: the code owners push that directly, and `verify:policy` (`scripts/check-main-direct-push.sh`) fails any other direct push.\n- **`dev`** is the breaking line, at the next minor's `-dev` version. Maintainers push to it for back-merges and version bumps only. Anything authored goes through a merge request, because a direct push skips the title check and the atomic spec-MR rule.\n\nRoute every change by one question: **does it break?** Before 1.0 the minor is the breaking bump, so \"is this a fix?\" is the wrong question -- a non-breaking feature goes to `main` and a breaking fix goes to `dev`. A breaking change branches from `dev`, targets `dev`, and carries `!` in its merge request title and commit subjects. Everything else branches from and targets `main`. There is no `hotfix/` branch before 1.0. Branch names stay `feature/qui-<id>` on either line; the target is set on the merge request, not in the name.\n\nWhen opening a merge request, answer the **Compatibility** block in `.gitlab/merge_request_templates/default.md` -- GitLab auto-populates that template, and its answer decides the target branch, so it has to match the branch chosen on the form. Leaving it blank means an unrouted change. A non-breaking claim needs two or three lines naming what the change touched and why a consumer pinned to the current minor can take it without editing their code. Releases use `release.md` instead (`glab mr create --template release`).\n\nNothing is squashed on merge. The one exception is post-1.0 `hotfix/*` into `main`.\n\nFeature branches stay linear: update one by rebasing onto its target, never by merging the target in, and merge stacked merge requests bottom-up rather than into each other. A merge commit inside a branch reaches the line with it; `verify:policy` fails the merge request. See \"Merge commits\" in the git protocol.\n\n### Conventional Commits\n\nAll commit messages must follow the [Conventional Commits](https://www.conventionalcommits.org/) format:\n\n```\n<type>(<scope>): <subject>\n\n[optional body]\n\n[optional footer(s)]\n```\n\n**Types:** `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`, `security`, `deprecate`, `release`\n\n**Scope** is optional. When used, it should be the crate or package name (e.g. `xqvm`, `xqcp`, `conformance`).\n\n**Rules:**\n- Subject line: imperative mood, lowercase start, no trailing period, max 72 characters\n- Body: wrap at 72 characters, explain what and why\n- Footer: reference the tracking issue (e.g. `Fixes #123`) to auto-link and close it on merge\n- Breaking changes: append `!` after type/scope (e.g. `feat(xqvm)!: remove deprecated API`) or add a `BREAKING CHANGE:` footer\n- NEVER add `Co-Authored-By` trailers for AI assistants\n\n**Example:**\n\n```\nfix(xqasm): handle forward label references in nested loops\n\nThe two-pass label resolver was not accounting for label offsets\ninside nested RANGE blocks, causing incorrect jump targets when\na forward reference crossed a loop boundary.\n\nFixes #456\n```\n\n### Post-Edit Linting\n\nAfter modifying files, run `make fmt` to format everything, or the per-file equivalents from the commands section: `cargo fmt` for Rust, `uvx ruff@<pinned version> check --fix <file>` + `uvx ruff@<pinned version> format <file>` for Python (pin lives in the Makefile's `RUFF_VERSION`), `taplo fmt` for TOML.\n\n### Constraints\n\n- NEVER use emojis in code, documentation, or commit messages.\n- NEVER use em-dash in documentation -- prefer using `--`.\n- Use 4 spaces for indentation (no tabs).\n- Aggregate edits to a single file into one pass. Do not thrash multiple small edits to the same file in sequence.\n\n### Naming\n\n- `snake_case` for functions, modules, and variables.\n- `PascalCase` for types, traits, and classes.\n- Follow `CONTRIBUTING.md` for additional Rust conventions and ruff/pycodestyle for Python.\n\n## Rust\n\n### Principles\n\n- **Safety First:** zero `unsafe` code unless absolutely necessary. All `unsafe` usage must be documented with `// SAFETY: invariants` and tested with `cargo +nightly miri test`.\n- **Idiomatic Rust:** follow `CONTRIBUTING.md` for contribution standards and idiomatic code.\n- **Functional-style code:** prefer functional interfaces over imperative code. Use imperative code if functional-style code is less clear.\n- **Performance:** zero-cost abstractions. Be efficient in terms of memory use and performance. Prefer stack allocation over heap.\n- **Ownership:** design ownership/borrowing structures *before* writing logic.\n- **DRY:** extract repeated error construction, span computation, and validation logic into private helper functions. Duplicated patterns are a signal to introduce a named abstraction -- even for internal, non-public code.\n\n### Development Workflow\n\n- **Architecture:** analyze crates, lifetimes, and public APIs first (methods, traits, etc.). Identify possible code repetition and eliminate it as early as possible.\n- **Implementation:**\n  - Prefer to use already existent libraries instead of reinventing the wheel.\n  - Do not use `unwrap()`. Use safer alternatives.\n  - Avoid subscripting or explicit slicing (e.g. `foo[3]`, `bar[0..2]`) and use `get`, `get_mut` instead.\n  - Use `panic`s and `assert`s only for testing and invariant violations.\n  - Avoid vague error messages, attach wider context to error messages to improve debugging availability.\n  - Use `miette` (`eyreish` sub-module) for application errors.\n  - Use `clap` for CLIs.\n  - Use `rayon` for CPU-bound tasks that may benefit from parallelism.\n  - The workspace enforces `unsafe-code = \"deny\"` and `rust-2018-idioms = \"deny\"` as hard errors. Key warnings that become blocking on CI: `indexing-slicing` (use `.get()`/`.get_mut()` with proper error handling instead of `[]`), `unused-results` (must handle or discard with `let _ =`).\n- **Code organisation:**\n  - Organise code in crates that takes up to one responsibility.\n  - Every crate should consist of `lib.rs` -- facade module that exposes public API by re-exporting other module items. Keep inner modules as private as possible.\n  - Design code using `newtype`s rather than type aliases.\n- **Writing tests:**\n  - Write unit tests for every change, take care of edge cases, use fuzzy testing if possible.\n  - Write integration tests in `tests/` directory.\n  - Write micro-benchmarks in `benches/` directory.\n- **Documentation:** document every publicly exposed element of API with this format:\n\n  ```rust\n  /// Short description, up to two sentences: Does this and that.\n  ///\n  /// Paragraph with a longer description of the code logic and behavior on certain inputs.\n  ///\n  /// # Examples\n  ///\n  /// ```rust\n  /// let foo: Foo = Foo::foo();\n  /// assert!(foo.works_ok());\n  /// ```\n  ///\n  /// # Panics\n  /// Description when function panics for unexpected reason.\n  ///\n  /// # Errors\n  /// Description when the function returns a business logic error.\n  ///\n  /// # Safety\n  /// Safety invariants and how the function is safe to use, if marked `unsafe`.\n  ```\n\n  When documenting a publicly exposed module, write a simple description of what the module is doing and how to use code written there. Add examples of how to use the API inside the module.\n- **Review:** perform a self-review of API surface area for ergonomics, safety, and code repetitions.\n- **Validation:** run `make lint` for checking lints. Then run `make test` to test the code.\n- **License compliance:** check compliancy of libraries added to the project.\n  - Check whether `cargo deny` passes.\n  - If not, check if the license of the library is compatible with `AGPL-3.0-or-later`.\n  - If compatible, add the license to `deny.toml`.\n  - If not compatible, look for compatible alternatives in `crates.io`.\n  - If there are no alternatives, write yourself a code that will fulfill the same needs.\n  - Update `NOTICE` file accordingly as the library added to the project.\n\n### Staff-Level Responsibilities\n\n- Focus on reducing complexity in `Cargo.toml`.\n- Optimize for build times (parallel processing, reducing dependencies).\n- Ensure high test coverage for edge cases (fuzz testing if necessary).\n- If the dependency is used widely enough, add it to the Cargo workspace (like `thiserror`, `rayon` or `itertools`).\n\n### Architecture\n\n#### Crate Map\n\n| Crate | Path | Role |\n| --- | --- | --- |\n| `xqvm` | `xqvm/` | Bytecode definitions, opcode table, instruction types, builder, codec, stream reader, VM interpreter, disassembler |\n| `xqasm` | `xqasm/` | Text assembler: pest parser -> AST -> bytecode |\n| `xqcli` | `xqcli/` | CLI binary (`xquad`): asm, dism, run, verify subcommands |\n| `xqffi` | `xqffi/` | PyO3 bindings exposing xqasm + xqvm to Python |\n| `xquad-conformance` | `conformance/` | Cross-implementation conformance harness |\n\n#### Key Patterns\n\n**X-Macro opcode table** (`xqvm/src/bytecode/types/table.rs`) -- The `opcodes!` macro is the single source of truth for all 93 instructions. The `Opcode` enum, `Instruction` enum, mnemonic strings, and operand arity are all derived from it. When adding or changing an opcode, edit only this table.\n\n**Two-pass label resolution** (`xqvm/src/bytecode/builder.rs`) -- `InstructionBuilder` records unresolved jump fixups on the first pass and patches offsets at `build()` time, supporting both forward and backward label references.\n\n**Binary codec** (`xqvm/src/bytecode/codec.rs`) -- Uses `oxicode` with BE fixint encoding: opcode byte followed by operand fields at their natural width in big-endian byte order (`i16` = 2 bytes, `[u8; N]` = N bytes, `u8`/`Register` = 1 byte). No varints, no length prefixes. `InstructionStream` (`stream.rs`) is an incremental seekable reader over encoded bytes. Mnemonic strings inside the bytecode crate use `pastey` for no-std compact string storage (avoids heap allocation for fixed-length identifiers).\n\n**Assembly pipeline** (`xqasm/`) -- `pest` grammar -> `ast::Program` -> `assembler::assemble()` -> `InstructionBuilder` -> `codec::encode`. Rich `miette` diagnostics with source spans are emitted at the assembler stage.\n\n**VM interpreter** (`xqvm/`) -- `Vm` executes a `Program` (raw instruction bytes) through `bytecode::Cursor`, a crate-internal, label-free reader that decodes exactly as `InstructionStream` does. State: 256-slot register file (`RegVal` enum: `Int(i64)`, `VecInt(Vec<i64>)`, `VecXqmx(Vec<XqmxModel>)`, `Model(XqmxModel)`, `Sample(XqmxSample)`), an unbounded integer stack, and a loop stack of `LoopFrame` records (one per `RANGE`/`ITER`). `StepResult` drives control flow: `Continue`, `Jump(offset)`, `Halt`, `StartLoop`. Default step limit is 10,000,000 (configurable via `set_step_limit()`). A second budget bounds memory: every allocating opcode is charged bytes before it allocates, against a 1 GiB default (configurable via `set_memory_limit()`), and raises `MemoryLimitExceeded` when it cannot pay. Calldata and output slots are injected before `run()` via `set_calldata()` / `set_output_slots()`. VM errors carry `into_diagnostic(&program, source_name)` which disassembles the failing offset for miette source annotation. `clippy::result_large_err` is explicitly allowed in the asm crate because `NamedSource<Arc<str>>` on the error path is intentional.\n\n#### Instruction Set Categories (93 total)\n\nControl flow, stack/register I/O, arithmetic (including `SQR`, `ABS`, `INC`, `DEC`, `MIN`, `MAX`), comparison, logical/bitwise, QUBO/Ising/integer matrix allocators (`BQMX`, `SQMX`, `XQMX`), sample allocators, vector ops, index math, matrix coefficient access, grid ops, high-level constraints (`ONEHOTR`, `ONEHOTC`, `EXCLUDE`, `IMPLIES`, `EQUALITY`, `ATLEAST`, `ATLEASTW`, `REDUCE`), and `ENERGY`.\n\n## Python\n\n### Dependencies & Environment\n\n- **Dependencies:** manage via each package's `pyproject.toml`. The repo-root `pyproject.toml` hosts the `uv` workspace declaration and dev-tool pins (maturin, pytest, pyyaml, ruff); the xqvm_py / xqcp / xqsa / xqffi members carry their own. Never modify the dev-dep pins without explicit user approval.\n- **Virtual environment:** always use the workspace `.venv/` managed by `uv sync`. Never install packages globally or create ad-hoc venvs. Invoke scripts and tests through the `make` targets or `uv run`, which picks up the workspace packages without a manual activation step. `xqffi/pyproject.toml` lists the cdylib's Rust inputs as uv `cache-keys`, so any `uv sync` or `uv run` rebuilds `xqffi` after a Rust edit in `xqffi`, `xqvm` or `xqasm`; uv compares modification times, so a copy that keeps an old mtime over new content is not seen. The make targets pass `--no-sync` to skip a second resolve after their own sync. For an extra, `uv sync --extra <name>`, then `uv run ...`; `uv run` keeps an installed extra, but a plain `uv sync`, including the one in `make deps-py` and `make test-py`, removes it again.\n- **Setup:** `make deps-py` runs `uv sync`, which builds `xqffi` through maturin with `cargo --locked` (the root `pyproject.toml`'s `config-settings-package`). Each package's editable install carries `dev-mode-dirs = [\"..\"]`, which puts the repo root on `sys.path`. Re-run after pulls that touch Rust sources or workspace deps.\n\n### Package Map\n\n| Package | Path | Role |\n| --- | --- | --- |\n| `xqvm_py` | `xqvm_py/` | Python reference VM implementation (conformance oracle) |\n| `xqcp` | `xqcp/` | High-level constraint programming DSL compiling to XQVM assembly |\n| `xqsa` | `xqsa/` | Solver adapters for XQMX models (dwave-samplers; pluggable solver interface) |\n| `xqffi` | `xqffi/` | PyO3 FFI bindings (maturin-built); also a Rust crate |\n| `xquad` | `xquad/` | Umbrella meta-package re-exporting xqffi, xqcp, xqsa under unified namespace |\n\n### Testing\n\n`make test-py` runs pytest across `xqvm_py/tests`, `xqcp/tests`, `xqsa/tests`, `xquad/tests`. Test paths are configured in the root `pyproject.toml` under `[tool.pytest.ini_options]`.\n\n## Cross-Language\n\n### Specifications\n\nThe `spec/` directory contains authoritative specifications for each toolchain component. Read the relevant spec before modifying that component:\n\n- `spec/xqvm/` -- XQVM architecture (opcodes, control flow, type system, encoding)\n- `spec/xqcp/README.md` -- XQCP constraint programming DSL\n- `spec/xqsa/README.md` -- XQSA solver adapter interface\n\nSpec changes are governed by the conformance harness: any modification affecting the opcode table, control-flow rules, stack depth, type system, or HLF expansions must be mirrored in `conformance/opcodes.yaml` and validated against `xqvm_py/opcodes.py` via `scripts/check-opcode-parity.py`.\n\n### Conformance Vectors\n\nBehavioural parity between `xqvm_py` (Python reference) and the Rust `xqvm` crate is enforced by the `xquad-conformance` test suite. Vectors live in `conformance/vectors/`. New semantics require a new vector; divergence between impls fails CI with no drift-tracking middle ground.\n\n### Atomic Spec-MR Rule\n\nAny MR that changes VM semantics must touch **all four** layers in the same MR: (1) `spec/xqvm/*.md`, (2) `xqvm/src/**/*.rs`, (3) `xqvm_py/{executor,opcodes,xqmx,state,vector,tracer,errors}.py`, (4) `conformance/vectors/**` or `conformance/opcodes.yaml`. CI enforces this via `verify:policy` (`scripts/check-atomic-spec-mr.sh`). MRs touching 0 or all 4 layers pass; partial changes (1-3 layers) fail.\n\nFor deliberately one-sided changes (e.g. aligning one impl to existing behaviour), add an `Atomic-Spec-Exempt: QUI-<id> <reason>` trailer to a commit message. It goes in the message's last paragraph at column 0, beside the sign-off, with the whole reason and the ticket on that one line; git reads trailers from the last paragraph only and truncates a wrapped reason, so the guard fails on either instead of bypassing. A `Fixes QUI-NNN` footer may share the paragraph but not the line directly below the trailer. The guard scans every commit in the MR range and bypasses when it finds at least one well-formed trailer. See `docs/guide/development-workflow.md` for the full rationale and exempt cases.\n\n### Opcode Addition Gate\n\nAdding a row to the `opcodes!` table in `xqvm/src/bytecode/types/table.rs` is a change to VM semantics. The MR that adds it argues in its description that the new opcode clears all six clauses of the on-chain admissibility bar: no floating point, no host I/O or ambient state, no nondeterministic iteration order, bounded allocation, bounded per-instruction work, and behaviour specified in `spec/xqvm/` with a conformance vector covering it. An opcode that cannot clear all six does not ship; the operation belongs in `xqcp`, `xqsa`, the `xquad` API or a helper library instead.\n\nThere is deliberately no CI guard for this one -- it is a correctness argument a reviewer weighs, not a string a script can find. `docs/guide/development-workflow.md` is required reading before changing VM semantics and carries the six clauses in full.\n\n### Rust-Python Bindings (xqffi)\n\n`xqvm_py` consumes `xqffi.asm` only -- its executor stays pure-Python so `xqvm_py` remains an independent conformance oracle. `uv sync` builds it (handled by `make deps-py`).\n\n### Examples & Smoke Tests\n\n`examples/tsp/` (Travelling Salesman) and `examples/maxcut/` (Max-Cut) each consist of `.xqasm` programs driven by a Python runner (`runner.py`) that exercises both the Rust and Python interpreters via the `--interpreter` flag. These are the canonical references for how host code loads and runs `.xqasm` programs via the toolchain. `make example-smoke` runs both interpreters and checks each produces a valid solution (`valid == 1`); the check is invariant-based, not golden-file diffing.\n\n### CI Pipeline\n\nFive phases, each answering one question about the change. `verify` and\n`test` read as synonyms, so the boundary is stated explicitly rather than\nleft to be inferred per job -- an unwritten boundary is exactly how the\nold `lint` stage decayed into four unrelated concerns (source formatting,\nRust compilation, cross-implementation parity, release packaging) that\nhappened to share a stage barrier and nothing else:\n\n- **`verify`** asks whether something matches what it is required to\n  match -- a licence against policy, one VM implementation's output\n  against the other's.\n- **`test`** asks whether something does what it should when actually\n  executed. Not a static-vs-dynamic split (`verify:parity` runs the VM);\n  it is consistency between artefacts versus correctness of one artefact.\n\n| Phase | Question it answers | What it covers |\n| --- | --- | --- |\n| `verify` | Does the workspace match what it's required to match? | clippy, rustdoc, cargo-deny (root workspace and pallet fixture), ruff, `uv.lock` freshness, the fresh-xqffi-cdylib check, opcode parity, Rust + Python conformance vectors, example smoke tests, atomic spec-MR guard, commit-message guard, merge-request-title guard, branch containment guard, changelog render |\n| `test` | Does the workspace do what it should when executed? | unit, integration, doc tests (Rust); pytest (Python); Quip signing-layer tests; WASM no_std tests; Substrate pallet fixture |\n| `hardware` | Does it work on real hardware? | CUDA, D-Wave QPU, and Metal solver tests on real hardware (protected refs only) |\n| `docs` | Is the documentation correct and buildable? | generated-docs freshness, docs drift guard, package README length guard, mdbook build, GitLab Pages publish (release tags only) |\n| `release` | Is the artefact publishable, and (on a tag) published? | `release:validate` packaging checks on every pipeline including tags; crates.io + PyPI publishing and GitLab Release notes via git-cliff on a pushed tag |\n\n**Gating topology.** `verify:*` and `test:*` jobs all carry `needs: []`\nand start together at t=0 -- peers, not a chain; a `verify` failure does\nnot hold `test` back. Every `hardware:*` job then `needs:` the full\nverify+test set, so nothing touches real hardware before the code is\nknown to compile and pass its own test suite -- a GPU, a metered D-Wave\nQPU token, and a macOS runner are all scarce and/or billed, and none of\nthem should be spent on code that does not even build. `docs:*` and\n`release:validate` jobs in turn `needs:` verify+test+hardware, and the\ntag-only publish chain (`release:crates` -> `release:pypi` ->\n`release:notes`) is gated transitively through `release:validate`'s\n`needs:` edge rather than through the stage barrier -- so the\ndocumentation site and the release path both wait on hardware being\nproven, not just on the workspace compiling. The accepted tradeoff: an\noffline GPU runner or an expired D-Wave token stalls the documentation\nsite, even though nothing in the docs content depends on hardware\npassing -- we would rather stall the docs than publish a book\ndescribing an opcode or solver behaviour the hardware suite just\nproved broken. On a tag the same gate now sits ahead of the publish\nchain too, so the same expired token stalls a release and not just the\ndocs -- `test:substrate`'s cold-cache build is the largest single wait\non that path. The exact graph and per-edge reasoning (including why\n`hardware:*` needs are marked `optional: true`) live in\n`.gitlab/ci/setup.yml`'s \"Dependency gating\" section.\n\n**Path gating.** Three jobs do not run on every pipeline. `test:wasm` and\n`test:substrate` are gated on `rules: changes:`, because each builds one\ncrate (`xqvm`) into one fixture and so has a narrow, writable input\nfootprint, while every other job in the pipeline is a whole-workspace\ncheck whose verdict a change anywhere can flip. They are the two most\nexpensive jobs in the pipeline and the least often relevant, so gating\nthem is most of the merge-request latency available to save.\n`verify:substrate-deny` runs cargo-deny over the pallet fixture's graph,\nwhich needs the same polkadot-sdk download, and is gated the same way.\nAll three stay unconditional on protected refs and on tags: the gate\nbuys latency, not coverage, and a path list is a claim about a build\ngraph that can be wrong -- keeping the protected refs unconditional\nmeans a wrong list costs a late signal on `main` rather than a shipped\nregression. Because any of them can be absent, every `needs:` edge into\nthem is `optional: true`; GitLab refuses to create a pipeline whose job\nneeds an absent job. The path lists, the per-clause reasoning, and the\nper-entry justification live in `.gitlab/ci/test.yml`'s \"Path gating\"\nsection and beside `verify:substrate-deny` in `.gitlab/ci/verify.yml`.\nLocal `make preflight-rs` runs all three targets unconditionally.\n\n**CI signals.** Two reds are expected, and each means a step of the release protocol is outstanding rather than that something is broken. Do not \"fix\" either by anything but the step it names:\n\n- **`dev` red from `check-branch-containment`** (in `verify:policy`): a release tag on `main` is not yet in `dev`. Every push pipeline on `dev` stays red until someone back-merges `main` into `dev`. Ordinary merges into `main` between releases do not trigger it. Merge requests into `dev` are not judged by it, so work continues in parallel.\n- **`main` red from `check-version-sites`** (in `release:validate`): `main` carries a release version. This follows every release merge until the direct push reopening `main` at the next patch's `-dev` version lands.\n\nA third is a gate, not a signal: a merge request from `release/*` fails `verify:policy` when the release branch does not contain `origin/main`. The fix is a back-merge of `main` into the release branch.\n\n**Naming.** A job's name prefix is its phase (`verify:rust` runs in the\n`verify` stage, `docs:build` in `docs`) -- that is the CI-side taxonomy.\nThe Makefile stays action-first (`<action>-<subject>`, e.g. `lint-rust`,\n`build-docs`) -- that is the local-workflow taxonomy -- so most job names\ndo not map onto their target mechanically: `verify:rust` runs `make -k\nlint-rust`, `docs:build` runs `make -k build-docs`. CI always invokes\nthese with `-k` so every prerequisite in a merged job is attempted even\nafter one fails; a plain local `make lint-rust` fails fast on the first,\nlike any other non-`-k` target.\n\nJobs are authored in per-stage files under `.gitlab/ci/` and composed via `include:` in the root `.gitlab-ci.yml`.\n\n### Changelog\n\n`CHANGELOG.md` is **not** committed to the source tree. The source of truth is `cliff.toml` plus the conventional-commit log; the file is regenerated on demand via `make changelog` and published as the GitLab Release description on tag (`release:notes` in `.gitlab/ci/release.yml`).\n\nCaveats:\n\n- Pre-conventional-commits history (everything before QUI-480) is filtered out by `filter_unconventional = true`; only commits on or after the QUI-480 enforcement appear in the rendered output. `make changelog` renders the whole unreleased history with no other scoping, so an empty render is the expected state until the first user-visible `feat`/`fix` lands.\n- `chore`, `style`, `test`, `ci`, `build` are **dropped silently** -- if a commit under one of those types ships a user-visible change (e.g. a security-relevant dep bump under `chore`), promote it to `feat`/`fix`/`security` before merging or it will be invisible in release notes.\n- `cliff.toml`'s `tag_pattern` scopes each release's notes to that release alone: it is three numeric fields and nothing else, so a prerelease tag (`-rcN`, `-betaN`) never becomes a range boundary and its commits fold into the following release's section instead of getting a page of their own. `changelog-release` (the Makefile target `release:notes` invokes) pairs this with an explicit `PREV..VERSION` range, where `PREV` is the nearest release predecessor tag (`git describe --exclude='*-*'`, the same pattern in glob form), rather than an unbounded `--tag`. `release:notes` and `docs:publish` match the same pattern, so a prerelease publishes to both registries with no Release page and no documentation. `make check-release-notes` (`scripts/check-release-notes.sh`, run as part of `lint-policy` / `verify:policy`) is the regression guard: it renders every release tag's range plus the pre-tag preview and asserts each yields exactly one `## [` heading. Mirror the pattern wherever a tag is classified; a denylist of prerelease spellings is what let `-beta` through before.\n- Before tagging a release, run `make changelog-release VERSION=vX.Y.Z` locally to preview what the GitLab Release page will say -- the render contains exactly one section. Bad commit subjects can be fixed on the source branch and re-merged before the tag is cut.\n\n## Local overrides\n\nPersonal, repository-specific configuration goes in `AGENTS.local.md` (gitignored). It is\nimported below and silently skipped for collaborators who do not have one.\n\n@AGENTS.local.md\n"},"files":{"AGENTS.md":"# AGENTS.md\n\nThis file is the authoritative, tool-neutral source of truth for AI coding assistants\nand human contributors working in this repository. It holds repo-wide conventions for\nany collaborator.\n\nTool-specific agent config files (`CLAUDE.md`, etc.) are not checked into git -- each\ndeveloper manages their own locally. Claude Code does not read `AGENTS.md` natively, so\nClaude Code users should add a gitignored `CLAUDE.md` (or `CLAUDE.local.md`) whose first\nline is `@AGENTS.md` to auto-load this file. Personal, repository-specific overrides go\nin `AGENTS.local.md` (gitignored), imported at the end of this file and silently skipped\nfor anyone who does not have one.\n\nThe **XQuad Toolchain** is a hardware-agnostic quantum VM and SDK: a problem is\nexpressed once in XQVM bytecode and executed on any supported quantum backend\n(annealers, gate-based chips, etc.). Think LLVM for quantum computing. The codebase\nis dual-language: a Rust core (VM, assembler, bytecode, CLI) with Python interfaces\n(reference VM, constraint programming DSL, solver adapters, FFI bindings).\n\nYou are a senior engineer with deep expertise in Rust 2024 edition and Python 3.13+,\nspecializing in compiler engineering, systems programming, and high-performance\nquantum computing SDKs. You emphasize memory safety, zero-cost abstractions, and\ncross-language correctness.\n\n## Quick-Reference Commands\n\n```sh\n# Full suite (what CI runs)\nmake all              # fmt + lint + test (Rust + Python)\nmake xquad            # bootstrap local dev: Python venv + install xquad CLI\nmake install-hooks    # point git at .githooks/ pre-commit hook\n\n# Preflight (run locally exactly what CI enforces; N/A a language you didn't touch)\nmake preflight         # preflight-rs + preflight-py + preflight-parity + preflight-docs + preflight-policy\nmake preflight-rs      # fmt, taplo, clippy, rustdoc, deny (root + pallet fixture), unit/integration/doc tests\nmake preflight-py      # taplo, ruff format + lint, pytest, uv.lock freshness\nmake preflight-parity  # opcode parity, conformance, example smoke\nmake preflight-docs    # generated-doc freshness + docs drift + README length + prose (needs vale)\nmake preflight-release # crate packaging dry-run + five Python dists (needs maturin/twine/uv; not in `preflight`)\n\n# Rust\nmake fmt              # cargo fmt + taplo fmt + ruff format\nmake lint             # lint-clippy + lint-doc + lint-deny-rs + lint-py + fmt-check\nmake lint-clippy      # cargo clippy --workspace --all-targets --all-features -- -D warnings\nmake lint-doc         # RUSTDOCFLAGS=\"-D warnings\" cargo doc --workspace --all-features --no-deps\nmake lint-deny-rs     # cargo deny check\nmake lint-deny-fixture # cargo deny over fixtures/pallet-xqvm, with its own deny.toml\nmake test             # test-unit-rs + test-integ-rs + test-doc + test-py\nmake test-unit-rs     # cargo nextest run --workspace --all-features --lib\nmake test-integ-rs    # cargo nextest run --workspace --exclude xquad-conformance --all-features --test '*'\nmake test-doc         # cargo test --doc --workspace --all-features\nmake test-miri        # cargo +nightly miri test --workspace --all-features\nmake deps             # install rustup components + pinned cargo tools\nmake deps-miri        # install nightly + miri\n\n# Single Rust test by name\ncargo nextest run --workspace -E 'test(my_test_name)'\ncargo test --workspace my_test_name\n\n# Python\nmake deps-py          # uv sync: editable installs, xqffi built from current Rust\nmake fmt-py           # ruff format across all Python packages\nmake fmt-check-py     # ruff format --check\nmake lint-py          # ruff check across all Python packages\nmake test-py          # pytest xqvm_py/tests xqcp/tests xqsa/tests xquad/tests\nmake check-uv-lock     # uv lock --check -- fails if uv.lock is stale against pyproject.toml\nmake check-xqffi-fresh # uv sync --extra dwave + import xquad -- asserts the xqffi cdylib is\n                       # fresh; mutates .venv/, re-run `make deps-py` afterwards\nmake repl             # Python REPL with xqffi + workspace packages\n\n# Cross-language\nmake opcode-parity    # opcode-parity-rs + opcode-parity-py\nmake conformance      # conformance-rs + conformance-py\nmake example-smoke    # run examples on both interpreters, check valid == 1\n\n# Documentation\nmake build-docs            # mdbook build\nmake regen-docs            # regenerate generated opcode and example book pages\nmake check-docs-generated  # assert generated docs match regenerated output\nmake check-docs-drift      # guard book prose, SUMMARY.md coverage, and page links\nmake check-docs-mermaid    # assert book diagrams rendered (needs make build-docs first)\nmake check-docs-readme     # guard published package READMEs against the 100-line limit\nmake check-docs-prose      # Vale over the handwritten book pages. Needs vale on PATH:\n                           # `brew install vale`; pinned by VALE_VERSION in the Makefile\nmake serve-docs            # mdbook serve --open\n\n# Changelog (CHANGELOG.md is gitignored; cliff.toml + git history is source of truth)\nmake changelog                              # generate CHANGELOG.md (preview unreleased)\nmake changelog-release VERSION=v0.2.0       # preview a tag's release notes (renders exactly one section)\nmake render-changelog                       # render-only validation (lint smoke)\nmake check-release-notes                    # regression guard: every release renders exactly one section\n\n# Versions and branches (docs/guide/gitflow-protocol.md is normative)\nmake list-version-sites                     # every place a release version is written\nmake set-version VERSION=0.4.1-dev          # write them all; then cargo check && uv lock\nmake check-branch-containment               # last release tag in dev, origin/main in release/* (no-op elsewhere)\n```\n\n## Shared Conventions\n\n### License Header\n\nEvery new source file must begin with the AGPL license header. Use `//` comments for Rust, `#` comments for Python. In Zed, the `agpl` snippet (`.zed/snippets.json`) inserts the Rust header automatically.\n\n```\nCopyright (C) 2026 Postquant Labs Incorporated\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License\nalong with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nSPDX-License-Identifier: AGPL-3.0-or-later\n```\n\n### DCO Sign-Off\n\nCommits must be signed off: `git commit -s` (DCO requirement from `CONTRIBUTING.md`).\n\n### Branching\n\nTwo long-lived branches. [`docs/guide/gitflow-protocol.md`](docs/guide/gitflow-protocol.md) is normative for routing, releases, betas and the back-merge; do not restate it. Before 1.0:\n\n- **`main`** is the non-breaking line, at the next patch's `-dev` version. Everything lands by merge request, except the version bump that reopens it after a release: the code owners push that directly, and `verify:policy` (`scripts/check-main-direct-push.sh`) fails any other direct push.\n- **`dev`** is the breaking line, at the next minor's `-dev` version. Maintainers push to it for back-merges and version bumps only. Anything authored goes through a merge request, because a direct push skips the title check and the atomic spec-MR rule.\n\nRoute every change by one question: **does it break?** Before 1.0 the minor is the breaking bump, so \"is this a fix?\" is the wrong question -- a non-breaking feature goes to `main` and a breaking fix goes to `dev`. A breaking change branches from `dev`, targets `dev`, and carries `!` in its merge request title and commit subjects. Everything else branches from and targets `main`. There is no `hotfix/` branch before 1.0. Branch names stay `feature/qui-<id>` on either line; the target is set on the merge request, not in the name.\n\nWhen opening a merge request, answer the **Compatibility** block in `.gitlab/merge_request_templates/default.md` -- GitLab auto-populates that template, and its answer decides the target branch, so it has to match the branch chosen on the form. Leaving it blank means an unrouted change. A non-breaking claim needs two or three lines naming what the change touched and why a consumer pinned to the current minor can take it without editing their code. Releases use `release.md` instead (`glab mr create --template release`).\n\nNothing is squashed on merge. The one exception is post-1.0 `hotfix/*` into `main`.\n\nFeature branches stay linear: update one by rebasing onto its target, never by merging the target in, and merge stacked merge requests bottom-up rather than into each other. A merge commit inside a branch reaches the line with it; `verify:policy` fails the merge request. See \"Merge commits\" in the git protocol.\n\n### Conventional Commits\n\nAll commit messages must follow the [Conventional Commits](https://www.conventionalcommits.org/) format:\n\n```\n<type>(<scope>): <subject>\n\n[optional body]\n\n[optional footer(s)]\n```\n\n**Types:** `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`, `security`, `deprecate`, `release`\n\n**Scope** is optional. When used, it should be the crate or package name (e.g. `xqvm`, `xqcp`, `conformance`).\n\n**Rules:**\n- Subject line: imperative mood, lowercase start, no trailing period, max 72 characters\n- Body: wrap at 72 characters, explain what and why\n- Footer: reference the tracking issue (e.g. `Fixes #123`) to auto-link and close it on merge\n- Breaking changes: append `!` after type/scope (e.g. `feat(xqvm)!: remove deprecated API`) or add a `BREAKING CHANGE:` footer\n- NEVER add `Co-Authored-By` trailers for AI assistants\n\n**Example:**\n\n```\nfix(xqasm): handle forward label references in nested loops\n\nThe two-pass label resolver was not accounting for label offsets\ninside nested RANGE blocks, causing incorrect jump targets when\na forward reference crossed a loop boundary.\n\nFixes #456\n```\n\n### Post-Edit Linting\n\nAfter modifying files, run `make fmt` to format everything, or the per-file equivalents from the commands section: `cargo fmt` for Rust, `uvx ruff@<pinned version> check --fix <file>` + `uvx ruff@<pinned version> format <file>` for Python (pin lives in the Makefile's `RUFF_VERSION`), `taplo fmt` for TOML.\n\n### Constraints\n\n- NEVER use emojis in code, documentation, or commit messages.\n- NEVER use em-dash in documentation -- prefer using `--`.\n- Use 4 spaces for indentation (no tabs).\n- Aggregate edits to a single file into one pass. Do not thrash multiple small edits to the same file in sequence.\n\n### Naming\n\n- `snake_case` for functions, modules, and variables.\n- `PascalCase` for types, traits, and classes.\n- Follow `CONTRIBUTING.md` for additional Rust conventions and ruff/pycodestyle for Python.\n\n## Rust\n\n### Principles\n\n- **Safety First:** zero `unsafe` code unless absolutely necessary. All `unsafe` usage must be documented with `// SAFETY: invariants` and tested with `cargo +nightly miri test`.\n- **Idiomatic Rust:** follow `CONTRIBUTING.md` for contribution standards and idiomatic code.\n- **Functional-style code:** prefer functional interfaces over imperative code. Use imperative code if functional-style code is less clear.\n- **Performance:** zero-cost abstractions. Be efficient in terms of memory use and performance. Prefer stack allocation over heap.\n- **Ownership:** design ownership/borrowing structures *before* writing logic.\n- **DRY:** extract repeated error construction, span computation, and validation logic into private helper functions. Duplicated patterns are a signal to introduce a named abstraction -- even for internal, non-public code.\n\n### Development Workflow\n\n- **Architecture:** analyze crates, lifetimes, and public APIs first (methods, traits, etc.). Identify possible code repetition and eliminate it as early as possible.\n- **Implementation:**\n  - Prefer to use already existent libraries instead of reinventing the wheel.\n  - Do not use `unwrap()`. Use safer alternatives.\n  - Avoid subscripting or explicit slicing (e.g. `foo[3]`, `bar[0..2]`) and use `get`, `get_mut` instead.\n  - Use `panic`s and `assert`s only for testing and invariant violations.\n  - Avoid vague error messages, attach wider context to error messages to improve debugging availability.\n  - Use `miette` (`eyreish` sub-module) for application errors.\n  - Use `clap` for CLIs.\n  - Use `rayon` for CPU-bound tasks that may benefit from parallelism.\n  - The workspace enforces `unsafe-code = \"deny\"` and `rust-2018-idioms = \"deny\"` as hard errors. Key warnings that become blocking on CI: `indexing-slicing` (use `.get()`/`.get_mut()` with proper error handling instead of `[]`), `unused-results` (must handle or discard with `let _ =`).\n- **Code organisation:**\n  - Organise code in crates that takes up to one responsibility.\n  - Every crate should consist of `lib.rs` -- facade module that exposes public API by re-exporting other module items. Keep inner modules as private as possible.\n  - Design code using `newtype`s rather than type aliases.\n- **Writing tests:**\n  - Write unit tests for every change, take care of edge cases, use fuzzy testing if possible.\n  - Write integration tests in `tests/` directory.\n  - Write micro-benchmarks in `benches/` directory.\n- **Documentation:** document every publicly exposed element of API with this format:\n\n  ```rust\n  /// Short description, up to two sentences: Does this and that.\n  ///\n  /// Paragraph with a longer description of the code logic and behavior on certain inputs.\n  ///\n  /// # Examples\n  ///\n  /// ```rust\n  /// let foo: Foo = Foo::foo();\n  /// assert!(foo.works_ok());\n  /// ```\n  ///\n  /// # Panics\n  /// Description when function panics for unexpected reason.\n  ///\n  /// # Errors\n  /// Description when the function returns a business logic error.\n  ///\n  /// # Safety\n  /// Safety invariants and how the function is safe to use, if marked `unsafe`.\n  ```\n\n  When documenting a publicly exposed module, write a simple description of what the module is doing and how to use code written there. Add examples of how to use the API inside the module.\n- **Review:** perform a self-review of API surface area for ergonomics, safety, and code repetitions.\n- **Validation:** run `make lint` for checking lints. Then run `make test` to test the code.\n- **License compliance:** check compliancy of libraries added to the project.\n  - Check whether `cargo deny` passes.\n  - If not, check if the license of the library is compatible with `AGPL-3.0-or-later`.\n  - If compatible, add the license to `deny.toml`.\n  - If not compatible, look for compatible alternatives in `crates.io`.\n  - If there are no alternatives, write yourself a code that will fulfill the same needs.\n  - Update `NOTICE` file accordingly as the library added to the project.\n\n### Staff-Level Responsibilities\n\n- Focus on reducing complexity in `Cargo.toml`.\n- Optimize for build times (parallel processing, reducing dependencies).\n- Ensure high test coverage for edge cases (fuzz testing if necessary).\n- If the dependency is used widely enough, add it to the Cargo workspace (like `thiserror`, `rayon` or `itertools`).\n\n### Architecture\n\n#### Crate Map\n\n| Crate | Path | Role |\n| --- | --- | --- |\n| `xqvm` | `xqvm/` | Bytecode definitions, opcode table, instruction types, builder, codec, stream reader, VM interpreter, disassembler |\n| `xqasm` | `xqasm/` | Text assembler: pest parser -> AST -> bytecode |\n| `xqcli` | `xqcli/` | CLI binary (`xquad`): asm, dism, run, verify subcommands |\n| `xqffi` | `xqffi/` | PyO3 bindings exposing xqasm + xqvm to Python |\n| `xquad-conformance` | `conformance/` | Cross-implementation conformance harness |\n\n#### Key Patterns\n\n**X-Macro opcode table** (`xqvm/src/bytecode/types/table.rs`) -- The `opcodes!` macro is the single source of truth for all 93 instructions. The `Opcode` enum, `Instruction` enum, mnemonic strings, and operand arity are all derived from it. When adding or changing an opcode, edit only this table.\n\n**Two-pass label resolution** (`xqvm/src/bytecode/builder.rs`) -- `InstructionBuilder` records unresolved jump fixups on the first pass and patches offsets at `build()` time, supporting both forward and backward label references.\n\n**Binary codec** (`xqvm/src/bytecode/codec.rs`) -- Uses `oxicode` with BE fixint encoding: opcode byte followed by operand fields at their natural width in big-endian byte order (`i16` = 2 bytes, `[u8; N]` = N bytes, `u8`/`Register` = 1 byte). No varints, no length prefixes. `InstructionStream` (`stream.rs`) is an incremental seekable reader over encoded bytes. Mnemonic strings inside the bytecode crate use `pastey` for no-std compact string storage (avoids heap allocation for fixed-length identifiers).\n\n**Assembly pipeline** (`xqasm/`) -- `pest` grammar -> `ast::Program` -> `assembler::assemble()` -> `InstructionBuilder` -> `codec::encode`. Rich `miette` diagnostics with source spans are emitted at the assembler stage.\n\n**VM interpreter** (`xqvm/`) -- `Vm` executes a `Program` (raw instruction bytes) through `bytecode::Cursor`, a crate-internal, label-free reader that decodes exactly as `InstructionStream` does. State: 256-slot register file (`RegVal` enum: `Int(i64)`, `VecInt(Vec<i64>)`, `VecXqmx(Vec<XqmxModel>)`, `Model(XqmxModel)`, `Sample(XqmxSample)`), an unbounded integer stack, and a loop stack of `LoopFrame` records (one per `RANGE`/`ITER`). `StepResult` drives control flow: `Continue`, `Jump(offset)`, `Halt`, `StartLoop`. Default step limit is 10,000,000 (configurable via `set_step_limit()`). A second budget bounds memory: every allocating opcode is charged bytes before it allocates, against a 1 GiB default (configurable via `set_memory_limit()`), and raises `MemoryLimitExceeded` when it cannot pay. Calldata and output slots are injected before `run()` via `set_calldata()` / `set_output_slots()`. VM errors carry `into_diagnostic(&program, source_name)` which disassembles the failing offset for miette source annotation. `clippy::result_large_err` is explicitly allowed in the asm crate because `NamedSource<Arc<str>>` on the error path is intentional.\n\n#### Instruction Set Categories (93 total)\n\nControl flow, stack/register I/O, arithmetic (including `SQR`, `ABS`, `INC`, `DEC`, `MIN`, `MAX`), comparison, logical/bitwise, QUBO/Ising/integer matrix allocators (`BQMX`, `SQMX`, `XQMX`), sample allocators, vector ops, index math, matrix coefficient access, grid ops, high-level constraints (`ONEHOTR`, `ONEHOTC`, `EXCLUDE`, `IMPLIES`, `EQUALITY`, `ATLEAST`, `ATLEASTW`, `REDUCE`), and `ENERGY`.\n\n## Python\n\n### Dependencies & Environment\n\n- **Dependencies:** manage via each package's `pyproject.toml`. The repo-root `pyproject.toml` hosts the `uv` workspace declaration and dev-tool pins (maturin, pytest, pyyaml, ruff); the xqvm_py / xqcp / xqsa / xqffi members carry their own. Never modify the dev-dep pins without explicit user approval.\n- **Virtual environment:** always use the workspace `.venv/` managed by `uv sync`. Never install packages globally or create ad-hoc venvs. Invoke scripts and tests through the `make` targets or `uv run`, which picks up the workspace packages without a manual activation step. `xqffi/pyproject.toml` lists the cdylib's Rust inputs as uv `cache-keys`, so any `uv sync` or `uv run` rebuilds `xqffi` after a Rust edit in `xqffi`, `xqvm` or `xqasm`; uv compares modification times, so a copy that keeps an old mtime over new content is not seen. The make targets pass `--no-sync` to skip a second resolve after their own sync. For an extra, `uv sync --extra <name>`, then `uv run ...`; `uv run` keeps an installed extra, but a plain `uv sync`, including the one in `make deps-py` and `make test-py`, removes it again.\n- **Setup:** `make deps-py` runs `uv sync`, which builds `xqffi` through maturin with `cargo --locked` (the root `pyproject.toml`'s `config-settings-package`). Each package's editable install carries `dev-mode-dirs = [\"..\"]`, which puts the repo root on `sys.path`. Re-run after pulls that touch Rust sources or workspace deps.\n\n### Package Map\n\n| Package | Path | Role |\n| --- | --- | --- |\n| `xqvm_py` | `xqvm_py/` | Python reference VM implementation (conformance oracle) |\n| `xqcp` | `xqcp/` | High-level constraint programming DSL compiling to XQVM assembly |\n| `xqsa` | `xqsa/` | Solver adapters for XQMX models (dwave-samplers; pluggable solver interface) |\n| `xqffi` | `xqffi/` | PyO3 FFI bindings (maturin-built); also a Rust crate |\n| `xquad` | `xquad/` | Umbrella meta-package re-exporting xqffi, xqcp, xqsa under unified namespace |\n\n### Testing\n\n`make test-py` runs pytest across `xqvm_py/tests`, `xqcp/tests`, `xqsa/tests`, `xquad/tests`. Test paths are configured in the root `pyproject.toml` under `[tool.pytest.ini_options]`.\n\n## Cross-Language\n\n### Specifications\n\nThe `spec/` directory contains authoritative specifications for each toolchain component. Read the relevant spec before modifying that component:\n\n- `spec/xqvm/` -- XQVM architecture (opcodes, control flow, type system, encoding)\n- `spec/xqcp/README.md` -- XQCP constraint programming DSL\n- `spec/xqsa/README.md` -- XQSA solver adapter interface\n\nSpec changes are governed by the conformance harness: any modification affecting the opcode table, control-flow rules, stack depth, type system, or HLF expansions must be mirrored in `conformance/opcodes.yaml` and validated against `xqvm_py/opcodes.py` via `scripts/check-opcode-parity.py`.\n\n### Conformance Vectors\n\nBehavioural parity between `xqvm_py` (Python reference) and the Rust `xqvm` crate is enforced by the `xquad-conformance` test suite. Vectors live in `conformance/vectors/`. New semantics require a new vector; divergence between impls fails CI with no drift-tracking middle ground.\n\n### Atomic Spec-MR Rule\n\nAny MR that changes VM semantics must touch **all four** layers in the same MR: (1) `spec/xqvm/*.md`, (2) `xqvm/src/**/*.rs`, (3) `xqvm_py/{executor,opcodes,xqmx,state,vector,tracer,errors}.py`, (4) `conformance/vectors/**` or `conformance/opcodes.yaml`. CI enforces this via `verify:policy` (`scripts/check-atomic-spec-mr.sh`). MRs touching 0 or all 4 layers pass; partial changes (1-3 layers) fail.\n\nFor deliberately one-sided changes (e.g. aligning one impl to existing behaviour), add an `Atomic-Spec-Exempt: QUI-<id> <reason>` trailer to a commit message. It goes in the message's last paragraph at column 0, beside the sign-off, with the whole reason and the ticket on that one line; git reads trailers from the last paragraph only and truncates a wrapped reason, so the guard fails on either instead of bypassing. A `Fixes QUI-NNN` footer may share the paragraph but not the line directly below the trailer. The guard scans every commit in the MR range and bypasses when it finds at least one well-formed trailer. See `docs/guide/development-workflow.md` for the full rationale and exempt cases.\n\n### Opcode Addition Gate\n\nAdding a row to the `opcodes!` table in `xqvm/src/bytecode/types/table.rs` is a change to VM semantics. The MR that adds it argues in its description that the new opcode clears all six clauses of the on-chain admissibility bar: no floating point, no host I/O or ambient state, no nondeterministic iteration order, bounded allocation, bounded per-instruction work, and behaviour specified in `spec/xqvm/` with a conformance vector covering it. An opcode that cannot clear all six does not ship; the operation belongs in `xqcp`, `xqsa`, the `xquad` API or a helper library instead.\n\nThere is deliberately no CI guard for this one -- it is a correctness argument a reviewer weighs, not a string a script can find. `docs/guide/development-workflow.md` is required reading before changing VM semantics and carries the six clauses in full.\n\n### Rust-Python Bindings (xqffi)\n\n`xqvm_py` consumes `xqffi.asm` only -- its executor stays pure-Python so `xqvm_py` remains an independent conformance oracle. `uv sync` builds it (handled by `make deps-py`).\n\n### Examples & Smoke Tests\n\n`examples/tsp/` (Travelling Salesman) and `examples/maxcut/` (Max-Cut) each consist of `.xqasm` programs driven by a Python runner (`runner.py`) that exercises both the Rust and Python interpreters via the `--interpreter` flag. These are the canonical references for how host code loads and runs `.xqasm` programs via the toolchain. `make example-smoke` runs both interpreters and checks each produces a valid solution (`valid == 1`); the check is invariant-based, not golden-file diffing.\n\n### CI Pipeline\n\nFive phases, each answering one question about the change. `verify` and\n`test` read as synonyms, so the boundary is stated explicitly rather than\nleft to be inferred per job -- an unwritten boundary is exactly how the\nold `lint` stage decayed into four unrelated concerns (source formatting,\nRust compilation, cross-implementation parity, release packaging) that\nhappened to share a stage barrier and nothing else:\n\n- **`verify`** asks whether something matches what it is required to\n  match -- a licence against policy, one VM implementation's output\n  against the other's.\n- **`test`** asks whether something does what it should when actually\n  executed. Not a static-vs-dynamic split (`verify:parity` runs the VM);\n  it is consistency between artefacts versus correctness of one artefact.\n\n| Phase | Question it answers | What it covers |\n| --- | --- | --- |\n| `verify` | Does the workspace match what it's required to match? | clippy, rustdoc, cargo-deny (root workspace and pallet fixture), ruff, `uv.lock` freshness, the fresh-xqffi-cdylib check, opcode parity, Rust + Python conformance vectors, example smoke tests, atomic spec-MR guard, commit-message guard, merge-request-title guard, branch containment guard, changelog render |\n| `test` | Does the workspace do what it should when executed? | unit, integration, doc tests (Rust); pytest (Python); Quip signing-layer tests; WASM no_std tests; Substrate pallet fixture |\n| `hardware` | Does it work on real hardware? | CUDA, D-Wave QPU, and Metal solver tests on real hardware (protected refs only) |\n| `docs` | Is the documentation correct and buildable? | generated-docs freshness, docs drift guard, package README length guard, mdbook build, GitLab Pages publish (release tags only) |\n| `release` | Is the artefact publishable, and (on a tag) published? | `release:validate` packaging checks on every pipeline including tags; crates.io + PyPI publishing and GitLab Release notes via git-cliff on a pushed tag |\n\n**Gating topology.** `verify:*` and `test:*` jobs all carry `needs: []`\nand start together at t=0 -- peers, not a chain; a `verify` failure does\nnot hold `test` back. Every `hardware:*` job then `needs:` the full\nverify+test set, so nothing touches real hardware before the code is\nknown to compile and pass its own test suite -- a GPU, a metered D-Wave\nQPU token, and a macOS runner are all scarce and/or billed, and none of\nthem should be spent on code that does not even build. `docs:*` and\n`release:validate` jobs in turn `needs:` verify+test+hardware, and the\ntag-only publish chain (`release:crates` -> `release:pypi` ->\n`release:notes`) is gated transitively through `release:validate`'s\n`needs:` edge rather than through the stage barrier -- so the\ndocumentation site and the release path both wait on hardware being\nproven, not just on the workspace compiling. The accepted tradeoff: an\noffline GPU runner or an expired D-Wave token stalls the documentation\nsite, even though nothing in the docs content depends on hardware\npassing -- we would rather stall the docs than publish a book\ndescribing an opcode or solver behaviour the hardware suite just\nproved broken. On a tag the same gate now sits ahead of the publish\nchain too, so the same expired token stalls a release and not just the\ndocs -- `test:substrate`'s cold-cache build is the largest single wait\non that path. The exact graph and per-edge reasoning (including why\n`hardware:*` needs are marked `optional: true`) live in\n`.gitlab/ci/setup.yml`'s \"Dependency gating\" section.\n\n**Path gating.** Three jobs do not run on every pipeline. `test:wasm` and\n`test:substrate` are gated on `rules: changes:`, because each builds one\ncrate (`xqvm`) into one fixture and so has a narrow, writable input\nfootprint, while every other job in the pipeline is a whole-workspace\ncheck whose verdict a change anywhere can flip. They are the two most\nexpensive jobs in the pipeline and the least often relevant, so gating\nthem is most of the merge-request latency available to save.\n`verify:substrate-deny` runs cargo-deny over the pallet fixture's graph,\nwhich needs the same polkadot-sdk download, and is gated the same way.\nAll three stay unconditional on protected refs and on tags: the gate\nbuys latency, not coverage, and a path list is a claim about a build\ngraph that can be wrong -- keeping the protected refs unconditional\nmeans a wrong list costs a late signal on `main` rather than a shipped\nregression. Because any of them can be absent, every `needs:` edge into\nthem is `optional: true`; GitLab refuses to create a pipeline whose job\nneeds an absent job. The path lists, the per-clause reasoning, and the\nper-entry justification live in `.gitlab/ci/test.yml`'s \"Path gating\"\nsection and beside `verify:substrate-deny` in `.gitlab/ci/verify.yml`.\nLocal `make preflight-rs` runs all three targets unconditionally.\n\n**CI signals.** Two reds are expected, and each means a step of the release protocol is outstanding rather than that something is broken. Do not \"fix\" either by anything but the step it names:\n\n- **`dev` red from `check-branch-containment`** (in `verify:policy`): a release tag on `main` is not yet in `dev`. Every push pipeline on `dev` stays red until someone back-merges `main` into `dev`. Ordinary merges into `main` between releases do not trigger it. Merge requests into `dev` are not judged by it, so work continues in parallel.\n- **`main` red from `check-version-sites`** (in `release:validate`): `main` carries a release version. This follows every release merge until the direct push reopening `main` at the next patch's `-dev` version lands.\n\nA third is a gate, not a signal: a merge request from `release/*` fails `verify:policy` when the release branch does not contain `origin/main`. The fix is a back-merge of `main` into the release branch.\n\n**Naming.** A job's name prefix is its phase (`verify:rust` runs in the\n`verify` stage, `docs:build` in `docs`) -- that is the CI-side taxonomy.\nThe Makefile stays action-first (`<action>-<subject>`, e.g. `lint-rust`,\n`build-docs`) -- that is the local-workflow taxonomy -- so most job names\ndo not map onto their target mechanically: `verify:rust` runs `make -k\nlint-rust`, `docs:build` runs `make -k build-docs`. CI always invokes\nthese with `-k` so every prerequisite in a merged job is attempted even\nafter one fails; a plain local `make lint-rust` fails fast on the first,\nlike any other non-`-k` target.\n\nJobs are authored in per-stage files under `.gitlab/ci/` and composed via `include:` in the root `.gitlab-ci.yml`.\n\n### Changelog\n\n`CHANGELOG.md` is **not** committed to the source tree. The source of truth is `cliff.toml` plus the conventional-commit log; the file is regenerated on demand via `make changelog` and published as the GitLab Release description on tag (`release:notes` in `.gitlab/ci/release.yml`).\n\nCaveats:\n\n- Pre-conventional-commits history (everything before QUI-480) is filtered out by `filter_unconventional = true`; only commits on or after the QUI-480 enforcement appear in the rendered output. `make changelog` renders the whole unreleased history with no other scoping, so an empty render is the expected state until the first user-visible `feat`/`fix` lands.\n- `chore`, `style`, `test`, `ci`, `build` are **dropped silently** -- if a commit under one of those types ships a user-visible change (e.g. a security-relevant dep bump under `chore`), promote it to `feat`/`fix`/`security` before merging or it will be invisible in release notes.\n- `cliff.toml`'s `tag_pattern` scopes each release's notes to that release alone: it is three numeric fields and nothing else, so a prerelease tag (`-rcN`, `-betaN`) never becomes a range boundary and its commits fold into the following release's section instead of getting a page of their own. `changelog-release` (the Makefile target `release:notes` invokes) pairs this with an explicit `PREV..VERSION` range, where `PREV` is the nearest release predecessor tag (`git describe --exclude='*-*'`, the same pattern in glob form), rather than an unbounded `--tag`. `release:notes` and `docs:publish` match the same pattern, so a prerelease publishes to both registries with no Release page and no documentation. `make check-release-notes` (`scripts/check-release-notes.sh`, run as part of `lint-policy` / `verify:policy`) is the regression guard: it renders every release tag's range plus the pre-tag preview and asserts each yields exactly one `## [` heading. Mirror the pattern wherever a tag is classified; a denylist of prerelease spellings is what let `-beta` through before.\n- Before tagging a release, run `make changelog-release VERSION=vX.Y.Z` locally to preview what the GitLab Release page will say -- the render contains exactly one section. Bad commit subjects can be fixed on the source branch and re-merged before the tag is cut.\n\n## Local overrides\n\nPersonal, repository-specific configuration goes in `AGENTS.local.md` (gitignored). It is\nimported below and silently skipped for collaborators who do not have one.\n\n@AGENTS.local.md\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/QuipNetwork/xq-rs/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# AGENTS.md\n\nThis file is the authoritative, tool-neutral source of truth for AI coding assistants\nand human contributors working in this repository. It holds repo-wide conventions for\nany collaborator.\n\nTool-specific agent config files (`CLAUDE.md`, etc.) are not checked into git -- each\ndeveloper manages their own locally. Claude Code does not read `AGENTS.md` natively, so\nClaude Code users should add a gitignored `CLAUDE.md` (or `CLAUDE.local.md`) whose first\nline is `@AGENTS.md` to auto-load this file. Personal, repository-specific overrides go\nin `AGENTS.local.md` (gitignored), imported at the end of this file and silently skipped\nfor anyone who does not have one.\n\nThe **XQuad Toolchain** is a hardware-agnostic quantum VM and SDK: a problem is\nexpressed once in XQVM bytecode and executed on any supported quantum backend\n(annealers, gate-based chips, etc.). Think LLVM for quantum computing. The codebase\nis dual-language: a Rust core (VM, assembler, bytecode, CLI) with Python interfaces\n(reference VM, constraint programming DSL, solver adapters, FFI bindings).\n\nYou are a senior engineer with deep expertise in Rust 2024 edition and Python 3.13+,\nspecializing in compiler engineering, systems programming, and high-performance\nquantum computing SDKs. You emphasize memory safety, zero-cost abstractions, and\ncross-language correctness.\n\n## Quick-Reference Commands\n\n```sh\n# Full suite (what CI runs)\nmake all              # fmt + lint + test (Rust + Python)\nmake xquad            # bootstrap local dev: Python venv + install xquad CLI\nmake install-hooks    # point git at .githooks/ pre-commit hook\n\n# Preflight (run locally exactly what CI enforces; N/A a language you didn't touch)\nmake preflight         # preflight-rs + preflight-py + preflight-parity + preflight-docs + preflight-policy\nmake preflight-rs      # fmt, taplo, clippy, rustdoc, deny (root + pallet fixture), unit/integration/doc tests\nmake preflight-py      # taplo, ruff format + lint, pytest, uv.lock freshness\nmake preflight-parity  # opcode parity, conformance, example smoke\nmake preflight-docs    # generated-doc freshness + docs drift + README length + prose (needs vale)\nmake preflight-release # crate packaging dry-run + five Python dists (needs maturin/twine/uv; not in `preflight`)\n\n# Rust\nmake fmt              # cargo fmt + taplo fmt + ruff format\nmake lint             # lint-clippy + lint-doc + lint-deny-rs + lint-py + fmt-check\nmake lint-clippy      # cargo clippy --workspace --all-targets --all-features -- -D warnings\nmake lint-doc         # RUSTDOCFLAGS=\"-D warnings\" cargo doc --workspace --all-features --no-deps\nmake lint-deny-rs     # cargo deny check\nmake lint-deny-fixture # cargo deny over fixtures/pallet-xqvm, with its own deny.toml\nmake test             # test-unit-rs + test-integ-rs + test-doc + test-py\nmake test-unit-rs     # cargo nextest run --workspace --all-features --lib\nmake test-integ-rs    # cargo nextest run --workspace --exclude xquad-conformance --all-features --test '*'\nmake test-doc         # cargo test --doc --workspace --all-features\nmake test-miri        # cargo +nightly miri test --workspace --all-features\nmake deps             # install rustup components + pinned cargo tools\nmake deps-miri        # install nightly + miri\n\n# Single Rust test by name\ncargo nextest run --workspace -E 'test(my_test_name)'\ncargo test --workspace my_test_name\n\n# Python\nmake deps-py          # uv sync: editable installs, xqffi built from current Rust\nmake fmt-py           # ruff format across all Python packages\nmake fmt-check-py     # ruff format --check\nmake lint-py          # ruff check across all Python packages\nmake test-py          # pytest xqvm_py/tests xqcp/tests xqsa/tests xquad/tests\nmake check-uv-lock     # uv lock --check -- fails if uv.lock is stale against pyproject.toml\nmake check-xqffi-fresh # uv sync --extra dwave + import xquad -- asserts the xqffi cdylib is\n                       # fresh; mutates .venv/, re-run `make deps-py` afterwards\nmake repl             # Python REPL with xqffi + workspace packages\n\n# Cross-language\nmake opcode-parity    # opcode-parity-rs + opcode-parity-py\nmake conformance      # conformance-rs + conformance-py\nmake example-smoke    # run examples on both interpreters, check valid == 1\n\n# Documentation\nmake build-docs            # mdbook build\nmake regen-docs            # regenerate generated opcode and example book pages\nmake check-docs-generated  # assert generated docs match regenerated output\nmake check-docs-drift      # guard book prose, SUMMARY.md coverage, and page links\nmake check-docs-mermaid    # assert book diagrams rendered (needs make build-docs first)\nmake check-docs-readme     # guard published package READMEs against the 100-line limit\nmake check-docs-prose      # Vale over the handwritten book pages. Needs vale on PATH:\n                           # `brew install vale`; pinned by VALE_VERSION in the Makefile\nmake serve-docs            # mdbook serve --open\n\n# Changelog (CHANGELOG.md is gitignored; cliff.toml + git history is source of truth)\nmake changelog                              # generate CHANGELOG.md (preview unreleased)\nmake changelog-release VERSION=v0.2.0       # preview a tag's release notes (renders exactly one section)\nmake render-changelog                       # render-only validation (lint smoke)\nmake check-release-notes                    # regression guard: every release renders exactly one section\n\n# Versions and branches (docs/guide/gitflow-protocol.md is normative)\nmake list-version-sites                     # every place a release version is written\nmake set-version VERSION=0.4.1-dev          # write them all; then cargo check && uv lock\nmake check-branch-containment               # last release tag in dev, origin/main in release/* (no-op elsewhere)\n```\n\n## Shared Conventions\n\n### License Header\n\nEvery new source file must begin with the AGPL license header. Use `//` comments for Rust, `#` comments for Python. In Zed, the `agpl` snippet (`.zed/snippets.json`) inserts the Rust header automatically.\n\n```\nCopyright (C) 2026 Postquant Labs Incorporated\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License\nalong with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nSPDX-License-Identifier: AGPL-3.0-or-later\n```\n\n### DCO Sign-Off\n\nCommits must be signed off: `git commit -s` (DCO requirement from `CONTRIBUTING.md`).\n\n### Branching\n\nTwo long-lived branches. [`docs/guide/gitflow-protocol.md`](docs/guide/gitflow-protocol.md) is normative for routing, releases, betas and the back-merge; do not restate it. Before 1.0:\n\n- **`main`** is the non-breaking line, at the next patch's `-dev` version. Everything lands by merge request, except the version bump that reopens it after a release: the code owners push that directly, and `verify:policy` (`scripts/check-main-direct-push.sh`) fails any other direct push.\n- **`dev`** is the breaking line, at the next minor's `-dev` version. Maintainers push to it for back-merges and version bumps only. Anything authored goes through a merge request, because a direct push skips the title check and the atomic spec-MR rule.\n\nRoute every change by one question: **does it break?** Before 1.0 the minor is the breaking bump, so \"is this a fix?\" is the wrong question -- a non-breaking feature goes to `main` and a breaking fix goes to `dev`. A breaking change branches from `dev`, targets `dev`, and carries `!` in its merge request title and commit subjects. Everything else branches from and targets `main`. There is no `hotfix/` branch before 1.0. Branch names stay `feature/qui-<id>` on either line; the target is set on the merge request, not in the name.\n\nWhen opening a merge request, answer the **Compatibility** block in `.gitlab/merge_request_templates/default.md` -- GitLab auto-populates that template, and its answer decides the target branch, so it has to match the branch chosen on the form. Leaving it blank means an unrouted change. A non-breaking claim needs two or three lines naming what the change touched and why a consumer pinned to the current minor can take it without editing their code. Releases use `release.md` instead (`glab mr create --template release`).\n\nNothing is squashed on merge. The one exception is post-1.0 `hotfix/*` into `main`.\n\nFeature branches stay linear: update one by rebasing onto its target, never by merging the target in, and merge stacked merge requests bottom-up rather than into each other. A merge commit inside a branch reaches the line with it; `verify:policy` fails the merge request. See \"Merge commits\" in the git protocol.\n\n### Conventional Commits\n\nAll commit messages must follow the [Conventional Commits](https://www.conventionalcommits.org/) format:\n\n```\n<type>(<scope>): <subject>\n\n[optional body]\n\n[optional footer(s)]\n```\n\n**Types:** `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`, `security`, `deprecate`, `release`\n\n**Scope** is optional. When used, it should be the crate or package name (e.g. `xqvm`, `xqcp`, `conformance`).\n\n**Rules:**\n- Subject line: imperative mood, lowercase start, no trailing period, max 72 characters\n- Body: wrap at 72 characters, explain what and why\n- Footer: reference the tracking issue (e.g. `Fixes #123`) to auto-link and close it on merge\n- Breaking changes: append `!` after type/scope (e.g. `feat(xqvm)!: remove deprecated API`) or add a `BREAKING CHANGE:` footer\n- NEVER add `Co-Authored-By` trailers for AI assistants\n\n**Example:**\n\n```\nfix(xqasm): handle forward label references in nested loops\n\nThe two-pass label resolver was not accounting for label offsets\ninside nested RANGE blocks, causing incorrect jump targets when\na forward reference crossed a loop boundary.\n\nFixes #456\n```\n\n### Post-Edit Linting\n\nAfter modifying files, run `make fmt` to format everything, or the per-file equivalents from the commands section: `cargo fmt` for Rust, `uvx ruff@<pinned version> check --fix <file>` + `uvx ruff@<pinned version> format <file>` for Python (pin lives in the Makefile's `RUFF_VERSION`), `taplo fmt` for TOML.\n\n### Constraints\n\n- NEVER use emojis in code, documentation, or commit messages.\n- NEVER use em-dash in documentation -- prefer using `--`.\n- Use 4 spaces for indentation (no tabs).\n- Aggregate edits to a single file into one pass. Do not thrash multiple small edits to the same file in sequence.\n\n### Naming\n\n- `snake_case` for functions, modules, and variables.\n- `PascalCase` for types, traits, and classes.\n- Follow `CONTRIBUTING.md` for additional Rust conventions and ruff/pycodestyle for Python.\n\n## Rust\n\n### Principles\n\n- **Safety First:** zero `unsafe` code unless absolutely necessary. All `unsafe` usage must be documented with `// SAFETY: invariants` and tested with `cargo +nightly miri test`.\n- **Idiomatic Rust:** follow `CONTRIBUTING.md` for contribution standards and idiomatic code.\n- **Functional-style code:** prefer functional interfaces over imperative code. Use imperative code if functional-style code is less clear.\n- **Performance:** zero-cost abstractions. Be efficient in terms of memory use and performance. Prefer stack allocation over heap.\n- **Ownership:** design ownership/borrowing structures *before* writing logic.\n- **DRY:** extract repeated error construction, span computation, and validation logic into private helper functions. Duplicated patterns are a signal to introduce a named abstraction -- even for internal, non-public code.\n\n### Development Workflow\n\n- **Architecture:** analyze crates, lifetimes, and public APIs first (methods, traits, etc.). Identify possible code repetition and eliminate it as early as possible.\n- **Implementation:**\n  - Prefer to use already existent libraries instead of reinventing the wheel.\n  - Do not use `unwrap()`. Use safer alternatives.\n  - Avoid subscripting or explicit slicing (e.g. `foo[3]`, `bar[0..2]`) and use `get`, `get_mut` instead.\n  - Use `panic`s and `assert`s only for testing and invariant violations.\n  - Avoid vague error messages, attach wider context to error messages to improve debugging availability.\n  - Use `miette` (`eyreish` sub-module) for application errors.\n  - Use `clap` for CLIs.\n  - Use `rayon` for CPU-bound tasks that may benefit from parallelism.\n  - The workspace enforces `unsafe-code = \"deny\"` and `rust-2018-idioms = \"deny\"` as hard errors. Key warnings that become blocking on CI: `indexing-slicing` (use `.get()`/`.get_mut()` with proper error handling instead of `[]`), `unused-results` (must handle or discard with `let _ =`).\n- **Code organisation:**\n  - Organise code in crates that takes up to one responsibility.\n  - Every crate should consist of `lib.rs` -- facade module that exposes public API by re-exporting other module items. Keep inner modules as private as possible.\n  - Design code using `newtype`s rather than type aliases.\n- **Writing tests:**\n  - Write unit tests for every change, take care of edge cases, use fuzzy testing if possible.\n  - Write integration tests in `tests/` directory.\n  - Write micro-benchmarks in `benches/` directory.\n- **Documentation:** document every publicly exposed element of API with this format:\n\n  ```rust\n  /// Short description, up to two sentences: Does this and that.\n  ///\n  /// Paragraph with a longer description of the code logic and behavior on certain inputs.\n  ///\n  /// # Examples\n  ///\n  /// ```rust\n  /// let foo: Foo = Foo::foo();\n  /// assert!(foo.works_ok());\n  /// ```\n  ///\n  /// # Panics\n  /// Description when function panics for unexpected reason.\n  ///\n  /// # Errors\n  /// Description when the function returns a business logic error.\n  ///\n  /// # Safety\n  /// Safety invariants and how the function is safe to use, if marked `unsafe`.\n  ```\n\n  When documenting a publicly exposed module, write a simple description of what the module is doing and how to use code written there. Add examples of how to use the API inside the module.\n- **Review:** perform a self-review of API surface area for ergonomics, safety, and code repetitions.\n- **Validation:** run `make lint` for checking lints. Then run `make test` to test the code.\n- **License compliance:** check compliancy of libraries added to the project.\n  - Check whether `cargo deny` passes.\n  - If not, check if the license of the library is compatible with `AGPL-3.0-or-later`.\n  - If compatible, add the license to `deny.toml`.\n  - If not compatible, look for compatible alternatives in `crates.io`.\n  - If there are no alternatives, write yourself a code that will fulfill the same needs.\n  - Update `NOTICE` file accordingly as the library added to the project.\n\n### Staff-Level Responsibilities\n\n- Focus on reducing complexity in `Cargo.toml`.\n- Optimize for build times (parallel processing, reducing dependencies).\n- Ensure high test coverage for edge cases (fuzz testing if necessary).\n- If the dependency is used widely enough, add it to the Cargo workspace (like `thiserror`, `rayon` or `itertools`).\n\n### Architecture\n\n#### Crate Map\n\n| Crate | Path | Role |\n| --- | --- | --- |\n| `xqvm` | `xqvm/` | Bytecode definitions, opcode table, instruction types, builder, codec, stream reader, VM interpreter, disassembler |\n| `xqasm` | `xqasm/` | Text assembler: pest parser -> AST -> bytecode |\n| `xqcli` | `xqcli/` | CLI binary (`xquad`): asm, dism, run, verify subcommands |\n| `xqffi` | `xqffi/` | PyO3 bindings exposing xqasm + xqvm to Python |\n| `xquad-conformance` | `conformance/` | Cross-implementation conformance harness |\n\n#### Key Patterns\n\n**X-Macro opcode table** (`xqvm/src/bytecode/types/table.rs`) -- The `opcodes!` macro is the single source of truth for all 93 instructions. The `Opcode` enum, `Instruction` enum, mnemonic strings, and operand arity are all derived from it. When adding or changing an opcode, edit only this table.\n\n**Two-pass label resolution** (`xqvm/src/bytecode/builder.rs`) -- `InstructionBuilder` records unresolved jump fixups on the first pass and patches offsets at `build()` time, supporting both forward and backward label references.\n\n**Binary codec** (`xqvm/src/bytecode/codec.rs`) -- Uses `oxicode` with BE fixint encoding: opcode byte followed by operand fields at their natural width in big-endian byte order (`i16` = 2 bytes, `[u8; N]` = N bytes, `u8`/`Register` = 1 byte). No varints, no length prefixes. `InstructionStream` (`stream.rs`) is an incremental seekable reader over encoded bytes. Mnemonic strings inside the bytecode crate use `pastey` for no-std compact string storage (avoids heap allocation for fixed-length identifiers).\n\n**Assembly pipeline** (`xqasm/`) -- `pest` grammar -> `ast::Program` -> `assembler::assemble()` -> `InstructionBuilder` -> `codec::encode`. Rich `miette` diagnostics with source spans are emitted at the assembler stage.\n\n**VM interpreter** (`xqvm/`) -- `Vm` executes a `Program` (raw instruction bytes) through `bytecode::Cursor`, a crate-internal, label-free reader that decodes exactly as `InstructionStream` does. State: 256-slot register file (`RegVal` enum: `Int(i64)`, `VecInt(Vec<i64>)`, `VecXqmx(Vec<XqmxModel>)`, `Model(XqmxModel)`, `Sample(XqmxSample)`), an unbounded integer stack, and a loop stack of `LoopFrame` records (one per `RANGE`/`ITER`). `StepResult` drives control flow: `Continue`, `Jump(offset)`, `Halt`, `StartLoop`. Default step limit is 10,000,000 (configurable via `set_step_limit()`). A second budget bounds memory: every allocating opcode is charged bytes before it allocates, against a 1 GiB default (configurable via `set_memory_limit()`), and raises `MemoryLimitExceeded` when it cannot pay. Calldata and output slots are injected before `run()` via `set_calldata()` / `set_output_slots()`. VM errors carry `into_diagnostic(&program, source_name)` which disassembles the failing offset for miette source annotation. `clippy::result_large_err` is explicitly allowed in the asm crate because `NamedSource<Arc<str>>` on the error path is intentional.\n\n#### Instruction Set Categories (93 total)\n\nControl flow, stack/register I/O, arithmetic (including `SQR`, `ABS`, `INC`, `DEC`, `MIN`, `MAX`), comparison, logical/bitwise, QUBO/Ising/integer matrix allocators (`BQMX`, `SQMX`, `XQMX`), sample allocators, vector ops, index math, matrix coefficient access, grid ops, high-level constraints (`ONEHOTR`, `ONEHOTC`, `EXCLUDE`, `IMPLIES`, `EQUALITY`, `ATLEAST`, `ATLEASTW`, `REDUCE`), and `ENERGY`.\n\n## Python\n\n### Dependencies & Environment\n\n- **Dependencies:** manage via each package's `pyproject.toml`. The repo-root `pyproject.toml` hosts the `uv` workspace declaration and dev-tool pins (maturin, pytest, pyyaml, ruff); the xqvm_py / xqcp / xqsa / xqffi members carry their own. Never modify the dev-dep pins without explicit user approval.\n- **Virtual environment:** always use the workspace `.venv/` managed by `uv sync`. Never install packages globally or create ad-hoc venvs. Invoke scripts and tests through the `make` targets or `uv run`, which picks up the workspace packages without a manual activation step. `xqffi/pyproject.toml` lists the cdylib's Rust inputs as uv `cache-keys`, so any `uv sync` or `uv run` rebuilds `xqffi` after a Rust edit in `xqffi`, `xqvm` or `xqasm`; uv compares modification times, so a copy that keeps an old mtime over new content is not seen. The make targets pass `--no-sync` to skip a second resolve after their own sync. For an extra, `uv sync --extra <name>`, then `uv run ...`; `uv run` keeps an installed extra, but a plain `uv sync`, including the one in `make deps-py` and `make test-py`, removes it again.\n- **Setup:** `make deps-py` runs `uv sync`, which builds `xqffi` through maturin with `cargo --locked` (the root `pyproject.toml`'s `config-settings-package`). Each package's editable install carries `dev-mode-dirs = [\"..\"]`, which puts the repo root on `sys.path`. Re-run after pulls that touch Rust sources or workspace deps.\n\n### Package Map\n\n| Package | Path | Role |\n| --- | --- | --- |\n| `xqvm_py` | `xqvm_py/` | Python reference VM implementation (conformance oracle) |\n| `xqcp` | `xqcp/` | High-level constraint programming DSL compiling to XQVM assembly |\n| `xqsa` | `xqsa/` | Solver adapters for XQMX models (dwave-samplers; pluggable solver interface) |\n| `xqffi` | `xqffi/` | PyO3 FFI bindings (maturin-built); also a Rust crate |\n| `xquad` | `xquad/` | Umbrella meta-package re-exporting xqffi, xqcp, xqsa under unified namespace |\n\n### Testing\n\n`make test-py` runs pytest across `xqvm_py/tests`, `xqcp/tests`, `xqsa/tests`, `xquad/tests`. Test paths are configured in the root `pyproject.toml` under `[tool.pytest.ini_options]`.\n\n## Cross-Language\n\n### Specifications\n\nThe `spec/` directory contains authoritative specifications for each toolchain component. Read the relevant spec before modifying that component:\n\n- `spec/xqvm/` -- XQVM architecture (opcodes, control flow, type system, encoding)\n- `spec/xqcp/README.md` -- XQCP constraint programming DSL\n- `spec/xqsa/README.md` -- XQSA solver adapter interface\n\nSpec changes are governed by the conformance harness: any modification affecting the opcode table, control-flow rules, stack depth, type system, or HLF expansions must be mirrored in `conformance/opcodes.yaml` and validated against `xqvm_py/opcodes.py` via `scripts/check-opcode-parity.py`.\n\n### Conformance Vectors\n\nBehavioural parity between `xqvm_py` (Python reference) and the Rust `xqvm` crate is enforced by the `xquad-conformance` test suite. Vectors live in `conformance/vectors/`. New semantics require a new vector; divergence between impls fails CI with no drift-tracking middle ground.\n\n### Atomic Spec-MR Rule\n\nAny MR that changes VM semantics must touch **all four** layers in the same MR: (1) `spec/xqvm/*.md`, (2) `xqvm/src/**/*.rs`, (3) `xqvm_py/{executor,opcodes,xqmx,state,vector,tracer,errors}.py`, (4) `conformance/vectors/**` or `conformance/opcodes.yaml`. CI enforces this via `verify:policy` (`scripts/check-atomic-spec-mr.sh`). MRs touching 0 or all 4 layers pass; partial changes (1-3 layers) fail.\n\nFor deliberately one-sided changes (e.g. aligning one impl to existing behaviour), add an `Atomic-Spec-Exempt: QUI-<id> <reason>` trailer to a commit message. It goes in the message's last paragraph at column 0, beside the sign-off, with the whole reason and the ticket on that one line; git reads trailers from the last paragraph only and truncates a wrapped reason, so the guard fails on either instead of bypassing. A `Fixes QUI-NNN` footer may share the paragraph but not the line directly below the trailer. The guard scans every commit in the MR range and bypasses when it finds at least one well-formed trailer. See `docs/guide/development-workflow.md` for the full rationale and exempt cases.\n\n### Opcode Addition Gate\n\nAdding a row to the `opcodes!` table in `xqvm/src/bytecode/types/table.rs` is a change to VM semantics. The MR that adds it argues in its description that the new opcode clears all six clauses of the on-chain admissibility bar: no floating point, no host I/O or ambient state, no nondeterministic iteration order, bounded allocation, bounded per-instruction work, and behaviour specified in `spec/xqvm/` with a conformance vector covering it. An opcode that cannot clear all six does not ship; the operation belongs in `xqcp`, `xqsa`, the `xquad` API or a helper library instead.\n\nThere is deliberately no CI guard for this one -- it is a correctness argument a reviewer weighs, not a string a script can find. `docs/guide/development-workflow.md` is required reading before changing VM semantics and carries the six clauses in full.\n\n### Rust-Python Bindings (xqffi)\n\n`xqvm_py` consumes `xqffi.asm` only -- its executor stays pure-Python so `xqvm_py` remains an independent conformance oracle. `uv sync` builds it (handled by `make deps-py`).\n\n### Examples & Smoke Tests\n\n`examples/tsp/` (Travelling Salesman) and `examples/maxcut/` (Max-Cut) each consist of `.xqasm` programs driven by a Python runner (`runner.py`) that exercises both the Rust and Python interpreters via the `--interpreter` flag. These are the canonical references for how host code loads and runs `.xqasm` programs via the toolchain. `make example-smoke` runs both interpreters and checks each produces a valid solution (`valid == 1`); the check is invariant-based, not golden-file diffing.\n\n### CI Pipeline\n\nFive phases, each answering one question about the change. `verify` and\n`test` read as synonyms, so the boundary is stated explicitly rather than\nleft to be inferred per job -- an unwritten boundary is exactly how the\nold `lint` stage decayed into four unrelated concerns (source formatting,\nRust compilation, cross-implementation parity, release packaging) that\nhappened to share a stage barrier and nothing else:\n\n- **`verify`** asks whether something matches what it is required to\n  match -- a licence against policy, one VM implementation's output\n  against the other's.\n- **`test`** asks whether something does what it should when actually\n  executed. Not a static-vs-dynamic split (`verify:parity` runs the VM);\n  it is consistency between artefacts versus correctness of one artefact.\n\n| Phase | Question it answers | What it covers |\n| --- | --- | --- |\n| `verify` | Does the workspace match what it's required to match? | clippy, rustdoc, cargo-deny (root workspace and pallet fixture), ruff, `uv.lock` freshness, the fresh-xqffi-cdylib check, opcode parity, Rust + Python conformance vectors, example smoke tests, atomic spec-MR guard, commit-message guard, merge-request-title guard, branch containment guard, changelog render |\n| `test` | Does the workspace do what it should when executed? | unit, integration, doc tests (Rust); pytest (Python); Quip signing-layer tests; WASM no_std tests; Substrate pallet fixture |\n| `hardware` | Does it work on real hardware? | CUDA, D-Wave QPU, and Metal solver tests on real hardware (protected refs only) |\n| `docs` | Is the documentation correct and buildable? | generated-docs freshness, docs drift guard, package README length guard, mdbook build, GitLab Pages publish (release tags only) |\n| `release` | Is the artefact publishable, and (on a tag) published? | `release:validate` packaging checks on every pipeline including tags; crates.io + PyPI publishing and GitLab Release notes via git-cliff on a pushed tag |\n\n**Gating topology.** `verify:*` and `test:*` jobs all carry `needs: []`\nand start together at t=0 -- peers, not a chain; a `verify` failure does\nnot hold `test` back. Every `hardware:*` job then `needs:` the full\nverify+test set, so nothing touches real hardware before the code is\nknown to compile and pass its own test suite -- a GPU, a metered D-Wave\nQPU token, and a macOS runner are all scarce and/or billed, and none of\nthem should be spent on code that does not even build. `docs:*` and\n`release:validate` jobs in turn `needs:` verify+test+hardware, and the\ntag-only publish chain (`release:crates` -> `release:pypi` ->\n`release:notes`) is gated transitively through `release:validate`'s\n`needs:` edge rather than through the stage barrier -- so the\ndocumentation site and the release path both wait on hardware being\nproven, not just on the workspace compiling. The accepted tradeoff: an\noffline GPU runner or an expired D-Wave token stalls the documentation\nsite, even though nothing in the docs content depends on hardware\npassing -- we would rather stall the docs than publish a book\ndescribing an opcode or solver behaviour the hardware suite just\nproved broken. On a tag the same gate now sits ahead of the publish\nchain too, so the same expired token stalls a release and not just the\ndocs -- `test:substrate`'s cold-cache build is the largest single wait\non that path. The exact graph and per-edge reasoning (including why\n`hardware:*` needs are marked `optional: true`) live in\n`.gitlab/ci/setup.yml`'s \"Dependency gating\" section.\n\n**Path gating.** Three jobs do not run on every pipeline. `test:wasm` and\n`test:substrate` are gated on `rules: changes:`, because each builds one\ncrate (`xqvm`) into one fixture and so has a narrow, writable input\nfootprint, while every other job in the pipeline is a whole-workspace\ncheck whose verdict a change anywhere can flip. They are the two most\nexpensive jobs in the pipeline and the least often relevant, so gating\nthem is most of the merge-request latency available to save.\n`verify:substrate-deny` runs cargo-deny over the pallet fixture's graph,\nwhich needs the same polkadot-sdk download, and is gated the same way.\nAll three stay unconditional on protected refs and on tags: the gate\nbuys latency, not coverage, and a path list is a claim about a build\ngraph that can be wrong -- keeping the protected refs unconditional\nmeans a wrong list costs a late signal on `main` rather than a shipped\nregression. Because any of them can be absent, every `needs:` edge into\nthem is `optional: true`; GitLab refuses to create a pipeline whose job\nneeds an absent job. The path lists, the per-clause reasoning, and the\nper-entry justification live in `.gitlab/ci/test.yml`'s \"Path gating\"\nsection and beside `verify:substrate-deny` in `.gitlab/ci/verify.yml`.\nLocal `make preflight-rs` runs all three targets unconditionally.\n\n**CI signals.** Two reds are expected, and each means a step of the release protocol is outstanding rather than that something is broken. Do not \"fix\" either by anything but the step it names:\n\n- **`dev` red from `check-branch-containment`** (in `verify:policy`): a release tag on `main` is not yet in `dev`. Every push pipeline on `dev` stays red until someone back-merges `main` into `dev`. Ordinary merges into `main` between releases do not trigger it. Merge requests into `dev` are not judged by it, so work continues in parallel.\n- **`main` red from `check-version-sites`** (in `release:validate`): `main` carries a release version. This follows every release merge until the direct push reopening `main` at the next patch's `-dev` version lands.\n\nA third is a gate, not a signal: a merge request from `release/*` fails `verify:policy` when the release branch does not contain `origin/main`. The fix is a back-merge of `main` into the release branch.\n\n**Naming.** A job's name prefix is its phase (`verify:rust` runs in the\n`verify` stage, `docs:build` in `docs`) -- that is the CI-side taxonomy.\nThe Makefile stays action-first (`<action>-<subject>`, e.g. `lint-rust`,\n`build-docs`) -- that is the local-workflow taxonomy -- so most job names\ndo not map onto their target mechanically: `verify:rust` runs `make -k\nlint-rust`, `docs:build` runs `make -k build-docs`. CI always invokes\nthese with `-k` so every prerequisite in a merged job is attempted even\nafter one fails; a plain local `make lint-rust` fails fast on the first,\nlike any other non-`-k` target.\n\nJobs are authored in per-stage files under `.gitlab/ci/` and composed via `include:` in the root `.gitlab-ci.yml`.\n\n### Changelog\n\n`CHANGELOG.md` is **not** committed to the source tree. The source of truth is `cliff.toml` plus the conventional-commit log; the file is regenerated on demand via `make changelog` and published as the GitLab Release description on tag (`release:notes` in `.gitlab/ci/release.yml`).\n\nCaveats:\n\n- Pre-conventional-commits history (everything before QUI-480) is filtered out by `filter_unconventional = true`; only commits on or after the QUI-480 enforcement appear in the rendered output. `make changelog` renders the whole unreleased history with no other scoping, so an empty render is the expected state until the first user-visible `feat`/`fix` lands.\n- `chore`, `style`, `test`, `ci`, `build` are **dropped silently** -- if a commit under one of those types ships a user-visible change (e.g. a security-relevant dep bump under `chore`), promote it to `feat`/`fix`/`security` before merging or it will be invisible in release notes.\n- `cliff.toml`'s `tag_pattern` scopes each release's notes to that release alone: it is three numeric fields and nothing else, so a prerelease tag (`-rcN`, `-betaN`) never becomes a range boundary and its commits fold into the following release's section instead of getting a page of their own. `changelog-release` (the Makefile target `release:notes` invokes) pairs this with an explicit `PREV..VERSION` range, where `PREV` is the nearest release predecessor tag (`git describe --exclude='*-*'`, the same pattern in glob form), rather than an unbounded `--tag`. `release:notes` and `docs:publish` match the same pattern, so a prerelease publishes to both registries with no Release page and no documentation. `make check-release-notes` (`scripts/check-release-notes.sh`, run as part of `lint-policy` / `verify:policy`) is the regression guard: it renders every release tag's range plus the pre-tag preview and asserts each yields exactly one `## [` heading. Mirror the pattern wherever a tag is classified; a denylist of prerelease spellings is what let `-beta` through before.\n- Before tagging a release, run `make changelog-release VERSION=vX.Y.Z` locally to preview what the GitLab Release page will say -- the render contains exactly one section. Bad commit subjects can be fixed on the source branch and re-merged before the tag is cut.\n\n## Local overrides\n\nPersonal, repository-specific configuration goes in `AGENTS.local.md` (gitignored). It is\nimported below and silently skipped for collaborators who do not have one.\n\n@AGENTS.local.md\n","isInternal":false,"tokens":8340,"sizeBytes":33836}],"systemPromptSnippet":"<yakaai_skills repo=\"QuipNetwork/xq-rs\">\n<!-- File: AGENTS.md (Tokens: ~8340 | Category: root-instruction) -->\n# AGENTS.md\n\nThis file is the authoritative, tool-neutral source of truth for AI coding assistants\nand human contributors working in this repository. It holds repo-wide conventions for\nany collaborator.\n\nTool-specific agent config files (`CLAUDE.md`, etc.) are not checked into git -- each\ndeveloper manages their own locally. Claude Code does not read `AGENTS.md` natively, so\nClaude Code users should add a gitignored `CLAUDE.md` (or `CLAUDE.local.md`) whose first\nline is `@AGENTS.md` to auto-load this file. Personal, repository-specific overrides go\nin `AGENTS.local.md` (gitignored), imported at the end of this file and silently skipped\nfor anyone who does not have one.\n\nThe **XQuad Toolchain** is a hardware-agnostic quantum VM and SDK: a problem is\nexpressed once in XQVM bytecode and executed on any supported quantum backend\n(annealers, gate-based chips, etc.). Think LLVM for quantum computing. The codebase\nis dual-language: a Rust core (VM, assembler, bytecode, CLI) with Python interfaces\n(reference VM, constraint programming DSL, solver adapters, FFI bindings).\n\nYou are a senior engineer with deep expertise in Rust 2024 edition and Python 3.13+,\nspecializing in compiler engineering, systems programming, and high-performance\nquantum computing SDKs. You emphasize memory safety, zero-cost abstractions, and\ncross-language correctness.\n\n## Quick-Reference Commands\n\n```sh\n# Full suite (what CI runs)\nmake all              # fmt + lint + test (Rust + Python)\nmake xquad            # bootstrap local dev: Python venv + install xquad CLI\nmake install-hooks    # point git at .githooks/ pre-commit hook\n\n# Preflight (run locally exactly what CI enforces; N/A a language you didn't touch)\nmake preflight         # preflight-rs + preflight-py + preflight-parity + preflight-docs + preflight-policy\nmake preflight-rs      # fmt, taplo, clippy, rustdoc, deny (root + pallet fixture), unit/integration/doc tests\nmake preflight-py      # taplo, ruff format + lint, pytest, uv.lock freshness\nmake preflight-parity  # opcode parity, conformance, example smoke\nmake preflight-docs    # generated-doc freshness + docs drift + README length + prose (needs vale)\nmake preflight-release # crate packaging dry-run + five Python dists (needs maturin/twine/uv; not in `preflight`)\n\n# Rust\nmake fmt              # cargo fmt + taplo fmt + ruff format\nmake lint             # lint-clippy + lint-doc + lint-deny-rs + lint-py + fmt-check\nmake lint-clippy      # cargo clippy --workspace --all-targets --all-features -- -D warnings\nmake lint-doc         # RUSTDOCFLAGS=\"-D warnings\" cargo doc --workspace --all-features --no-deps\nmake lint-deny-rs     # cargo deny check\nmake lint-deny-fixture # cargo deny over fixtures/pallet-xqvm, with its own deny.toml\nmake test             # test-unit-rs + test-integ-rs + test-doc + test-py\nmake test-unit-rs     # cargo nextest run --workspace --all-features --lib\nmake test-integ-rs    # cargo nextest run --workspace --exclude xquad-conformance --all-features --test '*'\nmake test-doc         # cargo test --doc --workspace --all-features\nmake test-miri        # cargo +nightly miri test --workspace --all-features\nmake deps             # install rustup components + pinned cargo tools\nmake deps-miri        # install nightly + miri\n\n# Single Rust test by name\ncargo nextest run --workspace -E 'test(my_test_name)'\ncargo test --workspace my_test_name\n\n# Python\nmake deps-py          # uv sync: editable installs, xqffi built from current Rust\nmake fmt-py           # ruff format across all Python packages\nmake fmt-check-py     # ruff format --check\nmake lint-py          # ruff check across all Python packages\nmake test-py          # pytest xqvm_py/tests xqcp/tests xqsa/tests xquad/tests\nmake check-uv-lock     # uv lock --check -- fails if uv.lock is stale against pyproject.toml\nmake check-xqffi-fresh # uv sync --extra dwave + import xquad -- asserts the xqffi cdylib is\n                       # fresh; mutates .venv/, re-run `make deps-py` afterwards\nmake repl             # Python REPL with xqffi + workspace packages\n\n# Cross-language\nmake opcode-parity    # opcode-parity-rs + opcode-parity-py\nmake conformance      # conformance-rs + conformance-py\nmake example-smoke    # run examples on both interpreters, check valid == 1\n\n# Documentation\nmake build-docs            # mdbook build\nmake regen-docs            # regenerate generated opcode and example book pages\nmake check-docs-generated  # assert generated docs match regenerated output\nmake check-docs-drift      # guard book prose, SUMMARY.md coverage, and page links\nmake check-docs-mermaid    # assert book diagrams rendered (needs make build-docs first)\nmake check-docs-readme     # guard published package READMEs against the 100-line limit\nmake check-docs-prose      # Vale over the handwritten book pages. Needs vale on PATH:\n                           # `brew install vale`; pinned by VALE_VERSION in the Makefile\nmake serve-docs            # mdbook serve --open\n\n# Changelog (CHANGELOG.md is gitignored; cliff.toml + git history is source of truth)\nmake changelog                              # generate CHANGELOG.md (preview unreleased)\nmake changelog-release VERSION=v0.2.0       # preview a tag's release notes (renders exactly one section)\nmake render-changelog                       # render-only validation (lint smoke)\nmake check-release-notes                    # regression guard: every release renders exactly one section\n\n# Versions and branches (docs/guide/gitflow-protocol.md is normative)\nmake list-version-sites                     # every place a release version is written\nmake set-version VERSION=0.4.1-dev          # write them all; then cargo check && uv lock\nmake check-branch-containment               # last release tag in dev, origin/main in release/* (no-op elsewhere)\n```\n\n## Shared Conventions\n\n### License Header\n\nEvery new source file must begin with the AGPL license header. Use `//` comments for Rust, `#` comments for Python. In Zed, the `agpl` snippet (`.zed/snippets.json`) inserts the Rust header automatically.\n\n```\nCopyright (C) 2026 Postquant Labs Incorporated\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU Affero General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License\nalong with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nSPDX-License-Identifier: AGPL-3.0-or-later\n```\n\n### DCO Sign-Off\n\nCommits must be signed off: `git commit -s` (DCO requirement from `CONTRIBUTING.md`).\n\n### Branching\n\nTwo long-lived branches. [`docs/guide/gitflow-protocol.md`](docs/guide/gitflow-protocol.md) is normative for routing, releases, betas and the back-merge; do not restate it. Before 1.0:\n\n- **`main`** is the non-breaking line, at the next patch's `-dev` version. Everything lands by merge request, except the version bump that reopens it after a release: the code owners push that directly, and `verify:policy` (`scripts/check-main-direct-push.sh`) fails any other direct push.\n- **`dev`** is the breaking line, at the next minor's `-dev` version. Maintainers push to it for back-merges and version bumps only. Anything authored goes through a merge request, because a direct push skips the title check and the atomic spec-MR rule.\n\nRoute every change by one question: **does it break?** Before 1.0 the minor is the breaking bump, so \"is this a fix?\" is the wrong question -- a non-breaking feature goes to `main` and a breaking fix goes to `dev`. A breaking change branches from `dev`, targets `dev`, and carries `!` in its merge request title and commit subjects. Everything else branches from and targets `main`. There is no `hotfix/` branch before 1.0. Branch names stay `feature/qui-<id>` on either line; the target is set on the merge request, not in the name.\n\nWhen opening a merge request, answer the **Compatibility** block in `.gitlab/merge_request_templates/default.md` -- GitLab auto-populates that template, and its answer decides the target branch, so it has to match the branch chosen on the form. Leaving it blank means an unrouted change. A non-breaking claim needs two or three lines naming what the change touched and why a consumer pinned to the current minor can take it without editing their code. Releases use `release.md` instead (`glab mr create --template release`).\n\nNothing is squashed on merge. The one exception is post-1.0 `hotfix/*` into `main`.\n\nFeature branches stay linear: update one by rebasing onto its target, never by merging the target in, and merge stacked merge requests bottom-up rather than into each other. A merge commit inside a branch reaches the line with it; `verify:policy` fails the merge request. See \"Merge commits\" in the git protocol.\n\n### Conventional Commits\n\nAll commit messages must follow the [Conventional Commits](https://www.conventionalcommits.org/) format:\n\n```\n<type>(<scope>): <subject>\n\n[optional body]\n\n[optional footer(s)]\n```\n\n**Types:** `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`, `security`, `deprecate`, `release`\n\n**Scope** is optional. When used, it should be the crate or package name (e.g. `xqvm`, `xqcp`, `conformance`).\n\n**Rules:**\n- Subject line: imperative mood, lowercase start, no trailing period, max 72 characters\n- Body: wrap at 72 characters, explain what and why\n- Footer: reference the tracking issue (e.g. `Fixes #123`) to auto-link and close it on merge\n- Breaking changes: append `!` after type/scope (e.g. `feat(xqvm)!: remove deprecated API`) or add a `BREAKING CHANGE:` footer\n- NEVER add `Co-Authored-By` trailers for AI assistants\n\n**Example:**\n\n```\nfix(xqasm): handle forward label references in nested loops\n\nThe two-pass label resolver was not accounting for label offsets\ninside nested RANGE blocks, causing incorrect jump targets when\na forward reference crossed a loop boundary.\n\nFixes #456\n```\n\n### Post-Edit Linting\n\nAfter modifying files, run `make fmt` to format everything, or the per-file equivalents from the commands section: `cargo fmt` for Rust, `uvx ruff@<pinned version> check --fix <file>` + `uvx ruff@<pinned version> format <file>` for Python (pin lives in the Makefile's `RUFF_VERSION`), `taplo fmt` for TOML.\n\n### Constraints\n\n- NEVER use emojis in code, documentation, or commit messages.\n- NEVER use em-dash in documentation -- prefer using `--`.\n- Use 4 spaces for indentation (no tabs).\n- Aggregate edits to a single file into one pass. Do not thrash multiple small edits to the same file in sequence.\n\n### Naming\n\n- `snake_case` for functions, modules, and variables.\n- `PascalCase` for types, traits, and classes.\n- Follow `CONTRIBUTING.md` for additional Rust conventions and ruff/pycodestyle for Python.\n\n## Rust\n\n### Principles\n\n- **Safety First:** zero `unsafe` code unless absolutely necessary. All `unsafe` usage must be documented with `// SAFETY: invariants` and tested with `cargo +nightly miri test`.\n- **Idiomatic Rust:** follow `CONTRIBUTING.md` for contribution standards and idiomatic code.\n- **Functional-style code:** prefer functional interfaces over imperative code. Use imperative code if functional-style code is less clear.\n- **Performance:** zero-cost abstractions. Be efficient in terms of memory use and performance. Prefer stack allocation over heap.\n- **Ownership:** design ownership/borrowing structures *before* writing logic.\n- **DRY:** extract repeated error construction, span computation, and validation logic into private helper functions. Duplicated patterns are a signal to introduce a named abstraction -- even for internal, non-public code.\n\n### Development Workflow\n\n- **Architecture:** analyze crates, lifetimes, and public APIs first (methods, traits, etc.). Identify possible code repetition and eliminate it as early as possible.\n- **Implementation:**\n  - Prefer to use already existent libraries instead of reinventing the wheel.\n  - Do not use `unwrap()`. Use safer alternatives.\n  - Avoid subscripting or explicit slicing (e.g. `foo[3]`, `bar[0..2]`) and use `get`, `get_mut` instead.\n  - Use `panic`s and `assert`s only for testing and invariant violations.\n  - Avoid vague error messages, attach wider context to error messages to improve debugging availability.\n  - Use `miette` (`eyreish` sub-module) for application errors.\n  - Use `clap` for CLIs.\n  - Use `rayon` for CPU-bound tasks that may benefit from parallelism.\n  - The workspace enforces `unsafe-code = \"deny\"` and `rust-2018-idioms = \"deny\"` as hard errors. Key warnings that become blocking on CI: `indexing-slicing` (use `.get()`/`.get_mut()` with proper error handling instead of `[]`), `unused-results` (must handle or discard with `let _ =`).\n- **Code organisation:**\n  - Organise code in crates that takes up to one responsibility.\n  - Every crate should consist of `lib.rs` -- facade module that exposes public API by re-exporting other module items. Keep inner modules as private as possible.\n  - Design code using `newtype`s rather than type aliases.\n- **Writing tests:**\n  - Write unit tests for every change, take care of edge cases, use fuzzy testing if possible.\n  - Write integration tests in `tests/` directory.\n  - Write micro-benchmarks in `benches/` directory.\n- **Documentation:** document every publicly exposed element of API with this format:\n\n  ```rust\n  /// Short description, up to two sentences: Does this and that.\n  ///\n  /// Paragraph with a longer description of the code logic and behavior on certain inputs.\n  ///\n  /// # Examples\n  ///\n  /// ```rust\n  /// let foo: Foo = Foo::foo();\n  /// assert!(foo.works_ok());\n  /// ```\n  ///\n  /// # Panics\n  /// Description when function panics for unexpected reason.\n  ///\n  /// # Errors\n  /// Description when the function returns a business logic error.\n  ///\n  /// # Safety\n  /// Safety invariants and how the function is safe to use, if marked `unsafe`.\n  ```\n\n  When documenting a publicly exposed module, write a simple description of what the module is doing and how to use code written there. Add examples of how to use the API inside the module.\n- **Review:** perform a self-review of API surface area for ergonomics, safety, and code repetitions.\n- **Validation:** run `make lint` for checking lints. Then run `make test` to test the code.\n- **License compliance:** check compliancy of libraries added to the project.\n  - Check whether `cargo deny` passes.\n  - If not, check if the license of the library is compatible with `AGPL-3.0-or-later`.\n  - If compatible, add the license to `deny.toml`.\n  - If not compatible, look for compatible alternatives in `crates.io`.\n  - If there are no alternatives, write yourself a code that will fulfill the same needs.\n  - Update `NOTICE` file accordingly as the library added to the project.\n\n### Staff-Level Responsibilities\n\n- Focus on reducing complexity in `Cargo.toml`.\n- Optimize for build times (parallel processing, reducing dependencies).\n- Ensure high test coverage for edge cases (fuzz testing if necessary).\n- If the dependency is used widely enough, add it to the Cargo workspace (like `thiserror`, `rayon` or `itertools`).\n\n### Architecture\n\n#### Crate Map\n\n| Crate | Path | Role |\n| --- | --- | --- |\n| `xqvm` | `xqvm/` | Bytecode definitions, opcode table, instruction types, builder, codec, stream reader, VM interpreter, disassembler |\n| `xqasm` | `xqasm/` | Text assembler: pest parser -> AST -> bytecode |\n| `xqcli` | `xqcli/` | CLI binary (`xquad`): asm, dism, run, verify subcommands |\n| `xqffi` | `xqffi/` | PyO3 bindings exposing xqasm + xqvm to Python |\n| `xquad-conformance` | `conformance/` | Cross-implementation conformance harness |\n\n#### Key Patterns\n\n**X-Macro opcode table** (`xqvm/src/bytecode/types/table.rs`) -- The `opcodes!` macro is the single source of truth for all 93 instructions. The `Opcode` enum, `Instruction` enum, mnemonic strings, and operand arity are all derived from it. When adding or changing an opcode, edit only this table.\n\n**Two-pass label resolution** (`xqvm/src/bytecode/builder.rs`) -- `InstructionBuilder` records unresolved jump fixups on the first pass and patches offsets at `build()` time, supporting both forward and backward label references.\n\n**Binary codec** (`xqvm/src/bytecode/codec.rs`) -- Uses `oxicode` with BE fixint encoding: opcode byte followed by operand fields at their natural width in big-endian byte order (`i16` = 2 bytes, `[u8; N]` = N bytes, `u8`/`Register` = 1 byte). No varints, no length prefixes. `InstructionStream` (`stream.rs`) is an incremental seekable reader over encoded bytes. Mnemonic strings inside the bytecode crate use `pastey` for no-std compact string storage (avoids heap allocation for fixed-length identifiers).\n\n**Assembly pipeline** (`xqasm/`) -- `pest` grammar -> `ast::Program` -> `assembler::assemble()` -> `InstructionBuilder` -> `codec::encode`. Rich `miette` diagnostics with source spans are emitted at the assembler stage.\n\n**VM interpreter** (`xqvm/`) -- `Vm` executes a `Program` (raw instruction bytes) through `bytecode::Cursor`, a crate-internal, label-free reader that decodes exactly as `InstructionStream` does. State: 256-slot register file (`RegVal` enum: `Int(i64)`, `VecInt(Vec<i64>)`, `VecXqmx(Vec<XqmxModel>)`, `Model(XqmxModel)`, `Sample(XqmxSample)`), an unbounded integer stack, and a loop stack of `LoopFrame` records (one per `RANGE`/`ITER`). `StepResult` drives control flow: `Continue`, `Jump(offset)`, `Halt`, `StartLoop`. Default step limit is 10,000,000 (configurable via `set_step_limit()`). A second budget bounds memory: every allocating opcode is charged bytes before it allocates, against a 1 GiB default (configurable via `set_memory_limit()`), and raises `MemoryLimitExceeded` when it cannot pay. Calldata and output slots are injected before `run()` via `set_calldata()` / `set_output_slots()`. VM errors carry `into_diagnostic(&program, source_name)` which disassembles the failing offset for miette source annotation. `clippy::result_large_err` is explicitly allowed in the asm crate because `NamedSource<Arc<str>>` on the error path is intentional.\n\n#### Instruction Set Categories (93 total)\n\nControl flow, stack/register I/O, arithmetic (including `SQR`, `ABS`, `INC`, `DEC`, `MIN`, `MAX`), comparison, logical/bitwise, QUBO/Ising/integer matrix allocators (`BQMX`, `SQMX`, `XQMX`), sample allocators, vector ops, index math, matrix coefficient access, grid ops, high-level constraints (`ONEHOTR`, `ONEHOTC`, `EXCLUDE`, `IMPLIES`, `EQUALITY`, `ATLEAST`, `ATLEASTW`, `REDUCE`), and `ENERGY`.\n\n## Python\n\n### Dependencies & Environment\n\n- **Dependencies:** manage via each package's `pyproject.toml`. The repo-root `pyproject.toml` hosts the `uv` workspace declaration and dev-tool pins (maturin, pytest, pyyaml, ruff); the xqvm_py / xqcp / xqsa / xqffi members carry their own. Never modify the dev-dep pins without explicit user approval.\n- **Virtual environment:** always use the workspace `.venv/` managed by `uv sync`. Never install packages globally or create ad-hoc venvs. Invoke scripts and tests through the `make` targets or `uv run`, which picks up the workspace packages without a manual activation step. `xqffi/pyproject.toml` lists the cdylib's Rust inputs as uv `cache-keys`, so any `uv sync` or `uv run` rebuilds `xqffi` after a Rust edit in `xqffi`, `xqvm` or `xqasm`; uv compares modification times, so a copy that keeps an old mtime over new content is not seen. The make targets pass `--no-sync` to skip a second resolve after their own sync. For an extra, `uv sync --extra <name>`, then `uv run ...`; `uv run` keeps an installed extra, but a plain `uv sync`, including the one in `make deps-py` and `make test-py`, removes it again.\n- **Setup:** `make deps-py` runs `uv sync`, which builds `xqffi` through maturin with `cargo --locked` (the root `pyproject.toml`'s `config-settings-package`). Each package's editable install carries `dev-mode-dirs = [\"..\"]`, which puts the repo root on `sys.path`. Re-run after pulls that touch Rust sources or workspace deps.\n\n### Package Map\n\n| Package | Path | Role |\n| --- | --- | --- |\n| `xqvm_py` | `xqvm_py/` | Python reference VM implementation (conformance oracle) |\n| `xqcp` | `xqcp/` | High-level constraint programming DSL compiling to XQVM assembly |\n| `xqsa` | `xqsa/` | Solver adapters for XQMX models (dwave-samplers; pluggable solver interface) |\n| `xqffi` | `xqffi/` | PyO3 FFI bindings (maturin-built); also a Rust crate |\n| `xquad` | `xquad/` | Umbrella meta-package re-exporting xqffi, xqcp, xqsa under unified namespace |\n\n### Testing\n\n`make test-py` runs pytest across `xqvm_py/tests`, `xqcp/tests`, `xqsa/tests`, `xquad/tests`. Test paths are configured in the root `pyproject.toml` under `[tool.pytest.ini_options]`.\n\n## Cross-Language\n\n### Specifications\n\nThe `spec/` directory contains authoritative specifications for each toolchain component. Read the relevant spec before modifying that component:\n\n- `spec/xqvm/` -- XQVM architecture (opcodes, control flow, type system, encoding)\n- `spec/xqcp/README.md` -- XQCP constraint programming DSL\n- `spec/xqsa/README.md` -- XQSA solver adapter interface\n\nSpec changes are governed by the conformance harness: any modification affecting the opcode table, control-flow rules, stack depth, type system, or HLF expansions must be mirrored in `conformance/opcodes.yaml` and validated against `xqvm_py/opcodes.py` via `scripts/check-opcode-parity.py`.\n\n### Conformance Vectors\n\nBehavioural parity between `xqvm_py` (Python reference) and the Rust `xqvm` crate is enforced by the `xquad-conformance` test suite. Vectors live in `conformance/vectors/`. New semantics require a new vector; divergence between impls fails CI with no drift-tracking middle ground.\n\n### Atomic Spec-MR Rule\n\nAny MR that changes VM semantics must touch **all four** layers in the same MR: (1) `spec/xqvm/*.md`, (2) `xqvm/src/**/*.rs`, (3) `xqvm_py/{executor,opcodes,xqmx,state,vector,tracer,errors}.py`, (4) `conformance/vectors/**` or `conformance/opcodes.yaml`. CI enforces this via `verify:policy` (`scripts/check-atomic-spec-mr.sh`). MRs touching 0 or all 4 layers pass; partial changes (1-3 layers) fail.\n\nFor deliberately one-sided changes (e.g. aligning one impl to existing behaviour), add an `Atomic-Spec-Exempt: QUI-<id> <reason>` trailer to a commit message. It goes in the message's last paragraph at column 0, beside the sign-off, with the whole reason and the ticket on that one line; git reads trailers from the last paragraph only and truncates a wrapped reason, so the guard fails on either instead of bypassing. A `Fixes QUI-NNN` footer may share the paragraph but not the line directly below the trailer. The guard scans every commit in the MR range and bypasses when it finds at least one well-formed trailer. See `docs/guide/development-workflow.md` for the full rationale and exempt cases.\n\n### Opcode Addition Gate\n\nAdding a row to the `opcodes!` table in `xqvm/src/bytecode/types/table.rs` is a change to VM semantics. The MR that adds it argues in its description that the new opcode clears all six clauses of the on-chain admissibility bar: no floating point, no host I/O or ambient state, no nondeterministic iteration order, bounded allocation, bounded per-instruction work, and behaviour specified in `spec/xqvm/` with a conformance vector covering it. An opcode that cannot clear all six does not ship; the operation belongs in `xqcp`, `xqsa`, the `xquad` API or a helper library instead.\n\nThere is deliberately no CI guard for this one -- it is a correctness argument a reviewer weighs, not a string a script can find. `docs/guide/development-workflow.md` is required reading before changing VM semantics and carries the six clauses in full.\n\n### Rust-Python Bindings (xqffi)\n\n`xqvm_py` consumes `xqffi.asm` only -- its executor stays pure-Python so `xqvm_py` remains an independent conformance oracle. `uv sync` builds it (handled by `make deps-py`).\n\n### Examples & Smoke Tests\n\n`examples/tsp/` (Travelling Salesman) and `examples/maxcut/` (Max-Cut) each consist of `.xqasm` programs driven by a Python runner (`runner.py`) that exercises both the Rust and Python interpreters via the `--interpreter` flag. These are the canonical references for how host code loads and runs `.xqasm` programs via the toolchain. `make example-smoke` runs both interpreters and checks each produces a valid solution (`valid == 1`); the check is invariant-based, not golden-file diffing.\n\n### CI Pipeline\n\nFive phases, each answering one question about the change. `verify` and\n`test` read as synonyms, so the boundary is stated explicitly rather than\nleft to be inferred per job -- an unwritten boundary is exactly how the\nold `lint` stage decayed into four unrelated concerns (source formatting,\nRust compilation, cross-implementation parity, release packaging) that\nhappened to share a stage barrier and nothing else:\n\n- **`verify`** asks whether something matches what it is required to\n  match -- a licence against policy, one VM implementation's output\n  against the other's.\n- **`test`** asks whether something does what it should when actually\n  executed. Not a static-vs-dynamic split (`verify:parity` runs the VM);\n  it is consistency between artefacts versus correctness of one artefact.\n\n| Phase | Question it answers | What it covers |\n| --- | --- | --- |\n| `verify` | Does the workspace match what it's required to match? | clippy, rustdoc, cargo-deny (root workspace and pallet fixture), ruff, `uv.lock` freshness, the fresh-xqffi-cdylib check, opcode parity, Rust + Python conformance vectors, example smoke tests, atomic spec-MR guard, commit-message guard, merge-request-title guard, branch containment guard, changelog render |\n| `test` | Does the workspace do what it should when executed? | unit, integration, doc tests (Rust); pytest (Python); Quip signing-layer tests; WASM no_std tests; Substrate pallet fixture |\n| `hardware` | Does it work on real hardware? | CUDA, D-Wave QPU, and Metal solver tests on real hardware (protected refs only) |\n| `docs` | Is the documentation correct and buildable? | generated-docs freshness, docs drift guard, package README length guard, mdbook build, GitLab Pages publish (release tags only) |\n| `release` | Is the artefact publishable, and (on a tag) published? | `release:validate` packaging checks on every pipeline including tags; crates.io + PyPI publishing and GitLab Release notes via git-cliff on a pushed tag |\n\n**Gating topology.** `verify:*` and `test:*` jobs all carry `needs: []`\nand start together at t=0 -- peers, not a chain; a `verify` failure does\nnot hold `test` back. Every `hardware:*` job then `needs:` the full\nverify+test set, so nothing touches real hardware before the code is\nknown to compile and pass its own test suite -- a GPU, a metered D-Wave\nQPU token, and a macOS runner are all scarce and/or billed, and none of\nthem should be spent on code that does not even build. `docs:*` and\n`release:validate` jobs in turn `needs:` verify+test+hardware, and the\ntag-only publish chain (`release:crates` -> `release:pypi` ->\n`release:notes`) is gated transitively through `release:validate`'s\n`needs:` edge rather than through the stage barrier -- so the\ndocumentation site and the release path both wait on hardware being\nproven, not just on the workspace compiling. The accepted tradeoff: an\noffline GPU runner or an expired D-Wave token stalls the documentation\nsite, even though nothing in the docs content depends on hardware\npassing -- we would rather stall the docs than publish a book\ndescribing an opcode or solver behaviour the hardware suite just\nproved broken. On a tag the same gate now sits ahead of the publish\nchain too, so the same expired token stalls a release and not just the\ndocs -- `test:substrate`'s cold-cache build is the largest single wait\non that path. The exact graph and per-edge reasoning (including why\n`hardware:*` needs are marked `optional: true`) live in\n`.gitlab/ci/setup.yml`'s \"Dependency gating\" section.\n\n**Path gating.** Three jobs do not run on every pipeline. `test:wasm` and\n`test:substrate` are gated on `rules: changes:`, because each builds one\ncrate (`xqvm`) into one fixture and so has a narrow, writable input\nfootprint, while every other job in the pipeline is a whole-workspace\ncheck whose verdict a change anywhere can flip. They are the two most\nexpensive jobs in the pipeline and the least often relevant, so gating\nthem is most of the merge-request latency available to save.\n`verify:substrate-deny` runs cargo-deny over the pallet fixture's graph,\nwhich needs the same polkadot-sdk download, and is gated the same way.\nAll three stay unconditional on protected refs and on tags: the gate\nbuys latency, not coverage, and a path list is a claim about a build\ngraph that can be wrong -- keeping the protected refs unconditional\nmeans a wrong list costs a late signal on `main` rather than a shipped\nregression. Because any of them can be absent, every `needs:` edge into\nthem is `optional: true`; GitLab refuses to create a pipeline whose job\nneeds an absent job. The path lists, the per-clause reasoning, and the\nper-entry justification live in `.gitlab/ci/test.yml`'s \"Path gating\"\nsection and beside `verify:substrate-deny` in `.gitlab/ci/verify.yml`.\nLocal `make preflight-rs` runs all three targets unconditionally.\n\n**CI signals.** Two reds are expected, and each means a step of the release protocol is outstanding rather than that something is broken. Do not \"fix\" either by anything but the step it names:\n\n- **`dev` red from `check-branch-containment`** (in `verify:policy`): a release tag on `main` is not yet in `dev`. Every push pipeline on `dev` stays red until someone back-merges `main` into `dev`. Ordinary merges into `main` between releases do not trigger it. Merge requests into `dev` are not judged by it, so work continues in parallel.\n- **`main` red from `check-version-sites`** (in `release:validate`): `main` carries a release version. This follows every release merge until the direct push reopening `main` at the next patch's `-dev` version lands.\n\nA third is a gate, not a signal: a merge request from `release/*` fails `verify:policy` when the release branch does not contain `origin/main`. The fix is a back-merge of `main` into the release branch.\n\n**Naming.** A job's name prefix is its phase (`verify:rust` runs in the\n`verify` stage, `docs:build` in `docs`) -- that is the CI-side taxonomy.\nThe Makefile stays action-first (`<action>-<subject>`, e.g. `lint-rust`,\n`build-docs`) -- that is the local-workflow taxonomy -- so most job names\ndo not map onto their target mechanically: `verify:rust` runs `make -k\nlint-rust`, `docs:build` runs `make -k build-docs`. CI always invokes\nthese with `-k` so every prerequisite in a merged job is attempted even\nafter one fails; a plain local `make lint-rust` fails fast on the first,\nlike any other non-`-k` target.\n\nJobs are authored in per-stage files under `.gitlab/ci/` and composed via `include:` in the root `.gitlab-ci.yml`.\n\n### Changelog\n\n`CHANGELOG.md` is **not** committed to the source tree. The source of truth is `cliff.toml` plus the conventional-commit log; the file is regenerated on demand via `make changelog` and published as the GitLab Release description on tag (`release:notes` in `.gitlab/ci/release.yml`).\n\nCaveats:\n\n- Pre-conventional-commits history (everything before QUI-480) is filtered out by `filter_unconventional = true`; only commits on or after the QUI-480 enforcement appear in the rendered output. `make changelog` renders the whole unreleased history with no other scoping, so an empty render is the expected state until the first user-visible `feat`/`fix` lands.\n- `chore`, `style`, `test`, `ci`, `build` are **dropped silently** -- if a commit under one of those types ships a user-visible change (e.g. a security-relevant dep bump under `chore`), promote it to `feat`/`fix`/`security` before merging or it will be invisible in release notes.\n- `cliff.toml`'s `tag_pattern` scopes each release's notes to that release alone: it is three numeric fields and nothing else, so a prerelease tag (`-rcN`, `-betaN`) never becomes a range boundary and its commits fold into the following release's section instead of getting a page of their own. `changelog-release` (the Makefile target `release:notes` invokes) pairs this with an explicit `PREV..VERSION` range, where `PREV` is the nearest release predecessor tag (`git describe --exclude='*-*'`, the same pattern in glob form), rather than an unbounded `--tag`. `release:notes` and `docs:publish` match the same pattern, so a prerelease publishes to both registries with no Release page and no documentation. `make check-release-notes` (`scripts/check-release-notes.sh`, run as part of `lint-policy` / `verify:policy`) is the regression guard: it renders every release tag's range plus the pre-tag preview and asserts each yields exactly one `## [` heading. Mirror the pattern wherever a tag is classified; a denylist of prerelease spellings is what let `-beta` through before.\n- Before tagging a release, run `make changelog-release VERSION=vX.Y.Z` locally to preview what the GitLab Release page will say -- the render contains exactly one section. Bad commit subjects can be fixed on the source branch and re-merged before the tag is cut.\n\n## Local overrides\n\nPersonal, repository-specific configuration goes in `AGENTS.local.md` (gitignored). It is\nimported below and silently skipped for collaborators who do not have one.\n\n@AGENTS.local.md\n</yakaai_skills>"}