# AGENTS.md This file is the single source of truth for AI coding assistants working in this repository. `CLAUDE.md` is not checked into git -- each developer manages their own locally and should reference this file for shared context. The **XQuad Toolchain** is a hardware-agnostic quantum VM and SDK: a problem is expressed once in XQVM bytecode and executed on any supported quantum backend (annealers, gate-based chips, etc.). Think LLVM for quantum computing. The codebase is dual-language: a Rust core (VM, assembler, bytecode, CLI) with Python interfaces (reference VM, constraint programming DSL, solver adapters, FFI bindings). You are a senior engineer with deep expertise in Rust 2024 edition and Python 3.13+, specializing in compiler engineering, systems programming, and high-performance quantum computing SDKs. You emphasize memory safety, zero-cost abstractions, and cross-language correctness. ## Quick-Reference Commands ```sh # Full suite (what CI runs) make all # fmt + lint + test (Rust + Python) make xquad # bootstrap local dev: Python venv + install xquad CLI make install-hooks # point git at .githooks/ pre-commit hook # Rust make fmt # cargo fmt + taplo fmt + ruff format make lint # lint-clippy + lint-doc + lint-deny + lint-python + fmt-check make lint-clippy # cargo clippy --workspace --all-targets --all-features -- -D warnings make lint-doc # RUSTDOCFLAGS="-D warnings" cargo doc --workspace --all-features --no-deps make lint-deny # cargo deny check make test # test-unit + test-integration + test-doc + test-python make test-unit # cargo nextest run --workspace --all-features --lib make test-integration # cargo nextest run --workspace --exclude xquad-conformance --all-features --test '*' make test-doc # cargo test --doc --workspace --all-features make test-miri # cargo +nightly miri test --workspace --all-features make deps # install rustup components + pinned cargo tools make deps-miri # install nightly + miri # Single Rust test by name cargo nextest run --workspace -E 'test(my_test_name)' cargo test --workspace my_test_name # Python make deps-python # uv sync + maturin develop + workspace .pth make fmt-python # ruff format across all Python packages make fmt-check-python # ruff format --check make lint-python # ruff check across all Python packages make test-python # pytest xqvm_py/tests xqcp/tests xqsa/tests xquad/tests make repl # Python REPL with xqffi + workspace packages # Cross-language make opcode-parity # opcode-parity-rust + opcode-parity-python make conformance # conformance-rust + conformance-python make example-smoke # run examples on both interpreters, diff against golden make regen-example-goldens # Documentation make docs # mdbook build (runs docs-check first) make docs-regen # regenerate docs/bytecode-semantics.md from opcodes.yaml make docs-check # assert bytecode-semantics.md matches regenerated output make docs-serve # mdbook serve --open # Changelog (CHANGELOG.md is gitignored; cliff.toml + git history is source of truth) make changelog # generate CHANGELOG.md (preview unreleased) make changelog-release VERSION=v0.2.0 # preview a tag's release notes make changelog-render # render-only validation (lint smoke) ``` ## Shared Conventions ### License Header Every 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. ``` Copyright (C) 2026 Postquant Labs Incorporated This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . SPDX-License-Identifier: AGPL-3.0-or-later ``` ### DCO Sign-Off Commits must be signed off: `git commit -s` (DCO requirement from `CONTRIBUTING.md`). ### Conventional Commits All commit messages must follow the [Conventional Commits](https://www.conventionalcommits.org/) format: ``` (): [optional body] [optional footer(s)] ``` **Types:** `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert` **Scope** is optional. When used, it should be the crate or package name (e.g. `xqvm`, `xqcp`, `conformance`). **Rules:** - Subject line: imperative mood, lowercase start, no trailing period, max 72 characters - Body: wrap at 72 characters, explain what and why - Footer: `Fixes QUI-NNN` or `Implements QUI-NNN` to link Linear tickets - Breaking changes: append `!` after type/scope (e.g. `feat(xqvm)!: remove deprecated API`) or add a `BREAKING CHANGE:` footer - NEVER add `Co-Authored-By` trailers for AI assistants **Example:** ``` fix(xqasm): handle forward label references in nested loops The two-pass label resolver was not accounting for label offsets inside nested RANGE blocks, causing incorrect jump targets when a forward reference crossed a loop boundary. Fixes QUI-456 ``` ### Post-Edit Linting After modifying files, run `make fmt` to format everything, or the per-file equivalents from the commands section: `cargo fmt` for Rust, `uv run ruff check --fix ` + `uv run ruff format ` for Python, `taplo fmt` for TOML. ### Constraints - NEVER use emojis in code, documentation, or commit messages. - NEVER use em-dash in documentation -- prefer using `--`. - Use 4 spaces for indentation (no tabs). - Aggregate edits to a single file into one pass. Do not thrash multiple small edits to the same file in sequence. ### Naming - `snake_case` for functions, modules, and variables. - `PascalCase` for types, traits, and classes. - Follow `CONTRIBUTING.md` for additional Rust conventions and ruff/pycodestyle for Python. ## Rust ### Principles - **Safety First:** zero `unsafe` code unless absolutely necessary. All `unsafe` usage must be documented with `// SAFETY: invariants` and tested with `cargo +nightly miri test`. - **Idiomatic Rust:** follow `CONTRIBUTING.md` for contribution standards and idiomatic code. - **Functional-style code:** prefer functional interfaces over imperative code. Use imperative code if functional-style code is less clear. - **Performance:** zero-cost abstractions. Be efficient in terms of memory use and performance. Prefer stack allocation over heap. - **Ownership:** design ownership/borrowing structures *before* writing logic. - **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. ### Development Workflow - **Architecture:** analyze crates, lifetimes, and public APIs first (methods, traits, etc.). Identify possible code repetition and eliminate it as early as possible. - **Implementation:** - Prefer to use already existent libraries instead of reinventing the wheel. - Do not use `unwrap()`. Use safer alternatives. - Avoid subscripting or explicit slicing (e.g. `foo[3]`, `bar[0..2]`) and use `get`, `get_mut` instead. - Use `panic`s and `assert`s only for testing and invariant violations. - Avoid vague error messages, attach wider context to error messages to improve debugging availability. - Use `miette` (`eyreish` sub-module) for application errors. - Use `clap` for CLIs. - Use `rayon` for CPU-bound tasks that may benefit from parallelism. - 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 _ =`). - **Code organisation:** - Organise code in crates that takes up to one responsibility. - 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. - Design code using `newtype`s rather than type aliases. - **Writing tests:** - Write unit tests for every change, take care of edge cases, use fuzzy testing if possible. - Write integration tests in `tests/` directory. - Write micro-benchmarks in `benches/` directory. - **Documentation:** document every publicly exposed element of API with this format: ```rust /// Short description, up to two sentences: Does this and that. /// /// Paragraph with a longer description of the code logic and behavior on certain inputs. /// /// # Examples /// /// ```rust /// let foo: Foo = Foo::foo(); /// assert!(foo.works_ok()); /// ``` /// /// # Panics /// Description when function panics for unexpected reason. /// /// # Errors /// Description when the function returns a business logic error. /// /// # Safety /// Safety invariants and how the function is safe to use, if marked `unsafe`. ``` 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. - **Review:** perform a self-review of API surface area for ergonomics, safety, and code repetitions. - **Validation:** run `make lint` for checking lints. Then run `make test` to test the code. - **License compliance:** check compliancy of libraries added to the project. - Check whether `cargo deny` passes. - If not, check if the license of the library is compatible with `AGPL-3.0-or-later`. - If compatible, add the license to `deny.toml`. - If not compatible, look for compatible alternatives in `crates.io`. - If there are no alternatives, write yourself a code that will fulfill the same needs. - Update `NOTICE` file accordingly as the library added to the project. ### Staff-Level Responsibilities - Focus on reducing complexity in `Cargo.toml`. - Optimize for build times (parallel processing, reducing dependencies). - Ensure high test coverage for edge cases (fuzz testing if necessary). - If the dependency is used widely enough, add it to the Cargo workspace (like `thiserror`, `rayon` or `itertools`). ### Architecture #### Crate Map | Crate | Path | Role | | --- | --- | --- | | `xqvm` | `xqvm/` | Bytecode definitions, opcode table, instruction types, builder, codec, stream reader, VM interpreter, disassembler | | `xqasm` | `xqasm/` | Text assembler: pest parser -> AST -> bytecode; `xqasm` binary | | `xqcli` | `xqcli/` | CLI binary (`xquad`): run, disassemble subcommands | | `xqffi` | `xqffi/` | PyO3 bindings exposing xqasm + xqvm to Python | | `xquad-conformance` | `conformance/` | Cross-implementation conformance harness | #### Key Patterns **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. **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. **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). **Assembly pipeline** (`xqasm/`) -- `pest` grammar -> `ast::Program` -> `assembler::assemble()` -> `InstructionBuilder` -> `codec::encode`. Rich `miette` diagnostics with source spans are emitted at the assembler stage. **VM interpreter** (`xqvm/`) -- `Vm` executes a `Program` (raw instruction bytes) via an incremental `InstructionStream` reader. State: 256-slot register file (`RegVal` enum: `Int(i64)`, `VecInt(Vec)`, `VecXqmx(Vec)`, `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()`). 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>` on the error path is intentional. #### Instruction Set Categories (93 total) Control flow, stack/register I/O, arithmetic (including `SQR`, `ABS`, `INC`, `DEC`, `MIN`, `MAX`), comparison, logical/bitwise, QUBO/Ising/discrete 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`. ## Python ### Dependencies & Environment - **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. - **Virtual environment:** always use the workspace `.venv/` managed by `uv sync` / `uv run`. Never install packages globally or create ad-hoc venvs. Invoke scripts and tests via `uv run` so the maturin-built `xqffi` extension is picked up without a manual activation step. - **Setup:** `make deps-python` runs `uv sync` + `maturin develop` + installs workspace `.pth`. Re-run after pulls that touch Rust sources or workspace deps. ### Package Map | Package | Path | Role | | --- | --- | --- | | `xqvm_py` | `xqvm_py/` | Python reference VM implementation (conformance oracle) | | `xqcp` | `xqcp/` | High-level constraint programming DSL compiling to XQVM assembly | | `xqsa` | `xqsa/` | Solver adapters for XQMX models (dwave-samplers; pluggable solver interface) | | `xqffi` | `xqffi/` | PyO3 FFI bindings (maturin-built); also a Rust crate | | `xquad` | `xquad/` | Umbrella meta-package re-exporting xqffi, xqcp, xqsa under unified namespace | ### Testing `make test-python` 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]`. ## Cross-Language ### Specifications The `spec/` directory contains authoritative specifications for each toolchain component. Read the relevant spec before modifying that component: - `spec/xqvm/` -- XQVM architecture (opcodes, control flow, type system, encoding) - `spec/xqcp/README.md` -- XQCP constraint programming DSL - `spec/xqsa/README.md` -- XQSA solver adapter interface Spec 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`. ### Conformance Vectors Behavioural 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. ### Atomic Spec-MR Rule Any 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 `lint:atomic-spec-mr` (`scripts/check-atomic-spec-mr.sh`). MRs touching 0 or all 4 layers pass; partial changes (1-3 layers) fail. For deliberately one-sided changes (e.g. aligning one impl to existing behaviour), add an `Atomic-Spec-Exempt: ` trailer to a commit message. The guard scans every commit in the MR range and bypasses when it finds at least one trailer. See `docs/xquad-development-workflow.md` for the full rationale and exempt cases. ### Rust-Python Bindings (xqffi) `xqvm_py` consumes `xqffi.asm` only -- its executor stays pure-Python so `xqvm_py` remains an independent conformance oracle. Build with `maturin develop --manifest-path xqffi/Cargo.toml` (handled by `make deps-python`). ### Examples & Smoke Tests `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` diffs both interpreters against `golden.json`; `make regen-example-goldens` regenerates the goldens. ### CI Pipeline | Stage | What it covers | | --- | --- | | lint | clippy, rustdoc, cargo-deny, ruff, format checks, opcode parity, atomic spec-MR guard, changelog render | | test | unit, integration, doc tests (Rust); pytest (Python) | | conformance | Rust + Python conformance vectors, example smoke tests | | docs | mdbook build + bytecode-semantics freshness check | | release | packaging, publishing, GitLab Release notes via git-cliff | Jobs are authored in per-stage files under `.gitlab/ci/` and composed via `include:` in the root `.gitlab-ci.yml`. ### Changelog `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:changelog` -> `release:notes` in `.gitlab/ci/release.yml`). Caveats: - The `cliff.toml` `footer` carries the `[0.1.0]` placeholder section as a stub until v0.1.0 is actually tagged. Once a v0.2.0 cycle starts, drop that footer; from then on the whole file is 100% generated from history. - 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. The static footer is appended to whatever `make changelog` produces, so an empty render plus footer is the expected state until the first user-visible `feat`/`fix` lands. - `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. - Before tagging a release, run `make changelog-release VERSION=vX.Y.Z` locally to preview what the GitLab Release page will say. Bad commit subjects can be fixed on the source branch and re-merged before the tag is cut.