diff --git a/crates/aura-research/src/lib.rs b/crates/aura-research/src/lib.rs index f754de4..cea174e 100644 --- a/crates/aura-research/src/lib.rs +++ b/crates/aura-research/src/lib.rs @@ -295,10 +295,17 @@ fn select_from( ) -> Result { let s = require_str(m, "select", block)?; // SelectRule's own derived Deserialize (which carries the #[serde(rename)] - // wire strings) is the type oracle, mirroring kind_tag/scalar_from_bare: - // no second hardcoded copy of the three select-rule wire strings. - serde_json::from_value(serde_json::Value::String(s.clone())) - .map_err(|_| format!("block {block}: unknown select rule \"{s}\"")) + // wire strings) is the type oracle for *parsing*, mirroring + // kind_tag/scalar_from_bare. The refusal's roster text below is sourced + // from slot_kind_label(SlotKind::SelectRule) rather than a second + // hardcoded copy of the three wire strings: CLI describe output and the + // document refusal stay in sync by construction (add a SelectRule + // variant, update one string). + serde_json::from_value(serde_json::Value::String(s.clone())).map_err(|_| { + let roster = + slot_kind_label(SlotKind::SelectRule).trim_start_matches("select rule: "); + format!("block {block}: unknown select rule \"{s}\" (accepted: {roster})") + }) } fn mode_from( @@ -1413,6 +1420,20 @@ mod tests { assert!(!msg.contains("alias"), "refusal grew an alias hint: {msg}"); } + /// The refusal derives its roster from slot_kind_label by trimming the + /// "select rule: " prefix. That trim is a positional contract: if the + /// label's wording ever drifts, trim_start_matches degrades to a no-op + /// and the refusal grows a redundant prefix without any test noticing. + /// This pin makes that drift loud. + #[test] + fn select_rule_label_keeps_the_prefix_the_refusal_trims() { + assert!( + slot_kind_label(SlotKind::SelectRule).starts_with("select rule: "), + "slot_kind_label(SelectRule) no longer starts with \"select rule: \" — \ + update select_from's roster derivation alongside the label" + ); + } + pub(crate) const CAMPAIGN_FIXTURE: &str = r#"{ "format_version": 1, "kind": "campaign",