Initial import
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user