Files
Aura/crates/aura-measurement/tests/registry_dispatch.rs
T
claude 5006766579 audit: shell-boundary cycle close — drift resolved in-commit
Cycle-close audit (architect: drift_found; design core clean). What
holds, architect-confirmed with the guard run and the diff read in
full: the shell boundary is real and enforced (full-workspace
c28_layering incl. completeness + shell-content checks, acceptance
grep clean — no member-run symbol left under crates/aura-cli/src);
aura-campaign keeps zero production dependency on the runner; the IC
move is verbatim against the anchor; the new library-only E2E pins C1
(two independent runners, byte-identical RunReport).

Drift items, all resolved as fixes in this commit:
- design ledger: the C28 status sentence "No crate exists yet for
  measurement" contradicted this cycle's own phase-3 done line — now
  records aura-measurement as seeded (#295), execution still unbuilt.
- the #147 registry-dispatch acceptance tests (IC deflation null,
  seeded determinism, cross-vocabulary refusal) were pure library
  properties stranded in the shell's test module — relocated to
  aura-measurement/tests/registry_dispatch.rs (engine/registry as
  dev-only, layering-exempt edges), per the cycle's own tests-move-
  with-their-module rule.
- aura-runner's direct production edge on the external data-server
  tree was structurally invisible (the direction table checks aura-*
  keys only) and unrecorded — the C28 assembly prose now names it, and
  a new c28_layering test pins the external tree's entry points to
  exactly {aura-ingest, aura-runner, aura-cli}.
- the C28 shell prose named three buckets that did not cover two real
  shell residents — it now names the op-script construction front-end
  (translation) and the `aura new` scaffolder (authoring-tooling,
  shell-resident like rendering until a second consumer wants it).

Deliberately deferred, tracker homes exist (not drift): #297
(process::exit sites in aura-runner's single-run verb paths), #294
(IC duplicate-timestamp semantics), #288-era rustdoc unresolved-link
warnings in aura-std/aura-backtest (pre-existing byte-identically at
the anchor).

No regression scripts are configured (the bench is report-only); the
architect review is the gate. Verification: cargo test --workspace
green (1474 passed, 0 failed) incl. the relocated registry-dispatch
tests and the new data-server guard; clippy --workspace --all-targets
-D warnings clean.

refs #295
2026-07-21 06:46:13 +02:00

102 lines
4.4 KiB
Rust

//! #147 acceptance at the library boundary: the registry's generic
//! deflation machinery dispatches to the IC vocabulary's OWN permutation
//! null, deterministically (C1), and the C10 wall refuses cross-vocabulary
//! metric names both ways. Relocated from the shell's test module with #295
//! (these are pure library properties — a library-only consumer must be able
//! to run them; the registry/engine edges are dev-only, layering-exempt).
use aura_measurement::IcMetrics;
fn ic_report(sigs: Vec<f64>, frs: Vec<f64>) -> aura_engine::RunReport<IcMetrics> {
aura_engine::RunReport {
manifest: aura_engine::RunManifest {
commit: "c".to_string(),
params: vec![("p".to_string(), aura_core::Scalar::f64(1.0))],
defaults: vec![],
window: (aura_core::Timestamp(1), aura_core::Timestamp(2)),
seed: 0,
broker: "b".to_string(),
selection: None,
instrument: None,
topology_hash: None,
project: None,
},
metrics: IcMetrics {
information_coefficient: aura_analysis::pearson_corr(&sigs, &frs),
sigs,
frs,
},
}
}
fn ic_family(members: Vec<aura_engine::RunReport<IcMetrics>>) -> aura_engine::SweepFamily<IcMetrics> {
aura_engine::SweepFamily {
space: vec![],
points: members
.into_iter()
.enumerate()
.map(|(i, report)| aura_engine::SweepPoint {
params: vec![aura_core::Cell::from_f64(i as f64)],
report,
})
.collect(),
}
}
/// #147 acceptance: the registry's deflation dispatches to the IC
/// vocabulary's OWN permutation null — a genuinely predictive family
/// deflates to a low overfit probability, a noise family to a high one.
#[test]
fn ic_family_deflation_dispatches_the_permutation_null() {
let ramp: Vec<f64> = (0..24).map(|i| i as f64).collect();
let engineered = ic_family(vec![
ic_report(ramp.clone(), ramp.clone()), // corr = 1.0: a real edge
ic_report(ramp.clone(), ramp.iter().rev().cloned().collect()), // corr = -1.0
]);
let (winner, sel) =
aura_registry::optimize_deflated(&engineered, "information_coefficient", 500, 5, 0)
.expect("ic family deflates");
assert!((winner.report.metrics.information_coefficient - 1.0).abs() < 1e-12);
let p = sel.overfit_probability.expect("resampling-null arm sets a probability");
assert!(p < 0.05, "perfectly predictive family must not look like overfit: p={p}");
// orthogonal signal — IC exactly 0.0; permuted draws beat it ~half the time.
let noise = ic_family(vec![ic_report(
vec![0.03, 0.01, 0.04, 0.02, 0.05, 0.00, 0.06, -0.01],
vec![0.01, 0.02, 0.03, 0.04, -0.04, -0.03, -0.02, -0.01],
)]);
let (_, sel) =
aura_registry::optimize_deflated(&noise, "information_coefficient", 500, 5, 0)
.expect("noise family deflates");
let p = sel.overfit_probability.expect("probability present");
assert!(p > 0.1, "a noise family must carry a high overfit probability: p={p}");
}
/// Same seed → identical selection provenance (C1 through the generic path).
#[test]
fn ic_family_deflation_is_deterministic_given_seed() {
let ramp: Vec<f64> = (0..16).map(|i| i as f64).collect();
let fam = ic_family(vec![ic_report(ramp.clone(), ramp)]);
let a = aura_registry::optimize_deflated(&fam, "information_coefficient", 300, 5, 7)
.expect("deflate a");
let b = aura_registry::optimize_deflated(&fam, "information_coefficient", 300, 5, 7)
.expect("deflate b");
assert_eq!(a.1, b.1, "same seed must yield identical FamilySelection");
}
/// C10 guard: cross-vocabulary names are refused, and the refusal names
/// the family's REAL vocabulary.
#[test]
fn cross_vocabulary_names_are_refused_both_ways() {
let ramp: Vec<f64> = (0..8).map(|i| i as f64).collect();
let fam = ic_family(vec![ic_report(ramp.clone(), ramp)]);
let err = aura_registry::optimize_deflated(&fam, "sqn", 100, 5, 0).unwrap_err();
let prose = err.to_string();
assert!(
prose.contains("unknown metric 'sqn'") && prose.contains("information_coefficient"),
"refusal must name the IC vocabulary: {prose}"
);
// and the R side still refuses the IC name with ITS roster:
assert!(aura_registry::check_r_metric("information_coefficient").is_err());
}