Initial import

This commit is contained in:
Vincent Bourdon
2026-06-09 16:14:55 +02:00
commit 9af114e391
87 changed files with 20848 additions and 0 deletions
+255
View File
@@ -0,0 +1,255 @@
# 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<T, E>` with an explicit error type (not `Box<dyn Error>`).
- 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