# quantum-bridge-mcp — Development Rules ## Project Pure Rust MCP quantum simulator. See `docs/superpowers/specs/2026-04-28-quantum-bridge-mcp-design.md` for the full spec. Stack: `rmcp` (MCP SDK) + `oq3_semantics` (QASM3 parser) + `spinoza` (statevector engine) + `tokio`. ## Bootstrap (greenfield project) `src/` and `Cargo.toml` do not exist yet. To initialize: ```bash cargo new --name quantum-bridge-mcp . # creates src/main.rs + Cargo.toml ``` Key dependencies to add to `Cargo.toml` (check current versions on crates.io): ```toml [dependencies] rmcp = { git = "https://github.com/modelcontextprotocol/rust-sdk" } oq3_semantics = "0.1" # official Qiskit OpenQASM3 parser in Rust spinoza = "2" # pure Rust statevector engine tokio = { version = "1", features = ["full"] } serde = { version = "1", features = ["derive"] } serde_json = "1" thiserror = "1" tracing = "0.1" tracing-subscriber = "0.3" [dev-dependencies] proptest = "1" criterion = { version = "0.5", features = ["html_reports"] } [[bench]] name = "simulation" harness = false ``` ## Architecture ``` src/ ├── main.rs # Entry point: MCP stdio server (rmcp), reads from stdin, writes to stdout ├── tools/ │ ├── mod.rs # MCP tool registration │ ├── list_backends.rs # list_backends handler │ ├── validate_circuit.rs # validate_circuit handler │ └── run_circuit.rs # run_circuit handler ├── validator.rs # oq3_semantics wrapper → structured diagnostics ├── executor.rs # OpenQASM 3 AST → Spinoza gates → counts └── error.rs # thiserror error types tests/ ├── integration/ # MCP JSON-RPC roundtrip, tool schema conformance ├── reference/ # QASM3 circuits + expected statevectors (generated by scripts/gen_reference.py) │ └── invalid/ # Invalid circuits + exact expected error messages (golden files) └── proptest/ # Property-based tests (unitarity, normalization) benches/ └── simulation.rs # Criterion benchmarks (Bell < 5 ms, 20 qubits < 500 ms) scripts/ └── gen_reference.py # Prerequisites: Python 3.11 + pip install qiskit qiskit-aer .github/workflows/ ├── ci.yml # Rust tests + Qiskit cross-validation └── release.yml # cargo-dist (precompiled binaries) ``` Central trait: `Backend` (executor.rs). `LocalSimulator` implements `Backend` in V1. MCP tools depend on `Backend`, never on `LocalSimulator` directly. --- ## 1. Test-Driven Development — absolute rule **Red → Green → Refactor. In that order. Always.** 1. Write the failing test first. 2. Write the minimum code to make it pass. 3. Refactor without breaking the tests. Never ship functional code without a test covering it. A feature without a test does not exist. ### Types of tests to write (see spec §8) - **Unit**: individual gates, isolated behaviors — inside the module (`#[cfg(test)]`) - **Statistical**: probabilistic counts with chi² test (N=10k shots, fixed seed) - **Qiskit cross-validation**: `tests/reference/` — QASM3 circuits + expected statevectors generated by `scripts/gen_reference.py` - **Property-based**: quantum invariants with `proptest` (unitarity, normalization) - **Golden files**: invalid circuits → exact error messages in `tests/reference/invalid/` - **MCP protocol**: JSON-RPC roundtrip, schema conformance - **Benchmarks**: Criterion in `benches/`, SLA Bell < 5 ms, 20 qubits < 500 ms ### Test rules - Tests must be deterministic. Use a fixed seed for any probabilistic simulation. - One test = one behavioral assertion. Do not test multiple things in a single test. - Test names describe behavior: `apply_h_to_zero_gives_equal_superposition`, not `test_h`. - Never comment out a failing test — fix it or remove the feature. --- ## 2. Clean Code - Expressive names: `parse_circuit` not `parse`, `num_qubits` not `nq`, `QubitIndex` not `usize`. - One function = one responsibility. If the name contains "and", it's two functions. - Max 3 parameters; beyond that: config struct. No boolean parameters: use an enum or two functions. - Comments = *why* only (hidden constraint, non-obvious invariant). Not "what". - No commented-out dead code. No `TODO`/`FIXME` committed to main. --- ## 3. SOLID Principles (project applications) - **SRP**: `validator.rs` = parse+validate only. `executor.rs` = translate AST+simulate only. Tools = orchestration only. - **OCP**: adding IBM (V1.5) does not touch `executor.rs` — `Backend` trait is mandatory. - **LSP**: every `impl Backend` respects the contract: `run()` never panics, returns `Result`. - **ISP**: separate traits `CanValidate` / `CanExecute` / `CanIntrospect`. No fat trait. - **DIP**: tools depend on `&dyn Backend`, not on `LocalSimulator`. Gates injected via `&dyn GateSet`. --- ## 4. Rust Architecture ### Error handling - **No `unwrap()` or `expect()` in production code.** Only in tests (if truly justified). - All errors propagate via `Result` with an explicit error type (not `Box`). - Use `thiserror` to define error types. Each variant has a clear message. - Errors exposed via MCP include: structured code, human-readable message, circuit position if applicable. ```rust // Good #[derive(thiserror::Error, Debug)] pub enum ValidationError { #[error("gate '{gate}' not supported (line {line})")] UnsupportedGate { gate: String, line: usize }, } // Forbidden fn parse(input: &str) -> bool { ... } // error swallowed ``` ### Modules and visibility - Everything is private by default. `pub` is intentional, not the default. - A module's public API = its contract. Minimize it. - No cascading `pub use` without intent. Re-export only what belongs to the public interface. ### Types and newtypes - Use newtypes for domain concepts: `QubitIndex(usize)`, `ShotCount(u32)` rather than bare `usize` everywhere. - Do not pass raw primitives where a domain type exists. - `Config` structs are separate from `State` structs. Do not mix configuration and mutable state. ### No magic numbers ```rust // Forbidden if qubits > 28 { ... } // Good const MAX_LOCAL_QUBITS: usize = 28; if qubits > MAX_LOCAL_QUBITS { ... } ``` --- ## 5. Refactoring Rules - Refactor **after** tests pass, never before. - A refactoring does not change observable behavior. If the tests change, it's no longer a refactoring. - The rule of three: first time, just code. Second time, code and grimace. Third time, abstract. - Do not abstract prematurely. Three similar lines are not necessarily an abstraction. --- ## 6. Git & Commits - **One commit = one coherent intent.** Do not mix feature + fix + refactoring in the same commit. - Commit messages in English, conventional format: `feat:`, `fix:`, `test:`, `refactor:`, `chore:`, `docs:`. - Tests and the production code they cover go in the same commit. - Never commit code that breaks tests (`cargo test` must pass). - Never commit code that does not compile. - Prefer readable atomic commits over a single large WIP commit. --- ## 7. Development Commands ```bash # Bootstrap (first setup) — verifies dependencies compile cargo build # Run the MCP server (stdio) locally cargo run # f32 mode: +30% speed, +1 qubit at equal RAM (negligible precision loss) cargo run -- --f32 # Claude Desktop config (~/.config/claude/claude_desktop_config.json) # { "mcpServers": { "quantum-bridge": { "command": "/path/to/quantum-bridge-mcp" } } } # Tests (all) cargo test # Tests + Qiskit cross-validation cargo test && python3 scripts/gen_reference.py --validate # Benchmarks cargo bench # Strict lint cargo clippy -- -D warnings # Format cargo fmt --check # Full check (before commit) cargo fmt --check && cargo clippy -- -D warnings && cargo test ``` CI rejects any code that fails any of these checks. --- ## 8. Quantum Gotchas - **Endianness / bit-ordering**: Qiskit and Spinoza may use opposite qubit-ordering conventions. Cross-validation (`tests/reference/`) is there to catch this. Never assume — verify against golden files. - **Probabilities vs counts**: `run_circuit` returns counts (integers), not probabilities. Normalize on the Claude side if needed. - **Fixed seed in tests**: any probabilistic simulation in tests uses a fixed seed. Without this, statistical tests are flaky. - **Qubit limit**: statevector = `2^n × 16 bytes`. 25 qubits = 512 MB. Validate `num_qubits ≤ MAX_LOCAL_QUBITS` before simulation. --- ## 9. Environment Environment variables: - `RUST_LOG=debug` — enables tracing logs during development - `IBM_QUANTUM_TOKEN` — IBM token (V1.5 only, not used in V1) Prerequisites for cross-validation (CI only): ```bash python3 -m pip install qiskit qiskit-aer # Python 3.11+ python3 scripts/gen_reference.py # generates tests/reference/ ``` --- ## 10. What Is Forbidden - `panic!()`, `unwrap()`, `expect()` in production code - `todo!()`, `unimplemented!()` committed to main - `unsafe` without a comment block justifying why and proving safety - Dependencies added without justification in the commit message - Disabled tests (`#[ignore]`) without a linked issue and resolution date - Dead code (`#[allow(dead_code)]`) without justification