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]>
74 lines
2.9 KiB
Rust
74 lines
2.9 KiB
Rust
//! Cone conformance — the reference [`BandDispatch`] must select the
|
|
//! `entry_tier` listed in `conformance/vectors.json` for every
|
|
//! (band, novelty) pair, and must succeed / refuse escalation
|
|
//! identically. Any independent implementation is conformant iff it
|
|
//! matches every vector.
|
|
|
|
use jouleclaw_cone::{BandDispatch, ConeCoord, ConeError, NoveltyBand};
|
|
use serde_json::Value;
|
|
|
|
const VECTORS: &str = include_str!("../conformance/vectors.json");
|
|
|
|
fn build_band(name: &str) -> NoveltyBand {
|
|
match name {
|
|
"datum_default" => NoveltyBand::datum_default(),
|
|
"jouleclaw_cascade_default" => NoveltyBand::jouleclaw_cascade_default(),
|
|
other => panic!("unknown band preset: {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"];
|
|
|
|
let band_name = input["band"].as_str().expect("band name");
|
|
let dispatch = BandDispatch::new(build_band(band_name)).expect("valid band");
|
|
let coord = ConeCoord::new(
|
|
input["complexity"].as_f64().expect("complexity") as f32,
|
|
input["novelty"].as_f64().expect("novelty") as f32,
|
|
);
|
|
|
|
let entry = dispatch.entry_tier(coord);
|
|
let expected_entry = expected["entry_tier"].as_str().expect("entry_tier");
|
|
assert_eq!(
|
|
entry, expected_entry,
|
|
"vector {n} ({desc}): entry_tier mismatch — got {entry}, expected {expected_entry}"
|
|
);
|
|
|
|
if let Some(from) = input.get("escalate_from").and_then(|v| v.as_str()) {
|
|
match dispatch.escalate(from) {
|
|
Ok(to) => {
|
|
let exp_to = expected["escalate_to"].as_str().unwrap_or("");
|
|
assert_eq!(
|
|
to, exp_to,
|
|
"vector {n} ({desc}): escalate({from}) → {to}, expected {exp_to}"
|
|
);
|
|
}
|
|
Err(ConeError::AtApex(_)) => {
|
|
let exp_err = expected["escalate_error"].as_str().unwrap_or("");
|
|
assert_eq!(
|
|
exp_err, "at_apex",
|
|
"vector {n} ({desc}): escalate returned AtApex but vector expected {exp_err:?}"
|
|
);
|
|
assert!(
|
|
expected["escalate_to"].is_null(),
|
|
"vector {n}: escalate_to must be null when at_apex"
|
|
);
|
|
}
|
|
Err(other) => panic!("vector {n} ({desc}): unexpected cone error: {other}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn every_cone_vector_matches_the_reference_dispatcher() {
|
|
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);
|
|
}
|
|
}
|