//! The cross-process loop: an author exports a signed spec + ops into //! `.joule/`, the `joule-review` binary runs as an independent reviewer //! process, and the reviews it writes back satisfy (or fail) the gate. use std::path::PathBuf; use std::process::Command; use ed25519_dalek::SigningKey; use joule_code_cargo::{Anchor, CodingOp, Edit, SignedOp}; use joule_code_review::{approval, SignedReview}; use joule_code_schema::{ContentId, Tier}; use joule_code_spec::{Clause, ClauseKind, SignedSpec, Spec, ALG_ED25519 as SALG}; fn sk(s: u8) -> SigningKey { SigningKey::from_bytes(&[s; 32]) } fn op(author: &SigningKey, intent: &str, body: &str) -> SignedOp { CodingOp { alg: 0x01, prior: ContentId::of_bytes(body.as_bytes()), edits: vec![Edit { path: "src/store.rs".into(), anchor: Anchor::ByteRange { start: 0, end: 0 }, replacement: body.as_bytes().to_vec(), }], author: author.verifying_key().to_bytes(), intent: intent.into(), tier: Tier::L3, } .sign(author) .unwrap() } #[test] fn an_independent_reviewer_process_closes_the_loop_over_files() { let author = sk(1); let work = std::env::temp_dir().join(format!("joule-review-it-{}", std::process::id())); let joule = work.join(".joule"); std::fs::create_dir_all(&joule).unwrap(); // author exports the adopted spec + ops let spec: SignedSpec = Spec { alg: SALG, intent: "a persistent todo list".into(), clauses: vec![ Clause { id: "C1".into(), text: "todos persist".into(), kind: ClauseKind::Requirement }, Clause { id: "NG1".into(), text: "no multiplayer".into(), kind: ClauseKind::NonGoal }, ], parent: None, author: author.verifying_key().to_bytes(), } .sign(&author) .unwrap(); std::fs::write(joule.join("spec.adopted.json"), serde_json::to_string(&spec).unwrap()).unwrap(); let good = op(&author, "C1: persist todos", "fn save() { write_to_disk(); }"); let bad = op(&author, "C1: persist todos", "fn save() { todo!() }"); let scope = op(&author, "NG1: multiplayer", "fn mp() {}"); let ops = vec![good.clone(), bad.clone(), scope.clone()]; std::fs::write(joule.join("ops.json"), serde_json::to_string(&ops).unwrap()).unwrap(); // run the independent reviewer process let status = Command::new(env!("CARGO_BIN_EXE_joule-review")) .arg(&work) .status() .expect("joule-review runs"); assert!(status.success()); // ingest the reviews it wrote let text = std::fs::read_to_string(joule.join("reviews.json")).unwrap(); let reviews: Vec = serde_json::from_str(&text).unwrap(); // the reviewer is a DIFFERENT principal than the author → reviews count let for_op = |o: &SignedOp| -> Vec { let id = o.op.op_id().unwrap(); reviews.iter().filter(|r| r.review.op_id == id).cloned().collect() }; assert!(approval(&good, &for_op(&good)).is_approved(), "the finished op is approved"); assert!(!approval(&bad, &for_op(&bad)).is_approved(), "the todo!() op is sent back"); assert!(!approval(&scope, &for_op(&scope)).is_approved(), "the non-goal op is sent back"); let _ = std::fs::remove_dir_all(&work); let _: PathBuf = work; // silence unused on early-return paths } #[test] fn the_reviewer_skips_ops_it_authored_itself() { // give the binary a known persistent identity (its reviewer keyfile), then // author an op with THAT principal: self-review, skipped, no reviews. use joule_code_identity::{ActorKind, Identity}; let work = std::env::temp_dir().join(format!("joule-review-self-{}", std::process::id())); let joule = work.join(".joule"); std::fs::create_dir_all(&joule).unwrap(); // the reviewer principal the binary will load from .joule/reviewer.key let reviewer = Identity::generate("structural-reviewer", ActorKind::Agent); reviewer.save(&joule.join("reviewer.key")).unwrap(); let spec: SignedSpec = Spec { alg: SALG, intent: "x".into(), clauses: vec![Clause { id: "C1".into(), text: "a".into(), kind: ClauseKind::Requirement }], parent: None, author: sk(1).verifying_key().to_bytes(), } .sign(&sk(1)) .unwrap(); std::fs::write(joule.join("spec.adopted.json"), serde_json::to_string(&spec).unwrap()).unwrap(); let own = op(reviewer.signing_key(), "C1: x", "fn f() { ok(); }"); std::fs::write(joule.join("ops.json"), serde_json::to_string(&vec![own]).unwrap()).unwrap(); let status = Command::new(env!("CARGO_BIN_EXE_joule-review")) .arg(&work) .status() .unwrap(); assert!(status.success()); let reviews: Vec = serde_json::from_str(&std::fs::read_to_string(joule.join("reviews.json")).unwrap()).unwrap(); assert!(reviews.is_empty(), "self-authored op yields no reviews"); let _ = std::fs::remove_dir_all(&work); } /// The full amortization loop, through the real binary and a real subprocess /// model: run 1 escalates a clean-but-unsafe op to the model, which rejects it /// and teaches a skill that persists to `.joule/review-skills.json`; run 2, /// with NO model configured, catches a *recurrence* of the same defect at the /// learned tier — the model's judgment, paid once, applied for free. #[test] fn the_model_teaches_a_skill_that_persists_and_catches_recurrences_model_free() { use std::os::unix::fs::PermissionsExt; let author = sk(1); let work = std::env::temp_dir().join(format!("joule-review-learn-{}", std::process::id())); let joule = work.join(".joule"); std::fs::create_dir_all(&joule).unwrap(); let spec: SignedSpec = Spec { alg: SALG, intent: "x".into(), clauses: vec![Clause { id: "C1".into(), text: "run input".into(), kind: ClauseKind::Requirement }], parent: None, author: author.verifying_key().to_bytes(), } .sign(&author) .unwrap(); std::fs::write(joule.join("spec.adopted.json"), serde_json::to_string(&spec).unwrap()).unwrap(); // a subprocess "model" that rejects eval( and names it as the inducible cause let model = work.join("model.sh"); std::fs::write( &model, "#!/bin/sh\ncat >/dev/null\nprintf 'VERDICT: REQUEST_CHANGES\\nFINDINGS: eval on untrusted input\\nINDUCE: eval(\\n'\n", ) .unwrap(); std::fs::set_permissions(&model, std::fs::Permissions::from_mode(0o755)).unwrap(); // run 1: a clean-but-unsafe op (passes structural, escalates to the model) let unsafe1 = op(&author, "C1: run", "fn run() { eval(input); }"); std::fs::write(joule.join("ops.json"), serde_json::to_string(&vec![unsafe1.clone()]).unwrap()).unwrap(); let status = Command::new(env!("CARGO_BIN_EXE_joule-review")) .arg(&work) .env("JOULE_REVIEW_MODEL_CMD", &model) .status() .unwrap(); assert!(status.success()); let r1: Vec = serde_json::from_str(&std::fs::read_to_string(joule.join("reviews.json")).unwrap()).unwrap(); assert!(!approval(&unsafe1, &r1).is_approved()); // the model rejected it // the skill was learned and persisted let skills = std::fs::read_to_string(joule.join("review-skills.json")).unwrap(); assert!(skills.contains("eval("), "the eval( skill persisted: {skills}"); // run 2: a DIFFERENT op with the same defect, NO model configured at all let unsafe2 = op(&author, "C1: run", "fn other() { eval(x); }"); std::fs::write(joule.join("ops.json"), serde_json::to_string(&vec![unsafe2.clone()]).unwrap()).unwrap(); let status = Command::new(env!("CARGO_BIN_EXE_joule-review")) .arg(&work) // no JOULE_REVIEW_MODEL_CMD .status() .unwrap(); assert!(status.success()); let r2: Vec = serde_json::from_str(&std::fs::read_to_string(joule.join("reviews.json")).unwrap()).unwrap(); assert!(!approval(&unsafe2, &r2).is_approved()); // caught by the LEARNED tier, no model let _ = std::fs::remove_dir_all(&work); }