open-standards/jouleclaw/jouleclaw-rs/crates/jouleclaw-oracle/tests/conformance.rs
Transaction Science 0055f59b3c jouleclaw: conformance vectors for the triangle's four primitives
JCR-1 ships ten conformance vectors with three independent verifier
implementations byte-equal. The triangle primitives (conservation,
cone, oracle, elicitor) had no equivalent — implementers in other
languages had to read the Rust source to know what "compliant" means.

This commit closes that gap. Each primitive gets:

  conformance/vectors.json — the canonical fixture set
  conformance/README.md    — the conformance contract spec
  tests/conformance.rs     — Rust verifier wired into cargo test

Vectors:

  conservation/1  — 5 vectors: conforming, below_spec, hallucination,
                    missing_delta, plus a multi-finding case
  cone/1          — 6 vectors: every datum_default band entry,
                    jouleclaw_cascade_default L0, escalate-lut-to-
                    formulaic, escalate-from-apex (refused)
  oracle/1        — 5 vectors: always_pass, always_fail, composite
                    all-pass with chain dedup, composite short-
                    circuit on first fail, empty-composite trivial-
                    pass
  elicitor/1      — 5 vectors: empty-walker-done, single-required-
                    ask, two-required-fill-first-ask-second, fully-
                    filled-done, optional-slot-never-asked

Each verifier asserts byte-equality between the reference impl's
serde_json output and the vector's `expected_*`. Adding a vector
extends the conformance contract for every implementer.

The Python and Go reference verifiers (the JCR-1 multi-language
pattern) are roadmap, not part of this commit — the vectors are
language-neutral so other-language verifiers can be added without
changing the contract.

4 new tests, one per primitive — all green.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-06-04 03:32:06 -04:00

80 lines
3 KiB
Rust

//! Oracle conformance — the reference Oracle implementations
//! (`AlwaysPassOracle`, `AlwaysFailOracle`, `CompositeOracle`) must
//! produce the verdict listed in `conformance/vectors.json` for every
//! (oracle_program, candidate) pair, byte-equal under serde_json.
use jouleclaw_oracle::{
AlwaysFailOracle, AlwaysPassOracle, Candidate, CompositeOracle, Oracle, Verdict,
};
use serde_json::Value;
const VECTORS: &str = include_str!("../conformance/vectors.json");
fn parse_candidate(v: &Value) -> Candidate {
Candidate {
id: v["id"].as_str().unwrap_or("").to_string(),
content: v["content"].as_str().unwrap_or("").to_string(),
language: v["language"].as_str().unwrap_or("").to_string(),
source_tier: v["source_tier"].as_str().unwrap_or("").to_string(),
provenance: v["provenance"].as_str().unwrap_or("").to_string(),
}
}
fn build_oracle(program: &[Value]) -> Box<dyn Oracle> {
// The first node is either a leaf (always_pass / always_fail) or
// a composite header (kind="composite", id=…) followed by its
// members.
let head = &program[0];
match head["kind"].as_str().expect("kind") {
"always_pass" => Box::new(AlwaysPassOracle),
"always_fail" => Box::new(AlwaysFailOracle::new(
head["reason"].as_str().unwrap_or(""),
)),
"composite" => {
let id = head["id"].as_str().expect("composite id");
let mut comp = CompositeOracle::new(id);
for member in &program[1..] {
comp = comp.add(build_oracle(std::slice::from_ref(member)));
}
Box::new(comp)
}
other => panic!("unknown oracle kind: {other}"),
}
}
fn check_vector(v: &Value) {
let n = v["n"].as_i64().expect("n");
let desc = v["description"].as_str().expect("description");
let input = &v["input"];
let expected = &v["expected_verdict"];
let candidate = parse_candidate(&input["candidate"]);
let program = input["oracle_program"]
.as_array()
.expect("oracle_program array");
let oracle = build_oracle(program);
// Drive the async verify via a current-thread runtime.
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("tokio runtime");
let verdict: Verdict = rt.block_on(oracle.verify(&candidate)).expect("verify");
let got = serde_json::to_value(&verdict).expect("verdict serialises");
assert_eq!(
&got, expected,
"vector {n} ({desc}): verdict mismatch.\n got = {got}\n expected = {expected}"
);
}
#[test]
fn every_oracle_vector_matches_the_reference() {
let suite: Value = serde_json::from_str(VECTORS).expect("vectors.json parses");
let count = suite["vector_count"].as_u64().expect("vector_count") as usize;
let vectors = suite["vectors"].as_array().expect("vectors array");
assert_eq!(vectors.len(), count, "vector_count mismatch");
for v in vectors {
check_vector(v);
}
}