1use serde::Serialize;
9
10pub const ARBITER_FORMATION_PACK_ID: &str = "arbiter.cedar";
12
13pub const POLICY_GATE_CAPABILITY_ID: &str = "arbiter.cedar.policy_gate";
15
16pub const HITL_GATE_CAPABILITY_ID: &str = "arbiter.cedar.hitl_gate";
18
19pub const ANALYSIS_EVIDENCE_CAPABILITY_ID: &str = "arbiter.cedar.analysis_evidence";
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
23#[serde(rename_all = "snake_case")]
24pub enum ArbiterFormationCapabilityKind {
25 RuntimeGate,
26 AnalysisEvidence,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
30pub struct ArbiterFormationCapability {
31 pub id: &'static str,
32 pub pack_id: &'static str,
33 pub kind: ArbiterFormationCapabilityKind,
34 pub suggestor: Option<&'static str>,
35 pub feature: Option<&'static str>,
36 pub input: &'static str,
37 pub output: &'static str,
38 pub evidence_tier: &'static str,
39 pub description: &'static str,
40}
41
42pub const ARBITER_FORMATION_CAPABILITIES: &[ArbiterFormationCapability] = &[
43 ArbiterFormationCapability {
44 id: POLICY_GATE_CAPABILITY_ID,
45 pack_id: ARBITER_FORMATION_PACK_ID,
46 kind: ArbiterFormationCapabilityKind::RuntimeGate,
47 suggestor: Some("policy-gate"),
48 feature: None,
49 input: "arbiter::DecideRequest",
50 output: "arbiter::PolicyDecision",
51 evidence_tier: "decided",
52 description: "Runtime Cedar policy gate over DecideRequest.",
53 },
54 ArbiterFormationCapability {
55 id: HITL_GATE_CAPABILITY_ID,
56 pack_id: ARBITER_FORMATION_PACK_ID,
57 kind: ArbiterFormationCapabilityKind::RuntimeGate,
58 suggestor: Some("cedar-hitl-gate"),
59 feature: None,
60 input: "converge_core::FlowGateInput",
61 output: "converge_core::FlowGateDecision",
62 evidence_tier: "decided",
63 description: "Strict Cedar-backed HITL gate that escalates only when Cedar allows the approved request.",
64 },
65 ArbiterFormationCapability {
66 id: ANALYSIS_EVIDENCE_CAPABILITY_ID,
67 pack_id: ARBITER_FORMATION_PACK_ID,
68 kind: ArbiterFormationCapabilityKind::AnalysisEvidence,
69 suggestor: Some("cedar-analysis"),
70 feature: Some("analysis"),
71 input: "arbiter::CedarAnalysisInput",
72 output: "arbiter::CedarAnalysisReport",
73 evidence_tier: "searched",
74 description: "Cedar SymCC analysis suggestor for searched policy-invariant evidence; not a runtime authorization gate.",
75 },
76];
77
78#[must_use]
79pub fn formation_capabilities() -> &'static [ArbiterFormationCapability] {
80 ARBITER_FORMATION_CAPABILITIES
81}
82
83#[must_use]
84pub fn find_formation_capability(id: &str) -> Option<&'static ArbiterFormationCapability> {
85 ARBITER_FORMATION_CAPABILITIES
86 .iter()
87 .find(|capability| capability.id == id)
88}
89
90#[cfg(test)]
91mod tests {
92 use std::collections::HashSet;
93
94 use super::*;
95
96 #[test]
97 fn exposes_cedar_hitl_gate_for_formations() {
98 let capability = find_formation_capability(HITL_GATE_CAPABILITY_ID)
99 .expect("HITL capability should be registered");
100
101 assert_eq!(capability.pack_id, ARBITER_FORMATION_PACK_ID);
102 assert_eq!(capability.kind, ArbiterFormationCapabilityKind::RuntimeGate);
103 assert_eq!(capability.suggestor, Some("cedar-hitl-gate"));
104 assert_eq!(capability.evidence_tier, "decided");
105 }
106
107 #[test]
108 fn exposes_cedar_analysis_suggestor_for_formations() {
109 let capability = find_formation_capability(ANALYSIS_EVIDENCE_CAPABILITY_ID)
110 .expect("analysis capability should be registered");
111
112 assert_eq!(capability.pack_id, ARBITER_FORMATION_PACK_ID);
113 assert_eq!(
114 capability.kind,
115 ArbiterFormationCapabilityKind::AnalysisEvidence
116 );
117 assert_eq!(capability.suggestor, Some("cedar-analysis"));
118 assert_eq!(capability.evidence_tier, "searched");
119 }
120
121 #[test]
122 fn capability_ids_are_unique() {
123 let mut ids = HashSet::new();
124
125 for capability in formation_capabilities() {
126 assert!(
127 ids.insert(capability.id),
128 "duplicate capability id: {}",
129 capability.id
130 );
131 }
132 }
133}