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
This commit is contained in:
Generated
+2
@@ -245,6 +245,8 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"aura-analysis",
|
"aura-analysis",
|
||||||
"aura-core",
|
"aura-core",
|
||||||
|
"aura-engine",
|
||||||
|
"aura-registry",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -84,11 +84,6 @@ use aura_runner::member::sim_optimal_manifest;
|
|||||||
// tests that exercised them — no longer imported here.
|
// tests that exercised them — no longer imported here.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use aura_runner::member::CostLeg;
|
use aura_runner::member::CostLeg;
|
||||||
// `IcMetrics` is only reached from the test module's `ic_report`/`ic_family` carves
|
|
||||||
// (the `IcReport` DTO holds the scalar directly in production), mirroring `Bias`
|
|
||||||
// below.
|
|
||||||
#[cfg(test)]
|
|
||||||
use aura_measurement::IcMetrics;
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use aura_backtest::{summarize, summarize_r};
|
use aura_backtest::{summarize, summarize_r};
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -3860,100 +3855,3 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod ic_tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -15,3 +15,11 @@ aura-analysis = { path = "../aura-analysis" }
|
|||||||
# R-vocabulary metric types (aura-backtest, aura-engine); the aligned pairs
|
# R-vocabulary metric types (aura-backtest, aura-engine); the aligned pairs
|
||||||
# ride #[serde(skip)] (in-memory null inputs only, never persisted).
|
# ride #[serde(skip)] (in-memory null inputs only, never persisted).
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
|
|
||||||
|
# The #147 registry-dispatch acceptance tests (tests/registry_dispatch.rs)
|
||||||
|
# exercise the generic deflation machinery over the IC vocabulary — pure
|
||||||
|
# library properties relocated from the shell with #295. Dev-only edges,
|
||||||
|
# C28-layering-exempt.
|
||||||
|
[dev-dependencies]
|
||||||
|
aura-engine = { path = "../aura-engine" }
|
||||||
|
aura-registry = { path = "../aura-registry" }
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
//! #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());
|
||||||
|
}
|
||||||
@@ -190,3 +190,29 @@ fn the_shell_defines_no_domain_modules_and_no_lib_target() {
|
|||||||
and presentation only — a new module needs a library home"
|
and presentation only — a new module needs a library home"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn the_external_data_server_tree_enters_at_pinned_points_only() {
|
||||||
|
// C28 (#295 audit): the external `data-server` tree is a production
|
||||||
|
// dependency of exactly the ingestion edge (External components), the
|
||||||
|
// assembly position, and the shell. Any further crate pulling it in is
|
||||||
|
// a new, undecided workspace entry point for the external tree — the
|
||||||
|
// aura-*-only direction table above cannot see it, so it is pinned here.
|
||||||
|
let permitted = ["aura-cli", "aura-ingest", "aura-runner"];
|
||||||
|
for name in workspace_crate_names() {
|
||||||
|
let manifest = workspace_root().join("crates").join(&name).join("Cargo.toml");
|
||||||
|
let text = std::fs::read_to_string(&manifest)
|
||||||
|
.unwrap_or_else(|e| panic!("read {}: {e}", manifest.display()));
|
||||||
|
let doc: toml::Value = toml::from_str(&text).expect("Cargo.toml parses as TOML");
|
||||||
|
let has = doc
|
||||||
|
.get("dependencies")
|
||||||
|
.and_then(|d| d.as_table())
|
||||||
|
.is_some_and(|deps| deps.contains_key("data-server"));
|
||||||
|
assert_eq!(
|
||||||
|
has,
|
||||||
|
permitted.contains(&name.as_str()),
|
||||||
|
"C28: `data-server` [dependencies] edge on `{name}` — the external \
|
||||||
|
tree enters the workspace only via {permitted:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+17
-8
@@ -2648,11 +2648,19 @@ composes the rungs into the canonical member-run recipe — harness assembly,
|
|||||||
input binding (C26), the C1-load-bearing param↔config translators, and the
|
input binding (C26), the C1-load-bearing param↔config translators, and the
|
||||||
shipped default `MemberRunner` — importing the ladder rungs, `aura-composites`,
|
shipped default `MemberRunner` — importing the ladder rungs, `aura-composites`,
|
||||||
`aura-ingest`, and the process column, and imported only by the shell and by
|
`aura-ingest`, and the process column, and imported only by the shell and by
|
||||||
downstream World programs. The **shell** (the `aura` CLI) imports everything,
|
downstream World programs. It also carries a direct production dependency on
|
||||||
|
the external `data-server` tree (inherited from the shell with the recipe: the
|
||||||
|
runner constructs archive servers itself). The external tree therefore enters
|
||||||
|
the workspace at the ingestion edge (`aura-ingest`, External components), the
|
||||||
|
assembly position (`aura-runner`), and the shell (which imports everything by
|
||||||
|
definition) — and at no ladder or column crate beyond those; the
|
||||||
|
`c28_layering` guard pins the exact set. The **shell** (the `aura` CLI) imports everything,
|
||||||
is imported by nothing, exports nothing, and holds no domain logic:
|
is imported by nothing, exports nothing, and holds no domain logic:
|
||||||
argv/dispatch, argv→document translation, and presentation only (rendering
|
argv/dispatch, argv→document translation (including the op-script construction
|
||||||
stays in the shell until the C22 web face provides its second consumer — #295
|
front-end, `graph_construct`), presentation, and the `aura new` project
|
||||||
deferral).
|
scaffolder (authoring-tooling template emission — shell-resident, like
|
||||||
|
rendering, until a second consumer wants it). Rendering stays in the shell
|
||||||
|
until the C22 web face provides its second consumer (#295 deferral).
|
||||||
|
|
||||||
**Import rule.** An outer ladder layer may import inner layers, never the
|
**Import rule.** An outer ladder layer may import inner layers, never the
|
||||||
reverse; *measurement* and *strategy* are siblings (no import either way);
|
reverse; *measurement* and *strategy* are siblings (no import either way);
|
||||||
@@ -2738,10 +2746,11 @@ cleanly by layer, so the layering is realized only partially:
|
|||||||
interweave is resolved (phase 5, #291): the backtest reductions live in
|
interweave is resolved (phase 5, #291): the backtest reductions live in
|
||||||
`aura-backtest::metrics` beside their `position_management` producer, and
|
`aura-backtest::metrics` beside their `position_management` producer, and
|
||||||
`aura-analysis` is reduced to domain-free statistics + selection provenance
|
`aura-analysis` is reduced to domain-free statistics + selection provenance
|
||||||
(`[dependencies]` = serde only). No crate exists yet for *measurement* (its run
|
(`[dependencies]` = serde only). The *measurement* rung is seeded (#295):
|
||||||
verb is the additive shape dispatch, #286) or *execution* (unbuilt — the C10/C13
|
`aura-measurement` carries the IC vocabulary + reduction (its run verb remains
|
||||||
edge only). Under this model the `aura-registry → aura-research` edge is
|
the additive shape dispatch, #286). No crate exists yet for *execution*
|
||||||
column-internal and legal.
|
(unbuilt — the C10/C13 edge only). Under this model the
|
||||||
|
`aura-registry → aura-research` edge is column-internal and legal.
|
||||||
- Phased realization (each independently shippable; behaviour byte-identical
|
- Phased realization (each independently shippable; behaviour byte-identical
|
||||||
except the purely additive shape dispatch): (1) this contract; (2) cut the
|
except the purely additive shape dispatch): (1) this contract; (2) cut the
|
||||||
engine's backtest-metrics edge via `RunReport<M>` — **done** (#292:
|
engine's backtest-metrics edge via `RunReport<M>` — **done** (#292:
|
||||||
|
|||||||
Reference in New Issue
Block a user