run takes --intent (a reference program, a distribution, intents, or a project's intent.json), --verdict and --repair, prints the verdict and exits by whether the intent holds. repair makes the mechanical fixes on the flagged lines and checks a proposal against them. test runs the program with its intents, so the verdict is sealed in the run's receipt. The MCP server's run_quantum_circuit takes intent and repair and always states its verdict, repair_qasm is new, every answer about a program carries its verdict, every refusal its codes, and every tool a versioned output schema. The committed agent docs and the Agent Skill are regenerated. Co-Authored-By: Claude Opus 5.5 <[email protected]>
122 lines
5.1 KiB
Rust
122 lines
5.1 KiB
Rust
//! `wai-quantum repair`: a program repaired from its diagnostics, on the
|
|
//! lines they flag alone ([`wai_quantum::engine::repair`]). The repaired
|
|
//! program goes to standard output (or back to its file with `--write`), and
|
|
//! each repair and what needs a person to standard error; `--json` prints the
|
|
//! engine's whole answer, its verdict included.
|
|
//!
|
|
//! `--proposed FILE` checks a repair someone else wrote (a person's, an
|
|
//! agent's): it is taken only if it changes no line the diagnostics do not
|
|
//! flag. Exit 0 when the program reads, 1 when something still needs a
|
|
//! person, 2 when a proposal is rejected.
|
|
|
|
use serde_json::{Value, json};
|
|
use std::path::PathBuf;
|
|
use wai_quantum::engine::repair;
|
|
|
|
#[derive(clap::Args)]
|
|
pub struct Args {
|
|
/// The program; `-` or nothing reads standard input
|
|
pub file: Option<PathBuf>,
|
|
/// A repaired version to check: taken only if it changes no line but those the diagnostics flag
|
|
#[arg(long, value_name = "FILE")]
|
|
pub proposed: Option<PathBuf>,
|
|
/// The bound for an unbounded `while` on measured bits (Q310): it runs at most this many times
|
|
#[arg(long, value_name = "N")]
|
|
pub max_iterations: Option<u64>,
|
|
/// Round each angle the bit-exact method refuses (Q500) to the nearest multiple of 2π/2^K, its rounding stated on its line
|
|
#[arg(long, value_name = "K")]
|
|
pub dyadic_bits: Option<u32>,
|
|
/// A register's size, for one used before it is declared (Q102), as q=3 (repeatable)
|
|
#[arg(long = "size", value_name = "NAME=N", value_parser = size_arg)]
|
|
pub sizes: Vec<(String, u32)>,
|
|
/// Only check the proposal: make none of the engine's own fixes
|
|
#[arg(long)]
|
|
pub no_rules: bool,
|
|
/// Write the repaired program back to its file
|
|
#[arg(long, conflicts_with = "json")]
|
|
pub write: bool,
|
|
/// Print the engine's answer as JSON: the program, each repair, what needs a person, and the verdict
|
|
#[arg(long)]
|
|
pub json: bool,
|
|
}
|
|
|
|
fn size_arg(s: &str) -> Result<(String, u32), String> {
|
|
let (k, v) = s.split_once('=').ok_or("give a register and its size, as q=3")?;
|
|
let n: u32 = v.trim().parse().map_err(|_| format!("{v}: a size is a whole number"))?;
|
|
Ok((k.trim().to_string(), n))
|
|
}
|
|
|
|
/// The repair's choices as the engine takes them.
|
|
pub fn options(a: &Args, proposed: Option<String>) -> Value {
|
|
let mut o = json!({});
|
|
if let Some(n) = a.max_iterations {
|
|
o["max_iterations"] = json!(n);
|
|
}
|
|
if let Some(k) = a.dyadic_bits {
|
|
o["dyadic_bits"] = json!(k);
|
|
}
|
|
if !a.sizes.is_empty() {
|
|
o["sizes"] = Value::Object(a.sizes.iter().map(|(k, n)| (k.clone(), json!(n))).collect());
|
|
}
|
|
if let Some(p) = proposed {
|
|
o["proposed"] = json!(p);
|
|
}
|
|
if a.no_rules {
|
|
o["rules"] = json!(false);
|
|
}
|
|
o
|
|
}
|
|
|
|
/// What was repaired and what needs a person, as a reader takes it in.
|
|
pub fn report(v: &Value) -> String {
|
|
let mut s = String::new();
|
|
for r in v["repairs"].as_array().into_iter().flatten() {
|
|
let code = r["code"].as_str().unwrap_or("—");
|
|
s += &format!("repaired {code}: {}\n", r["rule"].as_str().unwrap_or(""));
|
|
for e in r["edits"].as_array().into_iter().flatten() {
|
|
let line = e["line"].as_u64().unwrap_or(0);
|
|
match (e["before"].as_str(), e["after"].as_str()) {
|
|
(Some(b), Some(a)) => s += &format!(" line {line}: {b}\n → {a}\n"),
|
|
(None, Some(a)) => s += &format!(" added before line {line}: {a}\n"),
|
|
(Some(b), None) => s += &format!(" removed line {line}: {b}\n"),
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
for p in v["needs_person"].as_array().into_iter().flatten() {
|
|
s += &format!("needs a person {}: {}\n {}\n", p["code"].as_str().unwrap_or(""), p["message"].as_str().unwrap_or(""), p["advice"].as_str().unwrap_or(""));
|
|
}
|
|
s += &format!("verdict {}: {}\n", v["verdict"]["level"].as_str().unwrap_or(""), v["verdict"]["statement"].as_str().unwrap_or(""));
|
|
s
|
|
}
|
|
|
|
/// `wai-quantum repair`.
|
|
pub fn main(a: Args) {
|
|
let src = crate::read_text(a.file.as_deref()).unwrap_or_else(|e| crate::die(&e));
|
|
let proposed = a.proposed.as_deref().map(|p| crate::read_text(Some(p)).unwrap_or_else(|e| crate::die(&e)));
|
|
let v = match repair::repair(&src, &options(&a, proposed)) {
|
|
Ok(v) => v,
|
|
Err(e) => {
|
|
if a.json {
|
|
println!("{}", json!({ "error": e, "refusal": wai_quantum::engine::refusal::refusal_json(&e) }));
|
|
} else {
|
|
eprintln!("wai-quantum: {e}");
|
|
}
|
|
std::process::exit(2);
|
|
}
|
|
};
|
|
let text = v["qasm"].as_str().unwrap_or("");
|
|
if a.json {
|
|
println!("{v}");
|
|
} else if a.write {
|
|
let Some(path) = a.file.as_deref().filter(|p| *p != std::path::Path::new("-")) else { crate::die("--write needs the program's file") };
|
|
if v["changed"] == true {
|
|
std::fs::write(path, text).unwrap_or_else(|e| crate::die(&format!("{}: {e}", path.display())));
|
|
}
|
|
eprint!("{}", report(&v));
|
|
} else {
|
|
print!("{text}");
|
|
eprint!("{}", report(&v));
|
|
}
|
|
std::process::exit(if v["ok"] == true { 0 } else { 1 });
|
|
}
|