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:
@@ -462,8 +462,14 @@ pub(crate) fn ref_fault_prose(f: &RefFault) -> String {
|
||||
RefFault::StrategyUnloadable { id, error } => {
|
||||
format!("strategy {id} cannot be loaded: {error}")
|
||||
}
|
||||
RefFault::AxisNotInParamSpace { strategy, axis } => {
|
||||
format!("strategy {strategy}: axis \"{axis}\" is not in the param space")
|
||||
RefFault::AxisNotInParamSpace { strategy, axis, raw_candidate } => {
|
||||
let mut msg = format!("strategy {strategy}: axis \"{axis}\" is not in the param space");
|
||||
if let Some(candidate) = raw_candidate {
|
||||
msg.push_str(&format!(
|
||||
"; axis names are raw node.param paths — did you mean \"{candidate}\"?"
|
||||
));
|
||||
}
|
||||
msg
|
||||
}
|
||||
RefFault::AxisKindMismatch { strategy, axis } => {
|
||||
format!("strategy {strategy}: axis \"{axis}\" declares a kind that is not the param's kind")
|
||||
@@ -761,6 +767,7 @@ mod tests {
|
||||
ref_fault_prose(&RefFault::AxisNotInParamSpace {
|
||||
strategy: "9f3a".into(),
|
||||
axis: "nope".into(),
|
||||
raw_candidate: None,
|
||||
}),
|
||||
ref_fault_prose(&RefFault::AxisKindMismatch {
|
||||
strategy: "9f3a".into(),
|
||||
@@ -781,6 +788,39 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// #328: `ref_fault_prose` appends the did-you-mean clause, contiguous
|
||||
/// and verbatim, exactly when `raw_candidate` is present — the document-
|
||||
/// side refusal seam's mirror of the sweep `--axis` intake translation
|
||||
/// (`aura-runner`'s `axes.rs` classify predicate), sharing the same
|
||||
/// `axis names are raw node.param paths` clause pinned there.
|
||||
#[test]
|
||||
fn ref_fault_prose_renders_the_did_you_mean_when_a_raw_candidate_is_present() {
|
||||
let msg = ref_fault_prose(&RefFault::AxisNotInParamSpace {
|
||||
strategy: "9f3a".into(),
|
||||
axis: "graph.fast.length".into(),
|
||||
raw_candidate: Some("fast.length".into()),
|
||||
});
|
||||
assert_eq!(
|
||||
msg,
|
||||
"strategy 9f3a: axis \"graph.fast.length\" is not in the param space; \
|
||||
axis names are raw node.param paths — did you mean \"fast.length\"?"
|
||||
);
|
||||
}
|
||||
|
||||
/// #328 (negative): a rejected axis with no `raw_candidate` (stripping one
|
||||
/// leading segment yields no param-space hit, or the axis has no leading
|
||||
/// segment to strip at all) renders today's prose unchanged — no
|
||||
/// speculative suggestion.
|
||||
#[test]
|
||||
fn ref_fault_prose_omits_the_did_you_mean_when_no_raw_candidate() {
|
||||
let msg = ref_fault_prose(&RefFault::AxisNotInParamSpace {
|
||||
strategy: "9f3a".into(),
|
||||
axis: "nope".into(),
|
||||
raw_candidate: None,
|
||||
});
|
||||
assert_eq!(msg, "strategy 9f3a: axis \"nope\" is not in the param space");
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// #194 (the prefix trap): a doc ref that carries the display `content:`
|
||||
/// prefix (a copy-paste from register/introspect output) is bare-only in
|
||||
|
||||
Reference in New Issue
Block a user