309 lines
7.6 KiB
Markdown
309 lines
7.6 KiB
Markdown
# Sub-plan 1 — Foundation (Tasks 1–3)
|
||
|
||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development or superpowers:executing-plans.
|
||
|
||
**Goal:** Cargo bootstrap + error types + domain newtypes + Backend trait hierarchy. Produces a compiling skeleton with all dependencies resolved.
|
||
|
||
**Starting state:** Greenfield — only `CLAUDE.md`, `.gitignore`, and `docs/` exist. No `src/`, no `Cargo.toml`.
|
||
|
||
**Deliverable:** `cargo test` exits 0. Three commits in git history.
|
||
|
||
**Tech Stack:** Rust 2021, `rmcp` (git), `oq3_semantics 0.7`, `spinoza 0.5`, `tokio`, `serde`, `thiserror 2`, `tracing`.
|
||
|
||
**Main plan reference:** `docs/superpowers/plans/2026-04-28-quantum-bridge-mcp-v1.md` Tasks 1–3.
|
||
|
||
---
|
||
|
||
## Task 1: Bootstrap
|
||
|
||
**Files:**
|
||
- Create: `Cargo.toml`
|
||
- Create: `src/main.rs` (via `cargo new`)
|
||
|
||
- [ ] **Step 1: Initialise project**
|
||
|
||
```bash
|
||
cd /home/vincent/src/misc/quantum-bridge-mcp
|
||
cargo new --name quantum-bridge-mcp .
|
||
```
|
||
|
||
Expected: `src/main.rs` and `Cargo.toml` created.
|
||
|
||
- [ ] **Step 2: Write Cargo.toml**
|
||
|
||
> Run `cargo search spinoza` and `cargo search oq3_semantics` to confirm latest patch versions before pinning.
|
||
|
||
```toml
|
||
[package]
|
||
name = "quantum-bridge-mcp"
|
||
version = "0.1.0"
|
||
edition = "2021"
|
||
description = "Zero-dependency MCP server for local OpenQASM 3.0 quantum circuit simulation"
|
||
license = "MIT"
|
||
|
||
[dependencies]
|
||
rmcp = { git = "https://github.com/modelcontextprotocol/rust-sdk", features = ["server", "transport-io", "macros"] }
|
||
oq3_semantics = "0.7"
|
||
spinoza = "0.5"
|
||
tokio = { version = "1", features = ["full"] }
|
||
serde = { version = "1", features = ["derive"] }
|
||
serde_json = "1"
|
||
schemars = "0.8"
|
||
thiserror = "2"
|
||
tracing = "0.1"
|
||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||
anyhow = "1"
|
||
|
||
[dev-dependencies]
|
||
proptest = "1"
|
||
criterion = { version = "0.5", features = ["html_reports"] }
|
||
|
||
[[bench]]
|
||
name = "simulation"
|
||
harness = false
|
||
```
|
||
|
||
- [ ] **Step 3: Write minimal src/main.rs**
|
||
|
||
```rust
|
||
fn main() {
|
||
println!("quantum-bridge-mcp");
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 4: Verify dependencies compile**
|
||
|
||
```bash
|
||
cargo build
|
||
```
|
||
|
||
Expected: success (first run ~200 MB). If a version constraint fails, run `cargo search <crate>` and adjust.
|
||
|
||
- [ ] **Step 5: Commit**
|
||
|
||
```bash
|
||
git add Cargo.toml Cargo.lock src/main.rs
|
||
git commit -m "chore: bootstrap project with all V1 dependencies"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 2: Foundation — Error types and domain newtypes
|
||
|
||
**Files:**
|
||
- Create: `src/error.rs`
|
||
- Create: `src/types.rs`
|
||
- Modify: `src/main.rs`
|
||
|
||
- [ ] **Step 1: Write src/error.rs**
|
||
|
||
```rust
|
||
use thiserror::Error;
|
||
|
||
#[derive(Debug, Error)]
|
||
pub enum BridgeError {
|
||
#[error("parse error at line {line}, col {col}: {message}")]
|
||
Parse { line: usize, col: usize, message: String },
|
||
|
||
#[error("gate '{gate}' is not supported at line {line} — supported: {supported}")]
|
||
UnsupportedGate { gate: String, line: usize, supported: String },
|
||
|
||
#[error("qubit index {index} is out of range (circuit declares {declared} qubits)")]
|
||
QubitOutOfRange { index: usize, declared: usize },
|
||
|
||
#[error("circuit requires {requested} qubits, exceeds the local simulator limit of {limit}")]
|
||
QubitLimitExceeded { requested: usize, limit: usize },
|
||
|
||
#[error("simulation failed: {0}")]
|
||
Simulation(String),
|
||
|
||
#[error("measure at line {line} maps to an undeclared classical bit")]
|
||
MeasurementMapping { line: usize },
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: Write src/types.rs**
|
||
|
||
```rust
|
||
use std::collections::HashMap;
|
||
|
||
/// Newtype for qubit indices — prevents mixing with plain usizes.
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||
pub struct QubitIndex(pub usize);
|
||
|
||
/// Newtype for shot count — enforces range and intent at type level.
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||
pub struct ShotCount(pub u32);
|
||
|
||
impl ShotCount {
|
||
pub const DEFAULT: ShotCount = ShotCount(1024);
|
||
pub const MAX: ShotCount = ShotCount(100_000);
|
||
}
|
||
|
||
/// Wraps an OpenQASM 3.0 source string.
|
||
#[derive(Debug, Clone)]
|
||
pub struct CircuitSource(pub String);
|
||
|
||
#[derive(Debug, Clone)]
|
||
pub enum DiagnosticSeverity {
|
||
Error,
|
||
Warning,
|
||
}
|
||
|
||
#[derive(Debug, Clone)]
|
||
pub struct ValidationDiagnostic {
|
||
pub line: usize,
|
||
pub column: usize,
|
||
pub message: String,
|
||
pub severity: DiagnosticSeverity,
|
||
}
|
||
|
||
#[derive(Debug, Clone)]
|
||
pub struct ValidationResult {
|
||
pub is_valid: bool,
|
||
pub diagnostics: Vec<ValidationDiagnostic>,
|
||
pub num_qubits: Option<usize>,
|
||
pub num_gates: Option<usize>,
|
||
}
|
||
|
||
#[derive(Debug, Clone)]
|
||
pub struct SimulationResult {
|
||
/// Bitstring → count, e.g. `{"00": 512, "11": 512}`.
|
||
pub counts: HashMap<String, u64>,
|
||
pub shots: u32,
|
||
pub execution_time_ms: f64,
|
||
/// Optional full statevector as (real, imag) pairs per basis state.
|
||
pub statevector: Option<Vec<(f64, f64)>>,
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 3: Update src/main.rs**
|
||
|
||
```rust
|
||
mod error;
|
||
mod types;
|
||
|
||
fn main() {
|
||
println!("quantum-bridge-mcp");
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 4: Verify**
|
||
|
||
```bash
|
||
cargo build
|
||
```
|
||
|
||
Expected: no warnings.
|
||
|
||
- [ ] **Step 5: Commit**
|
||
|
||
```bash
|
||
git add src/error.rs src/types.rs src/main.rs
|
||
git commit -m "feat: add error types and domain newtypes"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 3: Backend Traits
|
||
|
||
**Files:**
|
||
- Create: `src/executor.rs` (traits + constants only — `LocalSimulator` comes in sub-plan 3)
|
||
|
||
- [ ] **Step 1: Write src/executor.rs**
|
||
|
||
```rust
|
||
use crate::error::BridgeError;
|
||
use crate::types::{CircuitSource, ShotCount, SimulationResult, ValidationResult};
|
||
|
||
pub const MAX_LOCAL_QUBITS: usize = 28;
|
||
|
||
pub const SUPPORTED_GATES: &[&str] = &[
|
||
"h", "x", "y", "z", "s", "t", "sdg", "tdg",
|
||
"rx", "ry", "rz", "cx", "cz", "swap", "ccx",
|
||
"measure",
|
||
];
|
||
|
||
pub trait CanIntrospect {
|
||
fn name(&self) -> &str;
|
||
fn max_qubits(&self) -> usize;
|
||
fn supported_gates(&self) -> &[&str];
|
||
}
|
||
|
||
pub trait CanValidate {
|
||
fn validate(&self, circuit: &CircuitSource) -> Result<ValidationResult, BridgeError>;
|
||
}
|
||
|
||
pub trait CanExecute {
|
||
fn run(
|
||
&self,
|
||
circuit: &CircuitSource,
|
||
shots: ShotCount,
|
||
return_statevector: bool,
|
||
) -> Result<SimulationResult, BridgeError>;
|
||
}
|
||
|
||
/// Marker trait combining all three capabilities. V1.5 IBM backend will impl this too.
|
||
pub trait Backend: CanIntrospect + CanValidate + CanExecute + Send + Sync {}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
struct MockBackend;
|
||
impl CanIntrospect for MockBackend {
|
||
fn name(&self) -> &str { "mock" }
|
||
fn max_qubits(&self) -> usize { 4 }
|
||
fn supported_gates(&self) -> &[&str] { SUPPORTED_GATES }
|
||
}
|
||
impl CanValidate for MockBackend {
|
||
fn validate(&self, _: &CircuitSource) -> Result<ValidationResult, BridgeError> {
|
||
Ok(ValidationResult { is_valid: true, diagnostics: vec![], num_qubits: None, num_gates: None })
|
||
}
|
||
}
|
||
impl CanExecute for MockBackend {
|
||
fn run(&self, _: &CircuitSource, _: ShotCount, _: bool) -> Result<SimulationResult, BridgeError> {
|
||
Ok(SimulationResult { counts: Default::default(), shots: 0, execution_time_ms: 0.0, statevector: None })
|
||
}
|
||
}
|
||
impl Backend for MockBackend {}
|
||
|
||
#[test]
|
||
fn mock_backend_satisfies_trait_bounds() {
|
||
let b = MockBackend;
|
||
assert_eq!(b.name(), "mock");
|
||
assert_eq!(b.max_qubits(), 4);
|
||
}
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: Declare module and run tests**
|
||
|
||
Add to `src/main.rs`:
|
||
```rust
|
||
mod executor;
|
||
```
|
||
|
||
```bash
|
||
cargo test
|
||
```
|
||
|
||
Expected: 1 test passes (`mock_backend_satisfies_trait_bounds`).
|
||
|
||
- [ ] **Step 3: Commit**
|
||
|
||
```bash
|
||
git add src/executor.rs src/main.rs
|
||
git commit -m "feat: define Backend trait hierarchy (CanIntrospect/CanValidate/CanExecute)"
|
||
```
|
||
|
||
---
|
||
|
||
## Final verification
|
||
|
||
```bash
|
||
cargo fmt --check && cargo clippy -- -D warnings && cargo test
|
||
```
|
||
|
||
Expected: all green. Sub-plan 1 complete — hand off to sub-plan 2.
|