Skip to main content

chio_governance/
evaluation.rs

1use crate::generic::{
2    GenericGovernanceCaseArtifact, GenericGovernanceCaseEvaluation,
3    GenericGovernanceCaseEvaluationRequest, GenericGovernanceCaseKind, GenericGovernanceCaseState,
4    GenericGovernanceEffectiveState, GenericGovernanceFinding, GenericGovernanceFindingCode,
5};
6use crate::listing::normalize_namespace;
7
8pub fn evaluate_generic_governance_case(
9    request: &GenericGovernanceCaseEvaluationRequest,
10    now: u64,
11) -> Result<GenericGovernanceCaseEvaluation, String> {
12    request.validate()?;
13    let evaluated_at = request.evaluated_at.unwrap_or(now);
14
15    if !request
16        .listing
17        .verify_signature()
18        .map_err(|error| error.to_string())?
19    {
20        return Ok(governance_failure(
21            request,
22            evaluated_at,
23            GenericGovernanceFindingCode::ListingUnverifiable,
24            "listing signature is invalid",
25        ));
26    }
27    if !request
28        .charter
29        .verify_signature()
30        .map_err(|error| error.to_string())?
31    {
32        return Ok(governance_failure(
33            request,
34            evaluated_at,
35            GenericGovernanceFindingCode::CharterUnverifiable,
36            "governance charter signature is invalid",
37        ));
38    }
39    if let Err(error) = request.charter.body.validate() {
40        return Ok(governance_failure(
41            request,
42            evaluated_at,
43            GenericGovernanceFindingCode::CharterUnverifiable,
44            &error,
45        ));
46    }
47    if !request
48        .case
49        .verify_signature()
50        .map_err(|error| error.to_string())?
51    {
52        return Ok(governance_failure(
53            request,
54            evaluated_at,
55            GenericGovernanceFindingCode::CaseUnverifiable,
56            "governance case signature is invalid",
57        ));
58    }
59    if let Err(error) = request.case.body.validate() {
60        return Ok(governance_failure(
61            request,
62            evaluated_at,
63            GenericGovernanceFindingCode::CaseUnverifiable,
64            &error,
65        ));
66    }
67    if let Some(activation) = request.activation.as_ref() {
68        if !activation
69            .verify_signature()
70            .map_err(|error| error.to_string())?
71        {
72            return Ok(governance_failure(
73                request,
74                evaluated_at,
75                GenericGovernanceFindingCode::ActivationUnverifiable,
76                "trust activation signature is invalid",
77            ));
78        }
79        if let Err(error) = activation.body.validate() {
80            return Ok(governance_failure(
81                request,
82                evaluated_at,
83                GenericGovernanceFindingCode::ActivationUnverifiable,
84                &error,
85            ));
86        }
87    }
88    if let Some(prior_case) = request.prior_case.as_ref() {
89        if !prior_case
90            .verify_signature()
91            .map_err(|error| error.to_string())?
92        {
93            return Ok(governance_failure(
94                request,
95                evaluated_at,
96                GenericGovernanceFindingCode::PriorCaseUnverifiable,
97                "prior governance case signature is invalid",
98            ));
99        }
100        if let Err(error) = prior_case.body.validate() {
101            return Ok(governance_failure(
102                request,
103                evaluated_at,
104                GenericGovernanceFindingCode::PriorCaseUnverifiable,
105                &error,
106            ));
107        }
108    }
109    if let Some(activation) = request.activation.as_ref() {
110        if activation.body.local_operator_id != request.charter.body.governing_operator_id {
111            return Ok(governance_failure(
112                request,
113                evaluated_at,
114                GenericGovernanceFindingCode::ActivationMismatch,
115                "governance cases require a trust activation issued by the governing operator",
116            ));
117        }
118    }
119
120    let charter = &request.charter.body;
121    let case = &request.case.body;
122    let listing = &request.listing.body;
123    let namespace = normalize_namespace(&listing.namespace);
124
125    if charter.governing_operator_id != case.governing_operator_id
126        || charter.charter_id != case.charter_id
127        || normalize_namespace(&charter.authority_scope.namespace) != namespace
128        || normalize_namespace(&case.namespace) != namespace
129        || case.listing_id != listing.listing_id
130    {
131        return Ok(governance_failure(
132            request,
133            evaluated_at,
134            GenericGovernanceFindingCode::CaseMismatch,
135            "governance charter or case does not match the current listing identity or namespace",
136        ));
137    }
138
139    if charter
140        .expires_at
141        .is_some_and(|expires_at| expires_at <= evaluated_at)
142    {
143        return Ok(governance_failure(
144            request,
145            evaluated_at,
146            GenericGovernanceFindingCode::CharterExpired,
147            "governance charter has expired",
148        ));
149    }
150    if case
151        .expires_at
152        .is_some_and(|expires_at| expires_at <= evaluated_at)
153    {
154        return Ok(governance_failure(
155            request,
156            evaluated_at,
157            GenericGovernanceFindingCode::CaseExpired,
158            "governance case has expired",
159        ));
160    }
161    if !charter.allowed_case_kinds.contains(&case.kind) {
162        return Ok(governance_failure(
163            request,
164            evaluated_at,
165            GenericGovernanceFindingCode::CharterKindUnsupported,
166            "governance charter does not authorize this case kind",
167        ));
168    }
169    if !charter
170        .authority_scope
171        .allowed_listing_operator_ids
172        .is_empty()
173        && !charter
174            .authority_scope
175            .allowed_listing_operator_ids
176            .contains(&request.current_publisher.operator_id)
177    {
178        return Ok(governance_failure(
179            request,
180            evaluated_at,
181            GenericGovernanceFindingCode::CharterScopeMismatch,
182            "current listing publisher falls outside the charter authority scope",
183        ));
184    }
185    if !charter.authority_scope.allowed_actor_kinds.is_empty()
186        && !charter
187            .authority_scope
188            .allowed_actor_kinds
189            .contains(&listing.subject.actor_kind)
190    {
191        return Ok(governance_failure(
192            request,
193            evaluated_at,
194            GenericGovernanceFindingCode::CharterScopeMismatch,
195            "listing actor kind falls outside the charter authority scope",
196        ));
197    }
198
199    if matches!(
200        case.kind,
201        GenericGovernanceCaseKind::Freeze | GenericGovernanceCaseKind::Sanction
202    ) {
203        let Some(activation) = request.activation.as_ref() else {
204            return Ok(governance_failure(
205                request,
206                evaluated_at,
207                GenericGovernanceFindingCode::MissingActivation,
208                "freeze or sanction cases require an explicit local trust activation",
209            ));
210        };
211        if case.activation_id.as_deref() != Some(activation.body.activation_id.as_str()) {
212            return Ok(governance_failure(
213                request,
214                evaluated_at,
215                GenericGovernanceFindingCode::ActivationMismatch,
216                "governance case activation does not match the provided trust activation",
217            ));
218        }
219    }
220
221    if let Some(supersedes_case_id) = case.supersedes_case_id.as_deref() {
222        let Some(prior_case) = request.prior_case.as_ref() else {
223            return Ok(governance_failure(
224                request,
225                evaluated_at,
226                GenericGovernanceFindingCode::SupersessionTargetMissing,
227                "superseding governance case requires prior_case",
228            ));
229        };
230        if prior_case.body.case_id != supersedes_case_id
231            || normalize_namespace(&prior_case.body.namespace) != namespace
232            || prior_case.body.listing_id != listing.listing_id
233        {
234            return Ok(governance_failure(
235                request,
236                evaluated_at,
237                GenericGovernanceFindingCode::SupersessionTargetInvalid,
238                "supersession target does not match the referenced prior governance case",
239            ));
240        }
241    }
242
243    if matches!(case.kind, GenericGovernanceCaseKind::Appeal) {
244        let Some(appeal_of_case_id) = case.appeal_of_case_id.as_deref() else {
245            return Ok(governance_failure(
246                request,
247                evaluated_at,
248                GenericGovernanceFindingCode::AppealTargetMissing,
249                "appeal case requires appeal_of_case_id",
250            ));
251        };
252        let Some(prior_case) = request.prior_case.as_ref() else {
253            return Ok(governance_failure(
254                request,
255                evaluated_at,
256                GenericGovernanceFindingCode::AppealTargetMissing,
257                "appeal case requires prior_case",
258            ));
259        };
260        if prior_case.body.case_id != appeal_of_case_id
261            || normalize_namespace(&prior_case.body.namespace) != namespace
262            || prior_case.body.listing_id != listing.listing_id
263            || matches!(prior_case.body.kind, GenericGovernanceCaseKind::Appeal)
264        {
265            return Ok(governance_failure(
266                request,
267                evaluated_at,
268                GenericGovernanceFindingCode::AppealTargetInvalid,
269                "appeal target does not match a valid prior governance case",
270            ));
271        }
272    }
273
274    let (effective_state, blocks_admission) = effective_state_for_case(case);
275    Ok(GenericGovernanceCaseEvaluation {
276        listing_id: listing.listing_id.clone(),
277        namespace,
278        charter_id: charter.charter_id.clone(),
279        case_id: case.case_id.clone(),
280        governing_operator_id: case.governing_operator_id.clone(),
281        kind: case.kind,
282        state: case.state,
283        effective_state,
284        evaluated_at,
285        blocks_admission,
286        findings: Vec::new(),
287    })
288}
289
290pub(crate) fn effective_state_for_case(
291    case: &GenericGovernanceCaseArtifact,
292) -> (GenericGovernanceEffectiveState, bool) {
293    match case.state {
294        GenericGovernanceCaseState::Resolved
295        | GenericGovernanceCaseState::Denied
296        | GenericGovernanceCaseState::Superseded => (GenericGovernanceEffectiveState::Clear, false),
297        GenericGovernanceCaseState::Open | GenericGovernanceCaseState::Escalated => match case.kind
298        {
299            GenericGovernanceCaseKind::Dispute => {
300                (GenericGovernanceEffectiveState::Disputed, false)
301            }
302            GenericGovernanceCaseKind::Appeal => (GenericGovernanceEffectiveState::Appealed, false),
303            GenericGovernanceCaseKind::Freeze => (GenericGovernanceEffectiveState::Frozen, false),
304            GenericGovernanceCaseKind::Sanction => {
305                (GenericGovernanceEffectiveState::Sanctioned, false)
306            }
307        },
308        GenericGovernanceCaseState::Enforced => match case.kind {
309            GenericGovernanceCaseKind::Dispute => {
310                (GenericGovernanceEffectiveState::Disputed, false)
311            }
312            GenericGovernanceCaseKind::Appeal => (GenericGovernanceEffectiveState::Appealed, false),
313            GenericGovernanceCaseKind::Freeze => (GenericGovernanceEffectiveState::Frozen, true),
314            GenericGovernanceCaseKind::Sanction => {
315                (GenericGovernanceEffectiveState::Sanctioned, true)
316            }
317        },
318    }
319}
320
321pub(crate) fn governance_failure(
322    request: &GenericGovernanceCaseEvaluationRequest,
323    evaluated_at: u64,
324    code: GenericGovernanceFindingCode,
325    message: &str,
326) -> GenericGovernanceCaseEvaluation {
327    GenericGovernanceCaseEvaluation {
328        listing_id: request.listing.body.listing_id.clone(),
329        namespace: request.listing.body.namespace.clone(),
330        charter_id: request.case.body.charter_id.clone(),
331        case_id: request.case.body.case_id.clone(),
332        governing_operator_id: request.case.body.governing_operator_id.clone(),
333        kind: request.case.body.kind,
334        state: request.case.body.state,
335        effective_state: GenericGovernanceEffectiveState::Clear,
336        evaluated_at,
337        blocks_admission: false,
338        findings: vec![GenericGovernanceFinding {
339            code,
340            message: message.to_string(),
341        }],
342    }
343}