diff --git a/crates/aura-research/src/lib.rs b/crates/aura-research/src/lib.rs index 70bbbf5..1ea8c8e 100644 --- a/crates/aura-research/src/lib.rs +++ b/crates/aura-research/src/lib.rs @@ -123,6 +123,9 @@ pub enum SlotKind { Strings, Windows, Ref, + /// A content-id-only reference (the process ref: `validate_campaign` + /// refuses an identity id there, so the describe path must not offer one). + ContentRef, Axes, EmitKinds, } @@ -684,7 +687,7 @@ pub const CAMPAIGN_SECTIONS: &[BlockSchema] = &[ BlockSchema { id: "std::process_ref", doc: "campaign section: reference to a named process document (content id)", - slots: &[SlotInfo { name: "ref", kind: SlotKind::Ref, required: true }], + slots: &[SlotInfo { name: "ref", kind: SlotKind::ContentRef, required: true }], }, BlockSchema { id: "std::presentation", @@ -708,6 +711,7 @@ pub fn slot_kind_label(kind: SlotKind) -> &'static str { SlotKind::Strings => "list of strings", SlotKind::Windows => "list of { from_ms, to_ms }", SlotKind::Ref => "one of { content_id } | { identity_id }", + SlotKind::ContentRef => "content id of a process document", SlotKind::Axes => "map: param name -> { kind, values }", SlotKind::EmitKinds => "list of: family_table | selection_report", } @@ -1197,6 +1201,41 @@ mod tests { assert!(describe_block("std::nope").is_none()); } + /// A campaign process reference is content-id-only: `validate_campaign` + /// refuses an `identity_id` process ref (`ProcessRefMustBeContentId`), and + /// the block's own doc line says "(content id)". So the `std::process_ref` + /// describe output — the `describe_block` + `slot_kind_label` composition + /// the `--block` CLI verb renders — must not advertise an `identity_id` + /// form the validator would reject. The `--unwired` hint is already + /// narrowed (see `open_slots_campaign`); this pins the parallel describe + /// path to the same content-id-only contract. The narrowing is + /// process-ref-specific: a `std::strategy` ref legitimately accepts either + /// id form and must keep advertising both. + #[test] + fn process_ref_describe_advertises_content_id_only() { + let block = describe_block("std::process_ref").expect("process_ref describable"); + let ref_slot = + block.slots.iter().find(|s| s.name == "ref").expect("process_ref has a ref slot"); + let label = slot_kind_label(ref_slot.kind); + assert!( + !label.contains("identity_id"), + "process_ref advertises an identity_id form the validator refuses: {label}" + ); + assert!( + label.contains("content"), + "process_ref describe must still name the content-id form: {label}" + ); + + let strategy = describe_block("std::strategy").expect("strategy describable"); + let strat_ref = + strategy.slots.iter().find(|s| s.name == "ref").expect("strategy has a ref slot"); + let strat_label = slot_kind_label(strat_ref.kind); + assert!( + strat_label.contains("identity_id") && strat_label.contains("content_id"), + "strategy ref must keep advertising both id forms: {strat_label}" + ); + } + #[test] fn open_slots_report_partial_documents_by_path() { assert_eq!(open_slots_process(PROCESS_FIXTURE).unwrap(), Vec::new());