feat(aura-runner, aura-cli, aura-registry): one raw axis namespace end to end
Reconciles the sweep-axis namespace (closes #328): the raw form <node>.<param> (splice paths keep their interior path) is now the only user-facing axis name — printed by --list-axes, accepted by --axis on BOTH sweep routes, recorded in the synthetic run manifest, discovered by graph introspect --params (bound params included, default= lexicon), and validated by campaign documents. The wrapped <blueprint>.<...> form is retired with translation refusals on both seams. Mechanics: - classify_axis_intake (aura-runner axes.rs): explicit raw-first acceptance predicate shared by both --axis intake routes. Deliberately not built on raw_matches_wrapped, whose equality branch would have accepted the retired form silently (skeptic finding, minuted on #328). Wrapped hit -> translation refusal naming the raw candidate (shared prose builder wrapped_axis_refusal, one literal for both routes). - The synthetic route resolution moves to the raw frame the real route (bind_axes) already uses: override_paths/wrapped_bound_overrides_of match via raw_matches_wrapped (also removing a latent slicing panic), blueprint_sweep_family translates raw names onto wrapped SweepBinder slots, manifest.params records raw keys (name-only reshaping, zip is positional). The two sweep routes now share one convention. - reproduce translates recorded manifest names raw-or-wrapped onto the wrapped space: old registered artifacts stay replayable (C29), new raw manifests round-trip. - introspect --params gains the bound default= lines via the same render_value the --list-axes bound pass uses - the two discovery surfaces are byte-identical by construction. - AxisNotInParamSpace carries raw_candidate; ref_fault_prose renders the did-you-mean iff present. Sweep-terminal bind errors (MissingKnob/KindMismatch) render the raw knob name (render seam only; acceptance criterion: no user-facing surface prints a wrapped axis name). - Downstream consequence fixes: scaffold quickstart example spoke wrapped; ~90 wrapped test literals converted to raw across six test files (deliberately-bogus refusal names and the untouched internal walkforward wrapped route kept, with comments). Fork decisions and the skeptic correction are minuted on #328 (comments 2026-07-24/25). Grounding-check PASS first attempt (12 assumptions ratified). Review (opus): translation ambiguity cleared (uniform single prefix per blueprint), reproduce both-shapes traced; repair pass added the synthetic-route refusal e2e + the shared prose builder. Verified: cargo test --workspace green (99 targets, 0 failures), clippy -D warnings clean. refs #246, refs #319, refs #331
This commit is contained in:
@@ -669,7 +669,10 @@ fn graph_params_lists_raw_axis_namespace() {
|
||||
let (stdout, stderr, code) =
|
||||
run_in(&dir, &["graph", "introspect", "--params", &fixture("r_sma_open.json")]);
|
||||
assert_eq!(code, Some(0), "stdout: {stdout} stderr: {stderr}");
|
||||
assert_eq!(stdout, "fast.length:I64\nslow.length:I64\n", "the raw open params, in order");
|
||||
assert_eq!(
|
||||
stdout, "fast.length:I64\nslow.length:I64\nbias.scale:F64 default=0.5\n",
|
||||
"the raw open params, then the raw bound param with its default"
|
||||
);
|
||||
}
|
||||
|
||||
/// Property (#196, file-mode --content-id): a blueprint FILE's printed content
|
||||
@@ -719,7 +722,10 @@ fn graph_params_by_content_id_matches_params_by_file() {
|
||||
run_in(&dir, &["graph", "introspect", "--params", &bp]);
|
||||
assert_eq!(by_file_code, Some(0), "stdout: {by_file_out} stderr: {by_file_err}");
|
||||
assert_eq!(by_id_out, by_file_out, "by-id and by-file agree on the raw axis namespace");
|
||||
assert_eq!(by_id_out, "fast.length:I64\nslow.length:I64\n", "the raw open params, in order");
|
||||
assert_eq!(
|
||||
by_id_out, "fast.length:I64\nslow.length:I64\nbias.scale:F64 default=0.5\n",
|
||||
"the raw open params, then the raw bound param with its default"
|
||||
);
|
||||
}
|
||||
|
||||
/// #194 (the prefix trap): a `--params <ID>` target may carry the display
|
||||
@@ -736,7 +742,10 @@ fn graph_params_tolerates_content_prefix_on_target() {
|
||||
let (pfx_out, pfx_err, pfx_code) =
|
||||
run_in(&dir, &["graph", "introspect", "--params", &format!("content:{id}")]);
|
||||
assert_eq!(pfx_code, Some(0), "content:-prefixed --params must resolve: stdout {pfx_out} stderr {pfx_err}");
|
||||
assert_eq!(pfx_out, "fast.length:I64\nslow.length:I64\n", "same raw axis namespace as the bare id");
|
||||
assert_eq!(
|
||||
pfx_out, "fast.length:I64\nslow.length:I64\nbias.scale:F64 default=0.5\n",
|
||||
"same raw axis namespace as the bare id"
|
||||
);
|
||||
}
|
||||
|
||||
/// Property (#196, negative): `--params <ID>` with a well-shaped 64-hex id
|
||||
@@ -965,7 +974,10 @@ fn shipped_r_sma_example_is_genuinely_closed() {
|
||||
let (stdout, stderr, code) =
|
||||
run_in(&dir, &["graph", "introspect", "--params", &example("r_sma.json")]);
|
||||
assert_eq!(code, Some(0), "stdout: {stdout} stderr: {stderr}");
|
||||
assert_eq!(stdout, "", "the closed example must leave zero params unbound");
|
||||
assert_eq!(
|
||||
stdout, "fast.length:I64 default=2\nslow.length:I64 default=4\nbias.scale:F64 default=0.5\n",
|
||||
"zero OPEN params — every knob is now printed as a raw bound default instead"
|
||||
);
|
||||
}
|
||||
|
||||
/// Property (#159): the open fixture (`tests/fixtures/r_sma_open.json`) is a
|
||||
@@ -983,8 +995,8 @@ fn open_r_sma_fixture_lists_its_axis_namespace() {
|
||||
run_in(&dir, &["graph", "introspect", "--params", &fixture("r_sma_open.json")]);
|
||||
assert_eq!(code, Some(0), "stdout: {stdout} stderr: {stderr}");
|
||||
assert_eq!(
|
||||
stdout, "fast.length:I64\nslow.length:I64\n",
|
||||
"the raw open params, in order, from the public gallery copy"
|
||||
stdout, "fast.length:I64\nslow.length:I64\nbias.scale:F64 default=0.5\n",
|
||||
"the raw open params, then the raw bound param, from the public gallery copy"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1021,12 +1033,17 @@ fn shipped_r_breakout_example_is_genuinely_closed() {
|
||||
let (stdout, stderr, code) =
|
||||
run_in(&dir, &["graph", "introspect", "--params", &example("r_breakout.json")]);
|
||||
assert_eq!(code, Some(0), "stdout: {stdout} stderr: {stderr}");
|
||||
assert_eq!(stdout, "", "the closed example must leave zero params unbound");
|
||||
assert_eq!(
|
||||
stdout,
|
||||
"delay.lag:I64 default=1\nchannel_hi.length:I64 default=3\nchannel_lo.length:I64 default=3\n",
|
||||
"zero OPEN params — every knob is now printed as a raw bound default instead"
|
||||
);
|
||||
}
|
||||
|
||||
/// Property (#159 cut 2): the open fixture (`tests/fixtures/r_breakout_open.json`)
|
||||
/// lists its ONE ganged channel axis (#61: the two rolling windows are structurally
|
||||
/// one knob).
|
||||
/// one knob) followed by the still-bound `delay.lag` default (#328: bound params
|
||||
/// are listed too, line-identical to `--list-axes`).
|
||||
#[test]
|
||||
fn open_r_breakout_fixture_lists_its_axis_namespace() {
|
||||
let dir = temp_cwd("r-breakout-example-open-params");
|
||||
@@ -1034,8 +1051,9 @@ fn open_r_breakout_fixture_lists_its_axis_namespace() {
|
||||
run_in(&dir, &["graph", "introspect", "--params", &fixture("r_breakout_open.json")]);
|
||||
assert_eq!(code, Some(0), "stdout: {stdout} stderr: {stderr}");
|
||||
assert_eq!(
|
||||
stdout, "channel_length:I64\n",
|
||||
"the ganged channel knob — one public axis for the two rolling windows (#61)"
|
||||
stdout, "channel_length:I64\ndelay.lag:I64 default=1\n",
|
||||
"the ganged channel knob — one public axis for the two rolling windows (#61) — \
|
||||
then the still-bound delay.lag default"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1064,7 +1082,7 @@ fn open_r_breakout_fixture_gang_axis_matches_the_closed_example() {
|
||||
let sweep_dir = temp_cwd("r-breakout-gang-axis-sweep");
|
||||
let (sweep_stdout, sweep_stderr, sweep_code) = run_in(
|
||||
&sweep_dir,
|
||||
&["sweep", &fixture("r_breakout_open.json"), "--axis", "r_breakout_signal.channel_length=3"],
|
||||
&["sweep", &fixture("r_breakout_open.json"), "--axis", "channel_length=3"],
|
||||
);
|
||||
assert_eq!(sweep_code, Some(0), "sweep stderr: {sweep_stderr}");
|
||||
let lines: Vec<&str> = sweep_stdout.lines().collect();
|
||||
@@ -1086,7 +1104,11 @@ fn shipped_r_meanrev_example_is_genuinely_closed() {
|
||||
let (stdout, stderr, code) =
|
||||
run_in(&dir, &["graph", "introspect", "--params", &example("r_meanrev.json")]);
|
||||
assert_eq!(code, Some(0), "stdout: {stdout} stderr: {stderr}");
|
||||
assert_eq!(stdout, "", "the closed example must leave zero params unbound");
|
||||
assert_eq!(
|
||||
stdout,
|
||||
"mean_window.length:I64 default=3\nvar_window.length:I64 default=3\nband.factor:F64 default=2\n",
|
||||
"zero OPEN params — every knob is now printed as a raw bound default instead"
|
||||
);
|
||||
}
|
||||
|
||||
/// Property (#159 cut 3): the open fixture (`tests/fixtures/r_meanrev_open.json`)
|
||||
@@ -1124,8 +1146,8 @@ fn open_r_meanrev_fixture_gang_plus_plain_axis_matches_the_closed_example() {
|
||||
&sweep_dir,
|
||||
&[
|
||||
"sweep", &fixture("r_meanrev_open.json"),
|
||||
"--axis", "r_meanrev_signal.window=3",
|
||||
"--axis", "r_meanrev_signal.band.factor=2.0",
|
||||
"--axis", "window=3",
|
||||
"--axis", "band.factor=2.0",
|
||||
],
|
||||
);
|
||||
assert_eq!(sweep_code, Some(0), "sweep stderr: {sweep_stderr}");
|
||||
@@ -1890,8 +1912,8 @@ fn graph_build_use_end_to_end_axes() {
|
||||
run_in(&dir, &["sweep", consumer_bp.to_str().unwrap(), "--list-axes"]);
|
||||
assert_eq!(axes_code, Some(0), "list-axes: {axes_out} {axes_err}");
|
||||
assert_eq!(
|
||||
axes_out, "graph.trend.sma.length:I64\n",
|
||||
"the spliced instance's open param surfaces path-qualified, bare (unbound) form"
|
||||
axes_out, "trend.sma.length:I64\n",
|
||||
"the spliced instance's open param surfaces path-qualified, bare (unbound) RAW form (#328)"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user