Files
quantum-bridge-mcp/docs/superpowers/plans/sub1-foundation.md
T
Vincent Bourdon 9af114e391 Initial import
2026-06-09 16:14:55 +02:00

7.6 KiB
Raw Blame History

Sub-plan 1 — Foundation (Tasks 13)

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 13.


Task 1: Bootstrap

Files:

  • Create: Cargo.toml

  • Create: src/main.rs (via cargo new)

  • Step 1: Initialise project

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.

[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
fn main() {
    println!("quantum-bridge-mcp");
}
  • Step 4: Verify dependencies compile
cargo build

Expected: success (first run ~200 MB). If a version constraint fails, run cargo search <crate> and adjust.

  • Step 5: Commit
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

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
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
mod error;
mod types;

fn main() {
    println!("quantum-bridge-mcp");
}
  • Step 4: Verify
cargo build

Expected: no warnings.

  • Step 5: Commit
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

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:

mod executor;
cargo test

Expected: 1 test passes (mock_backend_satisfies_trait_bounds).

  • Step 3: Commit
git add src/executor.rs src/main.rs
git commit -m "feat: define Backend trait hierarchy (CanIntrospect/CanValidate/CanExecute)"

Final verification

cargo fmt --check && cargo clippy -- -D warnings && cargo test

Expected: all green. Sub-plan 1 complete — hand off to sub-plan 2.