Skip to main content

chio_autonomy/
lib.rs

1//! Chio autonomous pricing, capital optimization, and fail-safe automation types.
2//!
3//! These contracts extend the delegated underwriting, market, capital, and
4//! web3 surfaces into one bounded automation layer. The automation layer
5//! remains evidence-referential: it carries explicit references back to prior
6//! Chio truth rather than replacing those signed artifacts.
7
8pub use chio_core_types::{capability, receipt};
9pub use chio_market as market;
10pub use chio_web3 as web3;
11
12use std::collections::HashSet;
13
14use serde::{Deserialize, Serialize};
15
16use crate::capability::{MonetaryAmount, RuntimeAssuranceTier};
17use crate::market::LiabilityCoverageClass;
18use crate::receipt::SignedExportEnvelope;
19use crate::web3::Web3SettlementLifecycleState;
20
21pub const CHIO_AUTONOMOUS_PRICING_INPUT_SCHEMA: &str = "chio.autonomous-pricing-input.v1";
22pub const CHIO_AUTONOMOUS_PRICING_AUTHORITY_ENVELOPE_SCHEMA: &str =
23    "chio.autonomous-pricing-authority-envelope.v1";
24pub const CHIO_AUTONOMOUS_PRICING_DECISION_SCHEMA: &str = "chio.autonomous-pricing-decision.v1";
25pub const CHIO_CAPITAL_POOL_OPTIMIZATION_SCHEMA: &str = "chio.capital-pool-optimization.v1";
26pub const CHIO_CAPITAL_POOL_SIMULATION_REPORT_SCHEMA: &str =
27    "chio.capital-pool-simulation-report.v1";
28pub const CHIO_AUTONOMOUS_EXECUTION_DECISION_SCHEMA: &str = "chio.autonomous-execution-decision.v1";
29pub const CHIO_AUTONOMOUS_ROLLBACK_PLAN_SCHEMA: &str = "chio.autonomous-rollback-plan.v1";
30pub const CHIO_AUTONOMOUS_COMPARISON_REPORT_SCHEMA: &str = "chio.autonomous-comparison-report.v1";
31pub const CHIO_AUTONOMOUS_DRIFT_REPORT_SCHEMA: &str = "chio.autonomous-drift-report.v1";
32pub const CHIO_AUTONOMOUS_QUALIFICATION_MATRIX_SCHEMA: &str =
33    "chio.autonomous-qualification-matrix.v1";
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
36#[serde(rename_all = "snake_case")]
37pub enum AutonomousEvidenceKind {
38    UnderwritingDecision,
39    ExposureLedger,
40    CreditScorecard,
41    CapitalBook,
42    CreditFacility,
43    CreditLossLifecycle,
44    Web3SettlementReceipt,
45    LiabilityQuoteResponse,
46    LiabilityAutoBindDecision,
47    ClaimWorkflow,
48    RuntimeAssuranceAppraisal,
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
52#[serde(rename_all = "snake_case")]
53pub enum AutonomousPricingAction {
54    Reprice,
55    Renew,
56    Decline,
57    Bind,
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
61#[serde(rename_all = "snake_case")]
62pub enum AutonomousPricingDisposition {
63    Reprice,
64    Renew,
65    Decline,
66    BindWithinEnvelope,
67    ManualReview,
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
71#[serde(rename_all = "snake_case")]
72pub enum AutonomousDecisionReviewState {
73    AutoApproved,
74    HumanReviewRequired,
75    ShadowOnly,
76    Blocked,
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
80#[serde(rename_all = "snake_case")]
81pub enum AutonomousAutomationMode {
82    Shadow,
83    Advisory,
84    Active,
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
88#[serde(rename_all = "snake_case")]
89pub enum AutonomousAuthorityEnvelopeKind {
90    OperatorPolicy,
91    RegulatedRole,
92    DelegatedMarketAuthority,
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
96#[serde(rename_all = "snake_case")]
97pub enum AutonomousPricingExplanationDirection {
98    Increase,
99    Decrease,
100    Hold,
101    Escalate,
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
105#[serde(rename_all = "snake_case")]
106pub enum CapitalOptimizationAction {
107    IncreaseReserve,
108    DecreaseReserve,
109    ShiftCapacity,
110    HoldCapacity,
111    DeferClaim,
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
115#[serde(rename_all = "snake_case")]
116pub enum CapitalPoolSimulationMode {
117    WhatIf,
118    Shadow,
119}
120
121#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
122#[serde(rename_all = "snake_case")]
123pub enum AutonomousExecutionAction {
124    Reprice,
125    Renew,
126    Decline,
127    Bind,
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
131#[serde(rename_all = "snake_case")]
132pub enum AutonomousExecutionLifecycleState {
133    Prepared,
134    Executed,
135    Interrupted,
136    RolledBack,
137    Blocked,
138}
139
140#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
141#[serde(rename_all = "snake_case")]
142pub enum AutonomousDriftKind {
143    LossRatioSpike,
144    PremiumVariance,
145    CapitalUtilization,
146    SettlementFailureRate,
147    OverrideRate,
148    ModelVersionMismatch,
149    EvidenceStaleness,
150}
151
152#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
153#[serde(rename_all = "snake_case")]
154pub enum AutonomousDriftSeverity {
155    Warning,
156    Critical,
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
160#[serde(rename_all = "snake_case")]
161pub enum AutonomousSafeState {
162    ShadowModeOnly,
163    DelegatedOnly,
164    BindDisabled,
165    FullPause,
166}
167
168#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
169#[serde(rename_all = "snake_case")]
170pub enum AutonomousRollbackAction {
171    SwitchToSafeState,
172    CancelPendingExecution,
173    RequireHumanApproval,
174    RevertToDelegatedAuthority,
175    FreezeModelVersion,
176}
177
178#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
179#[serde(rename_all = "snake_case")]
180pub enum AutonomousComparisonDisposition {
181    Match,
182    NarrowerThanManual,
183    WiderThanManual,
184    ManualOverride,
185}
186
187#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
188#[serde(rename_all = "snake_case")]
189pub enum AutonomousQualificationOutcome {
190    Pass,
191    FailClosed,
192}
193
194#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
195#[serde(deny_unknown_fields)]
196pub struct AutonomousEvidenceReference {
197    pub kind: AutonomousEvidenceKind,
198    pub reference_id: String,
199    #[serde(default, skip_serializing_if = "Option::is_none")]
200    pub observed_at: Option<u64>,
201    #[serde(default, skip_serializing_if = "Option::is_none")]
202    pub locator: Option<String>,
203}
204
205#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
206#[serde(deny_unknown_fields)]
207pub struct AutonomousPricingSupportBoundary {
208    pub delegated_authority_required: bool,
209    pub live_bind_supported: bool,
210    pub reserve_optimization_required: bool,
211    pub operator_override_supported: bool,
212}
213
214impl Default for AutonomousPricingSupportBoundary {
215    fn default() -> Self {
216        Self {
217            delegated_authority_required: true,
218            live_bind_supported: true,
219            reserve_optimization_required: true,
220            operator_override_supported: true,
221        }
222    }
223}
224
225#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
226#[serde(deny_unknown_fields)]
227pub struct AutonomousModelProvenance {
228    pub model_id: String,
229    pub model_version: String,
230    pub engine_family: String,
231    pub published_at: u64,
232    pub training_cutoff: u64,
233    pub input_hash: String,
234    pub explanation_version: String,
235    pub supports_counterfactuals: bool,
236    pub supports_shadow_evaluation: bool,
237}
238
239#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
240#[serde(deny_unknown_fields)]
241pub struct AutonomousPricingInputArtifact {
242    pub schema: String,
243    pub input_id: String,
244    pub generated_at: u64,
245    pub subject_key: String,
246    pub provider_id: String,
247    pub coverage_class: LiabilityCoverageClass,
248    pub currency: String,
249    pub requested_coverage_amount: MonetaryAmount,
250    pub receipt_history_window_secs: u64,
251    pub reputation_score_bps: u32,
252    pub runtime_assurance_tier: RuntimeAssuranceTier,
253    pub pending_loss_units: u64,
254    pub settled_loss_units: u64,
255    pub available_capital_units: u64,
256    #[serde(default, skip_serializing_if = "Option::is_none")]
257    pub latest_web3_settlement_state: Option<Web3SettlementLifecycleState>,
258    pub evidence_refs: Vec<AutonomousEvidenceReference>,
259    pub support_boundary: AutonomousPricingSupportBoundary,
260    #[serde(default, skip_serializing_if = "Option::is_none")]
261    pub note: Option<String>,
262}
263
264pub type SignedAutonomousPricingInput = SignedExportEnvelope<AutonomousPricingInputArtifact>;
265
266#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
267#[serde(deny_unknown_fields)]
268pub struct AutonomousPricingAuthorityEnvelopeArtifact {
269    pub schema: String,
270    pub envelope_id: String,
271    pub issued_at: u64,
272    pub subject_key: String,
273    pub provider_id: String,
274    pub currency: String,
275    pub kind: AutonomousAuthorityEnvelopeKind,
276    pub automation_mode: AutonomousAutomationMode,
277    pub permitted_actions: Vec<AutonomousPricingAction>,
278    pub authority_chain_refs: Vec<String>,
279    pub max_coverage_amount: MonetaryAmount,
280    pub max_premium_amount: MonetaryAmount,
281    pub max_rate_change_bps: u32,
282    pub max_daily_decisions: u32,
283    pub requires_human_review_for_bind: bool,
284    #[serde(default, skip_serializing_if = "Option::is_none")]
285    pub requires_human_review_above_premium: Option<MonetaryAmount>,
286    #[serde(default, skip_serializing_if = "Option::is_none")]
287    pub regulated_role: Option<String>,
288    #[serde(default, skip_serializing_if = "Option::is_none")]
289    pub delegated_authority_reference: Option<String>,
290    pub not_before: u64,
291    pub not_after: u64,
292    pub support_boundary: AutonomousPricingSupportBoundary,
293    #[serde(default, skip_serializing_if = "Option::is_none")]
294    pub note: Option<String>,
295}
296
297pub type SignedAutonomousPricingAuthorityEnvelope =
298    SignedExportEnvelope<AutonomousPricingAuthorityEnvelopeArtifact>;
299
300#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
301#[serde(deny_unknown_fields)]
302pub struct AutonomousPricingExplanationFactor {
303    pub code: String,
304    pub description: String,
305    pub direction: AutonomousPricingExplanationDirection,
306    pub weight_bps: u32,
307    #[serde(default, skip_serializing_if = "Vec::is_empty")]
308    pub evidence_refs: Vec<AutonomousEvidenceReference>,
309}
310
311#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
312#[serde(deny_unknown_fields)]
313pub struct AutonomousPricingDecisionArtifact {
314    pub schema: String,
315    pub decision_id: String,
316    pub issued_at: u64,
317    pub pricing_input: AutonomousPricingInputArtifact,
318    pub model: AutonomousModelProvenance,
319    pub authority_envelope: AutonomousPricingAuthorityEnvelopeArtifact,
320    pub disposition: AutonomousPricingDisposition,
321    pub review_state: AutonomousDecisionReviewState,
322    pub suggested_coverage_amount: MonetaryAmount,
323    pub suggested_premium_amount: MonetaryAmount,
324    #[serde(default, skip_serializing_if = "Option::is_none")]
325    pub suggested_ceiling_factor_bps: Option<u32>,
326    pub confidence_bps: u32,
327    pub explanation_factors: Vec<AutonomousPricingExplanationFactor>,
328    #[serde(default, skip_serializing_if = "Option::is_none")]
329    pub comparison_baseline_ref: Option<String>,
330    #[serde(default, skip_serializing_if = "Option::is_none")]
331    pub note: Option<String>,
332}
333
334pub type SignedAutonomousPricingDecision = SignedExportEnvelope<AutonomousPricingDecisionArtifact>;
335
336#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
337#[serde(deny_unknown_fields)]
338pub struct CapitalPoolOptimizationSupportBoundary {
339    pub live_mutation_supported: bool,
340    pub scenario_comparison_supported: bool,
341    pub cross_currency_optimization_supported: bool,
342    pub web3_reconciliation_required: bool,
343    pub operator_override_required: bool,
344}
345
346impl Default for CapitalPoolOptimizationSupportBoundary {
347    fn default() -> Self {
348        Self {
349            live_mutation_supported: false,
350            scenario_comparison_supported: true,
351            cross_currency_optimization_supported: false,
352            web3_reconciliation_required: true,
353            operator_override_required: true,
354        }
355    }
356}
357
358#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
359#[serde(deny_unknown_fields)]
360pub struct CapitalPoolRecommendation {
361    pub action: CapitalOptimizationAction,
362    pub source_ref: String,
363    #[serde(default, skip_serializing_if = "Option::is_none")]
364    pub destination_ref: Option<String>,
365    pub amount: MonetaryAmount,
366    pub rationale: String,
367}
368
369#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
370#[serde(deny_unknown_fields)]
371pub struct CapitalPoolOptimizationArtifact {
372    pub schema: String,
373    pub optimization_id: String,
374    pub issued_at: u64,
375    pub subject_key: String,
376    pub currency: String,
377    pub pricing_decision_ref: String,
378    pub capital_book_ref: String,
379    pub facility_refs: Vec<String>,
380    #[serde(default, skip_serializing_if = "Vec::is_empty")]
381    pub pending_claim_refs: Vec<String>,
382    pub target_reserve_ratio_bps: u32,
383    pub max_facility_utilization_bps: u32,
384    pub max_bind_capacity_units: u64,
385    pub recommendations: Vec<CapitalPoolRecommendation>,
386    pub support_boundary: CapitalPoolOptimizationSupportBoundary,
387    #[serde(default, skip_serializing_if = "Option::is_none")]
388    pub note: Option<String>,
389}
390
391pub type SignedCapitalPoolOptimization = SignedExportEnvelope<CapitalPoolOptimizationArtifact>;
392
393#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
394#[serde(deny_unknown_fields)]
395pub struct CapitalPoolSimulationDelta {
396    pub metric_name: String,
397    pub baseline_units: u64,
398    pub candidate_units: u64,
399    pub description: String,
400}
401
402#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
403#[serde(deny_unknown_fields)]
404pub struct CapitalPoolSimulationReport {
405    pub schema: String,
406    pub simulation_id: String,
407    pub generated_at: u64,
408    pub subject_key: String,
409    pub currency: String,
410    pub baseline_optimization: CapitalPoolOptimizationArtifact,
411    pub candidate_optimization: CapitalPoolOptimizationArtifact,
412    pub simulation_mode: CapitalPoolSimulationMode,
413    pub deltas: Vec<CapitalPoolSimulationDelta>,
414    pub recommended_operator_action: String,
415    #[serde(default, skip_serializing_if = "Option::is_none")]
416    pub note: Option<String>,
417}
418
419pub type SignedCapitalPoolSimulationReport = SignedExportEnvelope<CapitalPoolSimulationReport>;
420
421#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
422#[serde(deny_unknown_fields)]
423pub struct AutonomousExecutionSafetyGate {
424    pub name: String,
425    pub passed: bool,
426    pub description: String,
427}
428
429#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
430#[serde(deny_unknown_fields)]
431pub struct AutonomousExecutionRollbackControl {
432    pub rollback_plan_ref: String,
433    pub interruptible: bool,
434    pub human_interrupt_contact: String,
435}
436
437#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
438#[serde(deny_unknown_fields)]
439pub struct AutonomousExecutionDecisionArtifact {
440    pub schema: String,
441    pub execution_id: String,
442    pub issued_at: u64,
443    pub pricing_decision_ref: String,
444    pub optimization_ref: String,
445    pub authority_envelope_ref: String,
446    pub subject_key: String,
447    pub provider_id: String,
448    pub currency: String,
449    pub action: AutonomousExecutionAction,
450    pub lifecycle_state: AutonomousExecutionLifecycleState,
451    #[serde(default, skip_serializing_if = "Option::is_none")]
452    pub quote_response_ref: Option<String>,
453    #[serde(default, skip_serializing_if = "Option::is_none")]
454    pub auto_bind_decision_ref: Option<String>,
455    #[serde(default, skip_serializing_if = "Option::is_none")]
456    pub bound_coverage_ref: Option<String>,
457    #[serde(default, skip_serializing_if = "Option::is_none")]
458    pub settlement_dispatch_ref: Option<String>,
459    pub safety_gates: Vec<AutonomousExecutionSafetyGate>,
460    pub rollback_control: AutonomousExecutionRollbackControl,
461    #[serde(default, skip_serializing_if = "Option::is_none")]
462    pub note: Option<String>,
463}
464
465pub type SignedAutonomousExecutionDecision =
466    SignedExportEnvelope<AutonomousExecutionDecisionArtifact>;
467
468#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
469#[serde(deny_unknown_fields)]
470pub struct AutonomousRollbackPlanArtifact {
471    pub schema: String,
472    pub plan_id: String,
473    pub issued_at: u64,
474    pub subject_key: String,
475    pub safe_state: AutonomousSafeState,
476    pub triggers: Vec<AutonomousDriftKind>,
477    pub actions: Vec<AutonomousRollbackAction>,
478    pub requires_operator_ack: bool,
479    #[serde(default, skip_serializing_if = "Option::is_none")]
480    pub note: Option<String>,
481}
482
483pub type SignedAutonomousRollbackPlan = SignedExportEnvelope<AutonomousRollbackPlanArtifact>;
484
485#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
486#[serde(deny_unknown_fields)]
487pub struct AutonomousComparisonDelta {
488    pub field: String,
489    pub automated_value: String,
490    pub manual_value: String,
491    pub description: String,
492}
493
494#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
495#[serde(deny_unknown_fields)]
496pub struct AutonomousComparisonReport {
497    pub schema: String,
498    pub comparison_id: String,
499    pub generated_at: u64,
500    pub pricing_decision_ref: String,
501    pub manual_decision_ref: String,
502    pub disposition: AutonomousComparisonDisposition,
503    #[serde(default, skip_serializing_if = "Vec::is_empty")]
504    pub deltas: Vec<AutonomousComparisonDelta>,
505    #[serde(default, skip_serializing_if = "Option::is_none")]
506    pub override_reference: Option<String>,
507    #[serde(default, skip_serializing_if = "Option::is_none")]
508    pub note: Option<String>,
509}
510
511pub type SignedAutonomousComparisonReport = SignedExportEnvelope<AutonomousComparisonReport>;
512
513#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
514#[serde(deny_unknown_fields)]
515pub struct AutonomousDriftSignal {
516    pub kind: AutonomousDriftKind,
517    pub severity: AutonomousDriftSeverity,
518    pub metric_name: String,
519    pub observed_value: u64,
520    pub threshold_value: u64,
521    pub description: String,
522    #[serde(default, skip_serializing_if = "Vec::is_empty")]
523    pub evidence_refs: Vec<AutonomousEvidenceReference>,
524}
525
526#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
527#[serde(deny_unknown_fields)]
528pub struct AutonomousDriftReport {
529    pub schema: String,
530    pub drift_report_id: String,
531    pub generated_at: u64,
532    pub subject_key: String,
533    pub pricing_decision_ref: String,
534    pub optimization_ref: String,
535    pub drift_signals: Vec<AutonomousDriftSignal>,
536    pub rollback_plan: AutonomousRollbackPlanArtifact,
537    pub comparison_report: AutonomousComparisonReport,
538    pub fail_safe_engaged: bool,
539    #[serde(default, skip_serializing_if = "Option::is_none")]
540    pub note: Option<String>,
541}
542
543pub type SignedAutonomousDriftReport = SignedExportEnvelope<AutonomousDriftReport>;
544
545#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
546#[serde(deny_unknown_fields)]
547pub struct AutonomousQualificationCase {
548    pub id: String,
549    pub name: String,
550    pub requirement_ids: Vec<String>,
551    #[serde(default, skip_serializing_if = "Option::is_none")]
552    pub drift_kind: Option<AutonomousDriftKind>,
553    pub expected_outcome: AutonomousQualificationOutcome,
554    pub observed_outcome: AutonomousQualificationOutcome,
555    pub notes: String,
556}
557
558#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
559#[serde(deny_unknown_fields)]
560pub struct AutonomousQualificationMatrix {
561    pub schema: String,
562    pub profile_id: String,
563    pub cases: Vec<AutonomousQualificationCase>,
564}
565
566#[derive(Debug, thiserror::Error, PartialEq, Eq)]
567pub enum AutonomyContractError {
568    #[error("unsupported schema: {0}")]
569    UnsupportedSchema(String),
570
571    #[error("missing field: {0}")]
572    MissingField(&'static str),
573
574    #[error("duplicate value: {0}")]
575    DuplicateValue(String),
576
577    #[error("unknown reference: {0}")]
578    UnknownReference(String),
579
580    #[error("invalid envelope: {0}")]
581    InvalidEnvelope(String),
582
583    #[error("invalid decision: {0}")]
584    InvalidDecision(String),
585
586    #[error("invalid optimization: {0}")]
587    InvalidOptimization(String),
588
589    #[error("invalid execution: {0}")]
590    InvalidExecution(String),
591
592    #[error("invalid drift: {0}")]
593    InvalidDrift(String),
594
595    #[error("invalid qualification case: {0}")]
596    InvalidQualificationCase(String),
597}
598
599pub fn validate_autonomous_pricing_input(
600    input: &AutonomousPricingInputArtifact,
601) -> Result<(), AutonomyContractError> {
602    if input.schema != CHIO_AUTONOMOUS_PRICING_INPUT_SCHEMA {
603        return Err(AutonomyContractError::UnsupportedSchema(
604            input.schema.clone(),
605        ));
606    }
607    ensure_non_empty(&input.input_id, "autonomous_pricing_input.input_id")?;
608    ensure_non_empty(&input.subject_key, "autonomous_pricing_input.subject_key")?;
609    ensure_non_empty(&input.provider_id, "autonomous_pricing_input.provider_id")?;
610    validate_currency_code(&input.currency, "autonomous_pricing_input.currency")?;
611    validate_positive_money(
612        &input.requested_coverage_amount,
613        "autonomous_pricing_input.requested_coverage_amount",
614    )?;
615    if input
616        .requested_coverage_amount
617        .currency
618        .trim()
619        .to_ascii_uppercase()
620        != input.currency
621    {
622        return Err(AutonomyContractError::InvalidDecision(
623            "autonomous pricing input coverage amount currency must match currency".to_string(),
624        ));
625    }
626    if input.receipt_history_window_secs == 0 {
627        return Err(AutonomyContractError::InvalidDecision(
628            "autonomous pricing input receipt_history_window_secs must be non-zero".to_string(),
629        ));
630    }
631    if input.reputation_score_bps > 10_000 {
632        return Err(AutonomyContractError::InvalidDecision(
633            "autonomous pricing input reputation_score_bps must be <= 10000".to_string(),
634        ));
635    }
636    if input.available_capital_units == 0 {
637        return Err(AutonomyContractError::InvalidDecision(
638            "autonomous pricing input available_capital_units must be non-zero".to_string(),
639        ));
640    }
641    if input.evidence_refs.is_empty() {
642        return Err(AutonomyContractError::MissingField(
643            "autonomous_pricing_input.evidence_refs",
644        ));
645    }
646
647    let mut ids = HashSet::new();
648    let mut kinds = HashSet::new();
649    for evidence in &input.evidence_refs {
650        ensure_non_empty(
651            &evidence.reference_id,
652            "autonomous_pricing_input.evidence_refs.reference_id",
653        )?;
654        if !ids.insert(evidence.reference_id.as_str()) {
655            return Err(AutonomyContractError::DuplicateValue(
656                evidence.reference_id.clone(),
657            ));
658        }
659        kinds.insert(evidence.kind);
660    }
661    for required in [
662        AutonomousEvidenceKind::UnderwritingDecision,
663        AutonomousEvidenceKind::ExposureLedger,
664        AutonomousEvidenceKind::CreditScorecard,
665        AutonomousEvidenceKind::CapitalBook,
666    ] {
667        if !kinds.contains(&required) {
668            return Err(AutonomyContractError::UnknownReference(format!(
669                "autonomous pricing input missing required evidence {:?}",
670                required
671            )));
672        }
673    }
674    if (input.pending_loss_units > 0 || input.settled_loss_units > 0)
675        && !kinds.contains(&AutonomousEvidenceKind::CreditLossLifecycle)
676    {
677        return Err(AutonomyContractError::UnknownReference(
678            "autonomous pricing input with loss units must include credit_loss_lifecycle evidence"
679                .to_string(),
680        ));
681    }
682    if input.latest_web3_settlement_state.is_some()
683        && !kinds.contains(&AutonomousEvidenceKind::Web3SettlementReceipt)
684    {
685        return Err(AutonomyContractError::UnknownReference(
686            "autonomous pricing input with latest_web3_settlement_state must include web3_settlement_receipt evidence"
687                .to_string(),
688        ));
689    }
690    Ok(())
691}
692
693pub fn validate_autonomous_pricing_authority_envelope(
694    envelope: &AutonomousPricingAuthorityEnvelopeArtifact,
695) -> Result<(), AutonomyContractError> {
696    if envelope.schema != CHIO_AUTONOMOUS_PRICING_AUTHORITY_ENVELOPE_SCHEMA {
697        return Err(AutonomyContractError::UnsupportedSchema(
698            envelope.schema.clone(),
699        ));
700    }
701    ensure_non_empty(
702        &envelope.envelope_id,
703        "autonomous_authority_envelope.envelope_id",
704    )?;
705    ensure_non_empty(
706        &envelope.subject_key,
707        "autonomous_authority_envelope.subject_key",
708    )?;
709    ensure_non_empty(
710        &envelope.provider_id,
711        "autonomous_authority_envelope.provider_id",
712    )?;
713    validate_currency_code(&envelope.currency, "autonomous_authority_envelope.currency")?;
714    if envelope.permitted_actions.is_empty() {
715        return Err(AutonomyContractError::MissingField(
716            "autonomous_authority_envelope.permitted_actions",
717        ));
718    }
719    ensure_unique_copy_values(
720        &envelope.permitted_actions,
721        "autonomous_authority_envelope.permitted_actions",
722    )?;
723    if envelope.authority_chain_refs.is_empty() {
724        return Err(AutonomyContractError::MissingField(
725            "autonomous_authority_envelope.authority_chain_refs",
726        ));
727    }
728    ensure_unique_strings(
729        &envelope.authority_chain_refs,
730        "autonomous_authority_envelope.authority_chain_refs",
731    )?;
732    validate_positive_money(
733        &envelope.max_coverage_amount,
734        "autonomous_authority_envelope.max_coverage_amount",
735    )?;
736    validate_positive_money(
737        &envelope.max_premium_amount,
738        "autonomous_authority_envelope.max_premium_amount",
739    )?;
740    if envelope
741        .max_coverage_amount
742        .currency
743        .trim()
744        .to_ascii_uppercase()
745        != envelope.currency
746    {
747        return Err(AutonomyContractError::InvalidEnvelope(
748            "autonomous authority max_coverage_amount currency must match envelope currency"
749                .to_string(),
750        ));
751    }
752    if envelope
753        .max_premium_amount
754        .currency
755        .trim()
756        .to_ascii_uppercase()
757        != envelope.currency
758    {
759        return Err(AutonomyContractError::InvalidEnvelope(
760            "autonomous authority max_premium_amount currency must match envelope currency"
761                .to_string(),
762        ));
763    }
764    if envelope.max_rate_change_bps == 0 || envelope.max_rate_change_bps > 10_000 {
765        return Err(AutonomyContractError::InvalidEnvelope(
766            "autonomous authority max_rate_change_bps must be between 1 and 10000".to_string(),
767        ));
768    }
769    if envelope.max_daily_decisions == 0 {
770        return Err(AutonomyContractError::InvalidEnvelope(
771            "autonomous authority max_daily_decisions must be non-zero".to_string(),
772        ));
773    }
774    if let Some(threshold) = envelope.requires_human_review_above_premium.as_ref() {
775        validate_positive_money(
776            threshold,
777            "autonomous_authority_envelope.requires_human_review_above_premium",
778        )?;
779        if threshold.currency.trim().to_ascii_uppercase() != envelope.currency {
780            return Err(AutonomyContractError::InvalidEnvelope(
781                "autonomous authority premium review threshold currency must match envelope currency"
782                    .to_string(),
783            ));
784        }
785        if threshold.units > envelope.max_premium_amount.units {
786            return Err(AutonomyContractError::InvalidEnvelope(
787                "autonomous authority premium review threshold cannot exceed max_premium_amount"
788                    .to_string(),
789            ));
790        }
791    }
792    if envelope.not_before >= envelope.not_after {
793        return Err(AutonomyContractError::InvalidEnvelope(
794            "autonomous authority not_before must be earlier than not_after".to_string(),
795        ));
796    }
797    match envelope.kind {
798        AutonomousAuthorityEnvelopeKind::OperatorPolicy => {}
799        AutonomousAuthorityEnvelopeKind::RegulatedRole => {
800            ensure_non_empty(
801                envelope.regulated_role.as_deref().unwrap_or_default(),
802                "autonomous_authority_envelope.regulated_role",
803            )?;
804        }
805        AutonomousAuthorityEnvelopeKind::DelegatedMarketAuthority => {
806            ensure_non_empty(
807                envelope
808                    .delegated_authority_reference
809                    .as_deref()
810                    .unwrap_or_default(),
811                "autonomous_authority_envelope.delegated_authority_reference",
812            )?;
813        }
814    }
815    if envelope.automation_mode != AutonomousAutomationMode::Active
816        && envelope
817            .permitted_actions
818            .contains(&AutonomousPricingAction::Bind)
819    {
820        return Err(AutonomyContractError::InvalidEnvelope(
821            "only active automation envelopes may permit bind".to_string(),
822        ));
823    }
824    Ok(())
825}
826
827pub fn validate_autonomous_pricing_decision(
828    decision: &AutonomousPricingDecisionArtifact,
829) -> Result<(), AutonomyContractError> {
830    if decision.schema != CHIO_AUTONOMOUS_PRICING_DECISION_SCHEMA {
831        return Err(AutonomyContractError::UnsupportedSchema(
832            decision.schema.clone(),
833        ));
834    }
835    ensure_non_empty(
836        &decision.decision_id,
837        "autonomous_pricing_decision.decision_id",
838    )?;
839    validate_autonomous_pricing_input(&decision.pricing_input)?;
840    validate_autonomous_pricing_authority_envelope(&decision.authority_envelope)?;
841    validate_model_provenance(&decision.model)?;
842    validate_positive_money(
843        &decision.suggested_coverage_amount,
844        "autonomous_pricing_decision.suggested_coverage_amount",
845    )?;
846    validate_positive_money(
847        &decision.suggested_premium_amount,
848        "autonomous_pricing_decision.suggested_premium_amount",
849    )?;
850    if decision
851        .suggested_coverage_amount
852        .currency
853        .trim()
854        .to_ascii_uppercase()
855        != decision.authority_envelope.currency
856        || decision
857            .suggested_premium_amount
858            .currency
859            .trim()
860            .to_ascii_uppercase()
861            != decision.authority_envelope.currency
862    {
863        return Err(AutonomyContractError::InvalidDecision(
864            "autonomous pricing decision money fields must match envelope currency".to_string(),
865        ));
866    }
867    if decision.pricing_input.subject_key != decision.authority_envelope.subject_key
868        || decision.pricing_input.provider_id != decision.authority_envelope.provider_id
869        || decision.pricing_input.currency != decision.authority_envelope.currency
870    {
871        return Err(AutonomyContractError::InvalidDecision(
872            "autonomous pricing decision input must match authority envelope subject/provider/currency"
873                .to_string(),
874        ));
875    }
876    if decision.suggested_coverage_amount.units
877        > decision.authority_envelope.max_coverage_amount.units
878    {
879        return Err(AutonomyContractError::InvalidDecision(
880            "autonomous pricing decision coverage exceeds authority envelope".to_string(),
881        ));
882    }
883    if decision.suggested_premium_amount.units
884        > decision.authority_envelope.max_premium_amount.units
885    {
886        return Err(AutonomyContractError::InvalidDecision(
887            "autonomous pricing decision premium exceeds authority envelope".to_string(),
888        ));
889    }
890    if let Some(ceiling_factor_bps) = decision.suggested_ceiling_factor_bps {
891        if ceiling_factor_bps == 0 || ceiling_factor_bps > 10_000 {
892            return Err(AutonomyContractError::InvalidDecision(
893                "autonomous pricing decision suggested_ceiling_factor_bps must be between 1 and 10000"
894                    .to_string(),
895            ));
896        }
897    }
898    if decision.confidence_bps == 0 || decision.confidence_bps > 10_000 {
899        return Err(AutonomyContractError::InvalidDecision(
900            "autonomous pricing decision confidence_bps must be between 1 and 10000".to_string(),
901        ));
902    }
903    if decision.explanation_factors.is_empty() {
904        return Err(AutonomyContractError::MissingField(
905            "autonomous_pricing_decision.explanation_factors",
906        ));
907    }
908    let mut factor_codes = HashSet::new();
909    for factor in &decision.explanation_factors {
910        ensure_non_empty(
911            &factor.code,
912            "autonomous_pricing_decision.explanation_factors.code",
913        )?;
914        ensure_non_empty(
915            &factor.description,
916            "autonomous_pricing_decision.explanation_factors.description",
917        )?;
918        if factor.weight_bps == 0 || factor.weight_bps > 10_000 {
919            return Err(AutonomyContractError::InvalidDecision(
920                "autonomous pricing explanation factor weight_bps must be between 1 and 10000"
921                    .to_string(),
922            ));
923        }
924        if !factor_codes.insert(factor.code.as_str()) {
925            return Err(AutonomyContractError::DuplicateValue(factor.code.clone()));
926        }
927    }
928    if decision.authority_envelope.automation_mode == AutonomousAutomationMode::Shadow
929        && decision.review_state != AutonomousDecisionReviewState::ShadowOnly
930    {
931        return Err(AutonomyContractError::InvalidDecision(
932            "shadow automation decisions must use review_state shadow_only".to_string(),
933        ));
934    }
935    if decision.review_state == AutonomousDecisionReviewState::ShadowOnly
936        && decision.authority_envelope.automation_mode != AutonomousAutomationMode::Shadow
937    {
938        return Err(AutonomyContractError::InvalidDecision(
939            "review_state shadow_only requires a shadow automation envelope".to_string(),
940        ));
941    }
942    if decision.disposition == AutonomousPricingDisposition::ManualReview
943        && decision.review_state == AutonomousDecisionReviewState::AutoApproved
944    {
945        return Err(AutonomyContractError::InvalidDecision(
946            "manual-review pricing decisions cannot be auto approved".to_string(),
947        ));
948    }
949    if let Some(threshold) = decision
950        .authority_envelope
951        .requires_human_review_above_premium
952        .as_ref()
953    {
954        if decision.review_state == AutonomousDecisionReviewState::AutoApproved
955            && decision.suggested_premium_amount.units > threshold.units
956        {
957            return Err(AutonomyContractError::InvalidDecision(
958                "auto-approved pricing decisions cannot exceed the premium review threshold"
959                    .to_string(),
960            ));
961        }
962    }
963    if decision.disposition == AutonomousPricingDisposition::BindWithinEnvelope {
964        if !decision
965            .authority_envelope
966            .permitted_actions
967            .contains(&AutonomousPricingAction::Bind)
968        {
969            return Err(AutonomyContractError::InvalidDecision(
970                "bind-within-envelope decisions require bind permission in the authority envelope"
971                    .to_string(),
972            ));
973        }
974        if !decision
975            .authority_envelope
976            .support_boundary
977            .live_bind_supported
978        {
979            return Err(AutonomyContractError::InvalidDecision(
980                "bind-within-envelope decisions require live_bind_supported".to_string(),
981            ));
982        }
983        if decision.authority_envelope.requires_human_review_for_bind
984            && decision.review_state == AutonomousDecisionReviewState::AutoApproved
985        {
986            return Err(AutonomyContractError::InvalidDecision(
987                "bind-within-envelope decisions cannot be auto approved when human review is required for bind"
988                    .to_string(),
989            ));
990        }
991    }
992    Ok(())
993}
994
995pub fn validate_capital_pool_optimization(
996    optimization: &CapitalPoolOptimizationArtifact,
997) -> Result<(), AutonomyContractError> {
998    if optimization.schema != CHIO_CAPITAL_POOL_OPTIMIZATION_SCHEMA {
999        return Err(AutonomyContractError::UnsupportedSchema(
1000            optimization.schema.clone(),
1001        ));
1002    }
1003    ensure_non_empty(
1004        &optimization.optimization_id,
1005        "capital_pool_optimization.optimization_id",
1006    )?;
1007    ensure_non_empty(
1008        &optimization.subject_key,
1009        "capital_pool_optimization.subject_key",
1010    )?;
1011    validate_currency_code(&optimization.currency, "capital_pool_optimization.currency")?;
1012    ensure_non_empty(
1013        &optimization.pricing_decision_ref,
1014        "capital_pool_optimization.pricing_decision_ref",
1015    )?;
1016    ensure_non_empty(
1017        &optimization.capital_book_ref,
1018        "capital_pool_optimization.capital_book_ref",
1019    )?;
1020    if optimization.facility_refs.is_empty() {
1021        return Err(AutonomyContractError::MissingField(
1022            "capital_pool_optimization.facility_refs",
1023        ));
1024    }
1025    ensure_unique_strings(
1026        &optimization.facility_refs,
1027        "capital_pool_optimization.facility_refs",
1028    )?;
1029    ensure_unique_strings(
1030        &optimization.pending_claim_refs,
1031        "capital_pool_optimization.pending_claim_refs",
1032    )?;
1033    if optimization.target_reserve_ratio_bps == 0 || optimization.target_reserve_ratio_bps > 10_000
1034    {
1035        return Err(AutonomyContractError::InvalidOptimization(
1036            "capital pool optimization target_reserve_ratio_bps must be between 1 and 10000"
1037                .to_string(),
1038        ));
1039    }
1040    if optimization.max_facility_utilization_bps == 0
1041        || optimization.max_facility_utilization_bps > 10_000
1042    {
1043        return Err(AutonomyContractError::InvalidOptimization(
1044            "capital pool optimization max_facility_utilization_bps must be between 1 and 10000"
1045                .to_string(),
1046        ));
1047    }
1048    if optimization.max_bind_capacity_units == 0 {
1049        return Err(AutonomyContractError::InvalidOptimization(
1050            "capital pool optimization max_bind_capacity_units must be non-zero".to_string(),
1051        ));
1052    }
1053    if optimization.recommendations.is_empty() {
1054        return Err(AutonomyContractError::MissingField(
1055            "capital_pool_optimization.recommendations",
1056        ));
1057    }
1058    for recommendation in &optimization.recommendations {
1059        ensure_non_empty(
1060            &recommendation.source_ref,
1061            "capital_pool_optimization.recommendations.source_ref",
1062        )?;
1063        ensure_non_empty(
1064            &recommendation.rationale,
1065            "capital_pool_optimization.recommendations.rationale",
1066        )?;
1067        validate_positive_money(
1068            &recommendation.amount,
1069            "capital_pool_optimization.recommendations.amount",
1070        )?;
1071        if recommendation.amount.currency.trim().to_ascii_uppercase() != optimization.currency {
1072            return Err(AutonomyContractError::InvalidOptimization(
1073                "capital pool optimization recommendation amounts must match optimization currency"
1074                    .to_string(),
1075            ));
1076        }
1077        if recommendation.action == CapitalOptimizationAction::ShiftCapacity
1078            && recommendation
1079                .destination_ref
1080                .as_deref()
1081                .is_none_or(|value| value.trim().is_empty())
1082        {
1083            return Err(AutonomyContractError::InvalidOptimization(
1084                "shift-capacity recommendations require destination_ref".to_string(),
1085            ));
1086        }
1087    }
1088    Ok(())
1089}
1090
1091pub fn validate_capital_pool_simulation_report(
1092    report: &CapitalPoolSimulationReport,
1093) -> Result<(), AutonomyContractError> {
1094    if report.schema != CHIO_CAPITAL_POOL_SIMULATION_REPORT_SCHEMA {
1095        return Err(AutonomyContractError::UnsupportedSchema(
1096            report.schema.clone(),
1097        ));
1098    }
1099    ensure_non_empty(
1100        &report.simulation_id,
1101        "capital_pool_simulation.simulation_id",
1102    )?;
1103    ensure_non_empty(&report.subject_key, "capital_pool_simulation.subject_key")?;
1104    validate_currency_code(&report.currency, "capital_pool_simulation.currency")?;
1105    validate_capital_pool_optimization(&report.baseline_optimization)?;
1106    validate_capital_pool_optimization(&report.candidate_optimization)?;
1107    if report.baseline_optimization.subject_key != report.subject_key
1108        || report.candidate_optimization.subject_key != report.subject_key
1109    {
1110        return Err(AutonomyContractError::InvalidOptimization(
1111            "capital pool simulation subject_key must match both baseline and candidate optimizations"
1112                .to_string(),
1113        ));
1114    }
1115    if report.baseline_optimization.currency != report.currency
1116        || report.candidate_optimization.currency != report.currency
1117    {
1118        return Err(AutonomyContractError::InvalidOptimization(
1119            "capital pool simulation currency must match both baseline and candidate optimizations"
1120                .to_string(),
1121        ));
1122    }
1123    if report.baseline_optimization.optimization_id == report.candidate_optimization.optimization_id
1124    {
1125        return Err(AutonomyContractError::DuplicateValue(
1126            report.baseline_optimization.optimization_id.clone(),
1127        ));
1128    }
1129    if !report
1130        .baseline_optimization
1131        .support_boundary
1132        .scenario_comparison_supported
1133        || !report
1134            .candidate_optimization
1135            .support_boundary
1136            .scenario_comparison_supported
1137    {
1138        return Err(AutonomyContractError::InvalidOptimization(
1139            "capital pool simulation requires scenario_comparison_supported on both optimizations"
1140                .to_string(),
1141        ));
1142    }
1143    if report.deltas.is_empty() {
1144        return Err(AutonomyContractError::MissingField(
1145            "capital_pool_simulation.deltas",
1146        ));
1147    }
1148    for delta in &report.deltas {
1149        ensure_non_empty(
1150            &delta.metric_name,
1151            "capital_pool_simulation.deltas.metric_name",
1152        )?;
1153        ensure_non_empty(
1154            &delta.description,
1155            "capital_pool_simulation.deltas.description",
1156        )?;
1157    }
1158    ensure_non_empty(
1159        &report.recommended_operator_action,
1160        "capital_pool_simulation.recommended_operator_action",
1161    )?;
1162    Ok(())
1163}
1164
1165pub fn validate_autonomous_execution_decision(
1166    decision: &AutonomousExecutionDecisionArtifact,
1167) -> Result<(), AutonomyContractError> {
1168    if decision.schema != CHIO_AUTONOMOUS_EXECUTION_DECISION_SCHEMA {
1169        return Err(AutonomyContractError::UnsupportedSchema(
1170            decision.schema.clone(),
1171        ));
1172    }
1173    for (value, field) in [
1174        (&decision.execution_id, "autonomous_execution.execution_id"),
1175        (
1176            &decision.pricing_decision_ref,
1177            "autonomous_execution.pricing_decision_ref",
1178        ),
1179        (
1180            &decision.optimization_ref,
1181            "autonomous_execution.optimization_ref",
1182        ),
1183        (
1184            &decision.authority_envelope_ref,
1185            "autonomous_execution.authority_envelope_ref",
1186        ),
1187        (&decision.subject_key, "autonomous_execution.subject_key"),
1188        (&decision.provider_id, "autonomous_execution.provider_id"),
1189    ] {
1190        ensure_non_empty(value, field)?;
1191    }
1192    validate_currency_code(&decision.currency, "autonomous_execution.currency")?;
1193    if decision.safety_gates.is_empty() {
1194        return Err(AutonomyContractError::MissingField(
1195            "autonomous_execution.safety_gates",
1196        ));
1197    }
1198    let mut gate_names = HashSet::new();
1199    for gate in &decision.safety_gates {
1200        ensure_non_empty(&gate.name, "autonomous_execution.safety_gates.name")?;
1201        ensure_non_empty(
1202            &gate.description,
1203            "autonomous_execution.safety_gates.description",
1204        )?;
1205        if !gate_names.insert(gate.name.as_str()) {
1206            return Err(AutonomyContractError::DuplicateValue(gate.name.clone()));
1207        }
1208    }
1209    validate_rollback_control(&decision.rollback_control)?;
1210
1211    let all_gates_passed = decision.safety_gates.iter().all(|gate| gate.passed);
1212    if decision.lifecycle_state == AutonomousExecutionLifecycleState::Executed && !all_gates_passed
1213    {
1214        return Err(AutonomyContractError::InvalidExecution(
1215            "executed autonomous actions require all safety gates to pass".to_string(),
1216        ));
1217    }
1218    if decision.lifecycle_state == AutonomousExecutionLifecycleState::Blocked && all_gates_passed {
1219        return Err(AutonomyContractError::InvalidExecution(
1220            "blocked autonomous actions require at least one failed safety gate".to_string(),
1221        ));
1222    }
1223
1224    match decision.action {
1225        AutonomousExecutionAction::Reprice | AutonomousExecutionAction::Renew => {
1226            ensure_non_empty(
1227                decision.quote_response_ref.as_deref().unwrap_or_default(),
1228                "autonomous_execution.quote_response_ref",
1229            )?;
1230            if decision.auto_bind_decision_ref.is_some()
1231                || decision.bound_coverage_ref.is_some()
1232                || decision.settlement_dispatch_ref.is_some()
1233            {
1234                return Err(AutonomyContractError::InvalidExecution(
1235                    "reprice and renew automation cannot embed bind or settlement references"
1236                        .to_string(),
1237                ));
1238            }
1239        }
1240        AutonomousExecutionAction::Decline => {
1241            if decision.auto_bind_decision_ref.is_some()
1242                || decision.bound_coverage_ref.is_some()
1243                || decision.settlement_dispatch_ref.is_some()
1244            {
1245                return Err(AutonomyContractError::InvalidExecution(
1246                    "decline automation cannot embed bind or settlement references".to_string(),
1247                ));
1248            }
1249        }
1250        AutonomousExecutionAction::Bind => {
1251            ensure_non_empty(
1252                decision.quote_response_ref.as_deref().unwrap_or_default(),
1253                "autonomous_execution.quote_response_ref",
1254            )?;
1255            ensure_non_empty(
1256                decision
1257                    .auto_bind_decision_ref
1258                    .as_deref()
1259                    .unwrap_or_default(),
1260                "autonomous_execution.auto_bind_decision_ref",
1261            )?;
1262            ensure_non_empty(
1263                decision.bound_coverage_ref.as_deref().unwrap_or_default(),
1264                "autonomous_execution.bound_coverage_ref",
1265            )?;
1266            if decision.lifecycle_state == AutonomousExecutionLifecycleState::Executed {
1267                ensure_non_empty(
1268                    decision
1269                        .settlement_dispatch_ref
1270                        .as_deref()
1271                        .unwrap_or_default(),
1272                    "autonomous_execution.settlement_dispatch_ref",
1273                )?;
1274            }
1275        }
1276    }
1277
1278    Ok(())
1279}
1280
1281pub fn validate_autonomous_comparison_report(
1282    report: &AutonomousComparisonReport,
1283) -> Result<(), AutonomyContractError> {
1284    if report.schema != CHIO_AUTONOMOUS_COMPARISON_REPORT_SCHEMA {
1285        return Err(AutonomyContractError::UnsupportedSchema(
1286            report.schema.clone(),
1287        ));
1288    }
1289    ensure_non_empty(&report.comparison_id, "autonomous_comparison.comparison_id")?;
1290    ensure_non_empty(
1291        &report.pricing_decision_ref,
1292        "autonomous_comparison.pricing_decision_ref",
1293    )?;
1294    ensure_non_empty(
1295        &report.manual_decision_ref,
1296        "autonomous_comparison.manual_decision_ref",
1297    )?;
1298    if report.disposition != AutonomousComparisonDisposition::Match && report.deltas.is_empty() {
1299        return Err(AutonomyContractError::MissingField(
1300            "autonomous_comparison.deltas",
1301        ));
1302    }
1303    if report.disposition == AutonomousComparisonDisposition::ManualOverride {
1304        ensure_non_empty(
1305            report.override_reference.as_deref().unwrap_or_default(),
1306            "autonomous_comparison.override_reference",
1307        )?;
1308    }
1309    for delta in &report.deltas {
1310        for (value, field) in [
1311            (&delta.field, "autonomous_comparison.deltas.field"),
1312            (
1313                &delta.automated_value,
1314                "autonomous_comparison.deltas.automated_value",
1315            ),
1316            (
1317                &delta.manual_value,
1318                "autonomous_comparison.deltas.manual_value",
1319            ),
1320            (
1321                &delta.description,
1322                "autonomous_comparison.deltas.description",
1323            ),
1324        ] {
1325            ensure_non_empty(value, field)?;
1326        }
1327    }
1328    Ok(())
1329}
1330
1331pub fn validate_autonomous_drift_report(
1332    report: &AutonomousDriftReport,
1333) -> Result<(), AutonomyContractError> {
1334    if report.schema != CHIO_AUTONOMOUS_DRIFT_REPORT_SCHEMA {
1335        return Err(AutonomyContractError::UnsupportedSchema(
1336            report.schema.clone(),
1337        ));
1338    }
1339    ensure_non_empty(&report.drift_report_id, "autonomous_drift.drift_report_id")?;
1340    ensure_non_empty(&report.subject_key, "autonomous_drift.subject_key")?;
1341    ensure_non_empty(
1342        &report.pricing_decision_ref,
1343        "autonomous_drift.pricing_decision_ref",
1344    )?;
1345    ensure_non_empty(
1346        &report.optimization_ref,
1347        "autonomous_drift.optimization_ref",
1348    )?;
1349    validate_autonomous_rollback_plan(&report.rollback_plan)?;
1350    validate_autonomous_comparison_report(&report.comparison_report)?;
1351    if report.drift_signals.is_empty() {
1352        return Err(AutonomyContractError::MissingField(
1353            "autonomous_drift.drift_signals",
1354        ));
1355    }
1356    let mut critical_kinds = HashSet::new();
1357    for signal in &report.drift_signals {
1358        ensure_non_empty(&signal.metric_name, "autonomous_drift.metric_name")?;
1359        ensure_non_empty(&signal.description, "autonomous_drift.description")?;
1360        if signal.threshold_value == 0 {
1361            return Err(AutonomyContractError::InvalidDrift(
1362                "autonomous drift threshold_value must be non-zero".to_string(),
1363            ));
1364        }
1365        if signal.severity == AutonomousDriftSeverity::Critical {
1366            critical_kinds.insert(signal.kind);
1367        }
1368    }
1369    if !critical_kinds.is_empty() && !report.fail_safe_engaged {
1370        return Err(AutonomyContractError::InvalidDrift(
1371            "critical drift signals require fail_safe_engaged".to_string(),
1372        ));
1373    }
1374    for kind in critical_kinds {
1375        if !report.rollback_plan.triggers.contains(&kind) {
1376            return Err(AutonomyContractError::InvalidDrift(format!(
1377                "rollback plan does not cover critical drift trigger {:?}",
1378                kind
1379            )));
1380        }
1381    }
1382    Ok(())
1383}
1384
1385pub fn validate_autonomous_rollback_plan(
1386    plan: &AutonomousRollbackPlanArtifact,
1387) -> Result<(), AutonomyContractError> {
1388    if plan.schema != CHIO_AUTONOMOUS_ROLLBACK_PLAN_SCHEMA {
1389        return Err(AutonomyContractError::UnsupportedSchema(
1390            plan.schema.clone(),
1391        ));
1392    }
1393    ensure_non_empty(&plan.plan_id, "autonomous_rollback.plan_id")?;
1394    ensure_non_empty(&plan.subject_key, "autonomous_rollback.subject_key")?;
1395    if plan.triggers.is_empty() {
1396        return Err(AutonomyContractError::MissingField(
1397            "autonomous_rollback.triggers",
1398        ));
1399    }
1400    if plan.actions.is_empty() {
1401        return Err(AutonomyContractError::MissingField(
1402            "autonomous_rollback.actions",
1403        ));
1404    }
1405    ensure_unique_copy_values(&plan.triggers, "autonomous_rollback.triggers")?;
1406    ensure_unique_copy_values(&plan.actions, "autonomous_rollback.actions")?;
1407    Ok(())
1408}
1409
1410pub fn validate_autonomous_qualification_matrix(
1411    matrix: &AutonomousQualificationMatrix,
1412) -> Result<(), AutonomyContractError> {
1413    if matrix.schema != CHIO_AUTONOMOUS_QUALIFICATION_MATRIX_SCHEMA {
1414        return Err(AutonomyContractError::UnsupportedSchema(
1415            matrix.schema.clone(),
1416        ));
1417    }
1418    ensure_non_empty(&matrix.profile_id, "autonomous_qualification.profile_id")?;
1419    if matrix.cases.is_empty() {
1420        return Err(AutonomyContractError::MissingField(
1421            "autonomous_qualification.cases",
1422        ));
1423    }
1424    let mut seen_ids = HashSet::new();
1425    for case in &matrix.cases {
1426        ensure_non_empty(&case.id, "autonomous_qualification.cases.id")?;
1427        ensure_non_empty(&case.name, "autonomous_qualification.cases.name")?;
1428        ensure_non_empty(&case.notes, "autonomous_qualification.cases.notes")?;
1429        if case.requirement_ids.is_empty() {
1430            return Err(AutonomyContractError::InvalidQualificationCase(format!(
1431                "qualification case `{}` requires at least one requirement id",
1432                case.id
1433            )));
1434        }
1435        ensure_unique_strings(
1436            &case.requirement_ids,
1437            "autonomous_qualification.cases.requirement_ids",
1438        )?;
1439        if !seen_ids.insert(case.id.as_str()) {
1440            return Err(AutonomyContractError::DuplicateValue(case.id.clone()));
1441        }
1442    }
1443    Ok(())
1444}
1445
1446fn validate_model_provenance(
1447    model: &AutonomousModelProvenance,
1448) -> Result<(), AutonomyContractError> {
1449    for (value, field) in [
1450        (&model.model_id, "autonomous_model.model_id"),
1451        (&model.model_version, "autonomous_model.model_version"),
1452        (&model.engine_family, "autonomous_model.engine_family"),
1453        (&model.input_hash, "autonomous_model.input_hash"),
1454        (
1455            &model.explanation_version,
1456            "autonomous_model.explanation_version",
1457        ),
1458    ] {
1459        ensure_non_empty(value, field)?;
1460    }
1461    if model.training_cutoff > model.published_at {
1462        return Err(AutonomyContractError::InvalidDecision(
1463            "autonomous model training_cutoff must be <= published_at".to_string(),
1464        ));
1465    }
1466    Ok(())
1467}
1468
1469fn validate_rollback_control(
1470    control: &AutonomousExecutionRollbackControl,
1471) -> Result<(), AutonomyContractError> {
1472    ensure_non_empty(
1473        &control.rollback_plan_ref,
1474        "autonomous_execution.rollback_control.rollback_plan_ref",
1475    )?;
1476    ensure_non_empty(
1477        &control.human_interrupt_contact,
1478        "autonomous_execution.rollback_control.human_interrupt_contact",
1479    )?;
1480    Ok(())
1481}
1482
1483fn validate_positive_money(
1484    amount: &MonetaryAmount,
1485    field: &'static str,
1486) -> Result<(), AutonomyContractError> {
1487    if amount.units == 0 {
1488        return Err(AutonomyContractError::InvalidDecision(format!(
1489            "{field} must be greater than zero"
1490        )));
1491    }
1492    validate_currency_code(&amount.currency, field)
1493}
1494
1495fn validate_currency_code(
1496    currency: &str,
1497    field: &'static str,
1498) -> Result<(), AutonomyContractError> {
1499    let normalized = currency.trim().to_ascii_uppercase();
1500    if normalized.len() != 3
1501        || !normalized
1502            .chars()
1503            .all(|character| character.is_ascii_uppercase())
1504    {
1505        return Err(AutonomyContractError::InvalidDecision(format!(
1506            "{field} must be a 3-letter uppercase currency code"
1507        )));
1508    }
1509    Ok(())
1510}
1511
1512fn ensure_non_empty(value: &str, field: &'static str) -> Result<(), AutonomyContractError> {
1513    if value.trim().is_empty() {
1514        return Err(AutonomyContractError::MissingField(field));
1515    }
1516    Ok(())
1517}
1518
1519fn ensure_unique_strings(
1520    values: &[String],
1521    field: &'static str,
1522) -> Result<(), AutonomyContractError> {
1523    let mut seen = HashSet::new();
1524    for value in values {
1525        if !seen.insert(value.as_str()) {
1526            return Err(AutonomyContractError::DuplicateValue(format!(
1527                "{field}:{value}"
1528            )));
1529        }
1530    }
1531    Ok(())
1532}
1533
1534fn ensure_unique_copy_values<T>(
1535    values: &[T],
1536    field: &'static str,
1537) -> Result<(), AutonomyContractError>
1538where
1539    T: Copy + Eq + std::hash::Hash + std::fmt::Debug,
1540{
1541    let mut seen = HashSet::new();
1542    for value in values {
1543        if !seen.insert(*value) {
1544            return Err(AutonomyContractError::DuplicateValue(format!(
1545                "{field}:{value:?}"
1546            )));
1547        }
1548    }
1549    Ok(())
1550}
1551
1552#[cfg(test)]
1553mod tests {
1554    use super::*;
1555
1556    fn sample_input() -> AutonomousPricingInputArtifact {
1557        AutonomousPricingInputArtifact {
1558            schema: CHIO_AUTONOMOUS_PRICING_INPUT_SCHEMA.to_string(),
1559            input_id: "api-1".to_string(),
1560            generated_at: 1_743_379_200,
1561            subject_key: "subject-1".to_string(),
1562            provider_id: "carrier-1".to_string(),
1563            coverage_class: LiabilityCoverageClass::ProfessionalLiability,
1564            currency: "USD".to_string(),
1565            requested_coverage_amount: MonetaryAmount {
1566                units: 120_000,
1567                currency: "USD".to_string(),
1568            },
1569            receipt_history_window_secs: 2_592_000,
1570            reputation_score_bps: 8_200,
1571            runtime_assurance_tier: RuntimeAssuranceTier::Verified,
1572            pending_loss_units: 0,
1573            settled_loss_units: 2_500,
1574            available_capital_units: 600_000,
1575            latest_web3_settlement_state: Some(Web3SettlementLifecycleState::Settled),
1576            evidence_refs: vec![
1577                AutonomousEvidenceReference {
1578                    kind: AutonomousEvidenceKind::UnderwritingDecision,
1579                    reference_id: "uwd-1".to_string(),
1580                    observed_at: Some(1_743_379_100),
1581                    locator: Some("underwriting:uwd-1".to_string()),
1582                },
1583                AutonomousEvidenceReference {
1584                    kind: AutonomousEvidenceKind::ExposureLedger,
1585                    reference_id: "eld-1".to_string(),
1586                    observed_at: Some(1_743_379_100),
1587                    locator: Some("ledger:eld-1".to_string()),
1588                },
1589                AutonomousEvidenceReference {
1590                    kind: AutonomousEvidenceKind::CreditScorecard,
1591                    reference_id: "score-1".to_string(),
1592                    observed_at: Some(1_743_379_050),
1593                    locator: Some("scorecard:score-1".to_string()),
1594                },
1595                AutonomousEvidenceReference {
1596                    kind: AutonomousEvidenceKind::CapitalBook,
1597                    reference_id: "cb-1".to_string(),
1598                    observed_at: Some(1_743_379_150),
1599                    locator: Some("capital-book:cb-1".to_string()),
1600                },
1601                AutonomousEvidenceReference {
1602                    kind: AutonomousEvidenceKind::CreditLossLifecycle,
1603                    reference_id: "loss-1".to_string(),
1604                    observed_at: Some(1_743_378_900),
1605                    locator: Some("loss:loss-1".to_string()),
1606                },
1607                AutonomousEvidenceReference {
1608                    kind: AutonomousEvidenceKind::Web3SettlementReceipt,
1609                    reference_id: "receipt-web3-1".to_string(),
1610                    observed_at: Some(1_743_379_000),
1611                    locator: Some("web3-settlement:receipt-web3-1".to_string()),
1612                },
1613            ],
1614            support_boundary: AutonomousPricingSupportBoundary::default(),
1615            note: Some(
1616                "Feeds one bounded autonomous pricing decision over Chio truth.".to_string(),
1617            ),
1618        }
1619    }
1620
1621    fn sample_authority_envelope() -> AutonomousPricingAuthorityEnvelopeArtifact {
1622        AutonomousPricingAuthorityEnvelopeArtifact {
1623            schema: CHIO_AUTONOMOUS_PRICING_AUTHORITY_ENVELOPE_SCHEMA.to_string(),
1624            envelope_id: "ape-1".to_string(),
1625            issued_at: 1_743_379_200,
1626            subject_key: "subject-1".to_string(),
1627            provider_id: "carrier-1".to_string(),
1628            currency: "USD".to_string(),
1629            kind: AutonomousAuthorityEnvelopeKind::DelegatedMarketAuthority,
1630            automation_mode: AutonomousAutomationMode::Active,
1631            permitted_actions: vec![
1632                AutonomousPricingAction::Reprice,
1633                AutonomousPricingAction::Renew,
1634                AutonomousPricingAction::Decline,
1635                AutonomousPricingAction::Bind,
1636            ],
1637            authority_chain_refs: vec![
1638                "underwriting-committee-approval".to_string(),
1639                "operator-treasury-approval".to_string(),
1640            ],
1641            max_coverage_amount: MonetaryAmount {
1642                units: 150_000,
1643                currency: "USD".to_string(),
1644            },
1645            max_premium_amount: MonetaryAmount {
1646                units: 6_000,
1647                currency: "USD".to_string(),
1648            },
1649            max_rate_change_bps: 750,
1650            max_daily_decisions: 20,
1651            requires_human_review_for_bind: false,
1652            requires_human_review_above_premium: Some(MonetaryAmount {
1653                units: 5_000,
1654                currency: "USD".to_string(),
1655            }),
1656            regulated_role: None,
1657            delegated_authority_reference: Some("lpa-1".to_string()),
1658            not_before: 1_743_379_200,
1659            not_after: 1_743_465_600,
1660            support_boundary: AutonomousPricingSupportBoundary::default(),
1661            note: Some("Binds automation to one delegated pricing authority envelope.".to_string()),
1662        }
1663    }
1664
1665    fn sample_decision() -> AutonomousPricingDecisionArtifact {
1666        AutonomousPricingDecisionArtifact {
1667            schema: CHIO_AUTONOMOUS_PRICING_DECISION_SCHEMA.to_string(),
1668            decision_id: "apd-1".to_string(),
1669            issued_at: 1_743_379_260,
1670            pricing_input: sample_input(),
1671            model: AutonomousModelProvenance {
1672                model_id: "pricing-model-chio-1".to_string(),
1673                model_version: "2026.03.31".to_string(),
1674                engine_family: "gradient_boosted_policy".to_string(),
1675                published_at: 1_743_379_000,
1676                training_cutoff: 1_743_292_800,
1677                input_hash: "4e4efc0ad4f8c80ad4c76f2f3ae2122e9b6cf407cdb2d43516c8f8e4dfd2c1df"
1678                    .to_string(),
1679                explanation_version: "counterfactual-v1".to_string(),
1680                supports_counterfactuals: true,
1681                supports_shadow_evaluation: true,
1682            },
1683            authority_envelope: sample_authority_envelope(),
1684            disposition: AutonomousPricingDisposition::BindWithinEnvelope,
1685            review_state: AutonomousDecisionReviewState::AutoApproved,
1686            suggested_coverage_amount: MonetaryAmount {
1687                units: 110_000,
1688                currency: "USD".to_string(),
1689            },
1690            suggested_premium_amount: MonetaryAmount {
1691                units: 4_800,
1692                currency: "USD".to_string(),
1693            },
1694            suggested_ceiling_factor_bps: Some(9_000),
1695            confidence_bps: 8_700,
1696            explanation_factors: vec![
1697                AutonomousPricingExplanationFactor {
1698                    code: "strong-runtime-assurance".to_string(),
1699                    description: "Verified runtime assurance supports automated bind posture."
1700                        .to_string(),
1701                    direction: AutonomousPricingExplanationDirection::Decrease,
1702                    weight_bps: 2_500,
1703                    evidence_refs: vec![AutonomousEvidenceReference {
1704                        kind: AutonomousEvidenceKind::RuntimeAssuranceAppraisal,
1705                        reference_id: "raa-1".to_string(),
1706                        observed_at: Some(1_743_379_000),
1707                        locator: Some("appraisal:raa-1".to_string()),
1708                    }],
1709                },
1710                AutonomousPricingExplanationFactor {
1711                    code: "settled-web3-history".to_string(),
1712                    description:
1713                        "Recent settled web3 history reduces uncertainty for automated renewal and bind."
1714                            .to_string(),
1715                    direction: AutonomousPricingExplanationDirection::Decrease,
1716                    weight_bps: 2_000,
1717                    evidence_refs: vec![AutonomousEvidenceReference {
1718                        kind: AutonomousEvidenceKind::Web3SettlementReceipt,
1719                        reference_id: "receipt-web3-1".to_string(),
1720                        observed_at: Some(1_743_379_000),
1721                        locator: Some("web3-settlement:receipt-web3-1".to_string()),
1722                    }],
1723                },
1724            ],
1725            comparison_baseline_ref: Some("uwd-1".to_string()),
1726            note: Some("Auto-approves one renewal/bind decision inside the active envelope.".to_string()),
1727        }
1728    }
1729
1730    fn sample_optimization() -> CapitalPoolOptimizationArtifact {
1731        CapitalPoolOptimizationArtifact {
1732            schema: CHIO_CAPITAL_POOL_OPTIMIZATION_SCHEMA.to_string(),
1733            optimization_id: "cpo-1".to_string(),
1734            issued_at: 1_743_379_320,
1735            subject_key: "subject-1".to_string(),
1736            currency: "USD".to_string(),
1737            pricing_decision_ref: "apd-1".to_string(),
1738            capital_book_ref: "cb-1".to_string(),
1739            facility_refs: vec!["facility-1".to_string(), "facility-2".to_string()],
1740            pending_claim_refs: vec!["claim-1".to_string()],
1741            target_reserve_ratio_bps: 3_500,
1742            max_facility_utilization_bps: 7_000,
1743            max_bind_capacity_units: 250_000,
1744            recommendations: vec![
1745                CapitalPoolRecommendation {
1746                    action: CapitalOptimizationAction::IncreaseReserve,
1747                    source_ref: "pool:primary".to_string(),
1748                    destination_ref: None,
1749                    amount: MonetaryAmount {
1750                        units: 25_000,
1751                        currency: "USD".to_string(),
1752                    },
1753                    rationale: "Raise reserve coverage before new autonomous binds.".to_string(),
1754                },
1755                CapitalPoolRecommendation {
1756                    action: CapitalOptimizationAction::ShiftCapacity,
1757                    source_ref: "facility-2".to_string(),
1758                    destination_ref: Some("facility-1".to_string()),
1759                    amount: MonetaryAmount {
1760                        units: 15_000,
1761                        currency: "USD".to_string(),
1762                    },
1763                    rationale: "Move capacity toward the lower-loss facility.".to_string(),
1764                },
1765            ],
1766            support_boundary: CapitalPoolOptimizationSupportBoundary::default(),
1767            note: Some("Keeps optimization bounded and override-ready.".to_string()),
1768        }
1769    }
1770
1771    fn sample_simulation() -> CapitalPoolSimulationReport {
1772        let baseline = sample_optimization();
1773        let mut candidate = sample_optimization();
1774        candidate.optimization_id = "cpo-2".to_string();
1775        candidate.target_reserve_ratio_bps = 4_000;
1776        candidate.max_bind_capacity_units = 230_000;
1777        CapitalPoolSimulationReport {
1778            schema: CHIO_CAPITAL_POOL_SIMULATION_REPORT_SCHEMA.to_string(),
1779            simulation_id: "cps-1".to_string(),
1780            generated_at: 1_743_379_380,
1781            subject_key: "subject-1".to_string(),
1782            currency: "USD".to_string(),
1783            baseline_optimization: baseline,
1784            candidate_optimization: candidate,
1785            simulation_mode: CapitalPoolSimulationMode::WhatIf,
1786            deltas: vec![
1787                CapitalPoolSimulationDelta {
1788                    metric_name: "reserve_ratio_bps".to_string(),
1789                    baseline_units: 3_500,
1790                    candidate_units: 4_000,
1791                    description: "Candidate scenario raises the reserve floor by 500 bps."
1792                        .to_string(),
1793                },
1794                CapitalPoolSimulationDelta {
1795                    metric_name: "max_bind_capacity_units".to_string(),
1796                    baseline_units: 250_000,
1797                    candidate_units: 230_000,
1798                    description:
1799                        "Candidate scenario trims bind capacity to create more reserve headroom."
1800                            .to_string(),
1801                },
1802            ],
1803            recommended_operator_action:
1804                "Adopt the candidate reserve posture for the next renewal cohort.".to_string(),
1805            note: Some(
1806                "Compares baseline and candidate capital strategies without mutating live state."
1807                    .to_string(),
1808            ),
1809        }
1810    }
1811
1812    fn sample_execution_decision() -> AutonomousExecutionDecisionArtifact {
1813        AutonomousExecutionDecisionArtifact {
1814            schema: CHIO_AUTONOMOUS_EXECUTION_DECISION_SCHEMA.to_string(),
1815            execution_id: "aed-1".to_string(),
1816            issued_at: 1_743_379_440,
1817            pricing_decision_ref: "apd-1".to_string(),
1818            optimization_ref: "cpo-1".to_string(),
1819            authority_envelope_ref: "ape-1".to_string(),
1820            subject_key: "subject-1".to_string(),
1821            provider_id: "carrier-1".to_string(),
1822            currency: "USD".to_string(),
1823            action: AutonomousExecutionAction::Bind,
1824            lifecycle_state: AutonomousExecutionLifecycleState::Executed,
1825            quote_response_ref: Some("quote-response-1".to_string()),
1826            auto_bind_decision_ref: Some("auto-bind-1".to_string()),
1827            bound_coverage_ref: Some("bound-coverage-1".to_string()),
1828            settlement_dispatch_ref: Some("dispatch-web3-1".to_string()),
1829            safety_gates: vec![
1830                AutonomousExecutionSafetyGate {
1831                    name: "authority-within-envelope".to_string(),
1832                    passed: true,
1833                    description:
1834                        "Coverage and premium remain inside the active authority envelope."
1835                            .to_string(),
1836                },
1837                AutonomousExecutionSafetyGate {
1838                    name: "capital-headroom".to_string(),
1839                    passed: true,
1840                    description: "Capital-pool optimization preserved minimum reserve headroom."
1841                        .to_string(),
1842                },
1843            ],
1844            rollback_control: AutonomousExecutionRollbackControl {
1845                rollback_plan_ref: "arp-1".to_string(),
1846                interruptible: true,
1847                human_interrupt_contact: "ops@chio.example".to_string(),
1848            },
1849            note: Some(
1850                "Executes one bounded autonomous bind over the official web3 lane.".to_string(),
1851            ),
1852        }
1853    }
1854
1855    fn sample_comparison_report() -> AutonomousComparisonReport {
1856        AutonomousComparisonReport {
1857            schema: CHIO_AUTONOMOUS_COMPARISON_REPORT_SCHEMA.to_string(),
1858            comparison_id: "acr-1".to_string(),
1859            generated_at: 1_743_379_500,
1860            pricing_decision_ref: "apd-1".to_string(),
1861            manual_decision_ref: "uwd-manual-1".to_string(),
1862            disposition: AutonomousComparisonDisposition::NarrowerThanManual,
1863            deltas: vec![AutonomousComparisonDelta {
1864                field: "premium_units".to_string(),
1865                automated_value: "4800".to_string(),
1866                manual_value: "5100".to_string(),
1867                description: "Automation priced inside the manual ceiling.".to_string(),
1868            }],
1869            override_reference: None,
1870            note: Some(
1871                "Shows automation staying narrower than the comparable manual decision."
1872                    .to_string(),
1873            ),
1874        }
1875    }
1876
1877    fn sample_rollback_plan() -> AutonomousRollbackPlanArtifact {
1878        AutonomousRollbackPlanArtifact {
1879            schema: CHIO_AUTONOMOUS_ROLLBACK_PLAN_SCHEMA.to_string(),
1880            plan_id: "arp-1".to_string(),
1881            issued_at: 1_743_379_560,
1882            subject_key: "subject-1".to_string(),
1883            safe_state: AutonomousSafeState::DelegatedOnly,
1884            triggers: vec![
1885                AutonomousDriftKind::SettlementFailureRate,
1886                AutonomousDriftKind::PremiumVariance,
1887            ],
1888            actions: vec![
1889                AutonomousRollbackAction::SwitchToSafeState,
1890                AutonomousRollbackAction::CancelPendingExecution,
1891                AutonomousRollbackAction::RequireHumanApproval,
1892            ],
1893            requires_operator_ack: true,
1894            note: Some("Falls back to delegated pricing when automation drifts beyond the accepted envelope.".to_string()),
1895        }
1896    }
1897
1898    fn sample_drift_report() -> AutonomousDriftReport {
1899        AutonomousDriftReport {
1900            schema: CHIO_AUTONOMOUS_DRIFT_REPORT_SCHEMA.to_string(),
1901            drift_report_id: "adr-1".to_string(),
1902            generated_at: 1_743_379_620,
1903            subject_key: "subject-1".to_string(),
1904            pricing_decision_ref: "apd-1".to_string(),
1905            optimization_ref: "cpo-1".to_string(),
1906            drift_signals: vec![AutonomousDriftSignal {
1907                kind: AutonomousDriftKind::SettlementFailureRate,
1908                severity: AutonomousDriftSeverity::Critical,
1909                metric_name: "failed_settlement_rate_bps".to_string(),
1910                observed_value: 275,
1911                threshold_value: 100,
1912                description: "Settlement failures exceeded the automation safe-state threshold."
1913                    .to_string(),
1914                evidence_refs: vec![AutonomousEvidenceReference {
1915                    kind: AutonomousEvidenceKind::Web3SettlementReceipt,
1916                    reference_id: "receipt-web3-1".to_string(),
1917                    observed_at: Some(1_743_379_000),
1918                    locator: Some("web3-settlement:receipt-web3-1".to_string()),
1919                }],
1920            }],
1921            rollback_plan: sample_rollback_plan(),
1922            comparison_report: sample_comparison_report(),
1923            fail_safe_engaged: true,
1924            note: Some(
1925                "Fail-safe engaged after settlement drift breached the critical threshold."
1926                    .to_string(),
1927            ),
1928        }
1929    }
1930
1931    #[test]
1932    fn shadow_mode_requires_shadow_review_state() {
1933        let mut decision = sample_decision();
1934        decision.authority_envelope.automation_mode = AutonomousAutomationMode::Shadow;
1935        decision.authority_envelope.permitted_actions = vec![AutonomousPricingAction::Reprice];
1936        decision.disposition = AutonomousPricingDisposition::Reprice;
1937        assert!(matches!(
1938            validate_autonomous_pricing_decision(&decision),
1939            Err(AutonomyContractError::InvalidDecision(_))
1940        ));
1941    }
1942
1943    #[test]
1944    fn capital_pool_simulation_requires_matching_subject() {
1945        let mut report = sample_simulation();
1946        report.candidate_optimization.subject_key = "subject-2".to_string();
1947        assert!(matches!(
1948            validate_capital_pool_simulation_report(&report),
1949            Err(AutonomyContractError::InvalidOptimization(_))
1950        ));
1951    }
1952
1953    #[test]
1954    fn bind_execution_requires_settlement_dispatch_when_executed() {
1955        let mut execution = sample_execution_decision();
1956        execution.settlement_dispatch_ref = None;
1957        assert!(matches!(
1958            validate_autonomous_execution_decision(&execution),
1959            Err(AutonomyContractError::MissingField(_))
1960        ));
1961    }
1962
1963    #[test]
1964    fn critical_drift_requires_fail_safe() {
1965        let mut report = sample_drift_report();
1966        report.fail_safe_engaged = false;
1967        assert!(matches!(
1968            validate_autonomous_drift_report(&report),
1969            Err(AutonomyContractError::InvalidDrift(_))
1970        ));
1971    }
1972
1973    #[test]
1974    fn reference_artifacts_parse_and_validate() {
1975        let envelope: AutonomousPricingAuthorityEnvelopeArtifact = serde_json::from_str(
1976            include_str!("../../../docs/standards/CHIO_AUTONOMOUS_PRICING_AUTHORITY_ENVELOPE.json"),
1977        )
1978        .unwrap();
1979        let decision: AutonomousPricingDecisionArtifact = serde_json::from_str(include_str!(
1980            "../../../docs/standards/CHIO_AUTONOMOUS_PRICING_DECISION_EXAMPLE.json"
1981        ))
1982        .unwrap();
1983        let optimization: CapitalPoolOptimizationArtifact = serde_json::from_str(include_str!(
1984            "../../../docs/standards/CHIO_CAPITAL_POOL_OPTIMIZATION_EXAMPLE.json"
1985        ))
1986        .unwrap();
1987        let simulation: CapitalPoolSimulationReport = serde_json::from_str(include_str!(
1988            "../../../docs/standards/CHIO_CAPITAL_POOL_SIMULATION_EXAMPLE.json"
1989        ))
1990        .unwrap();
1991        let execution: AutonomousExecutionDecisionArtifact = serde_json::from_str(include_str!(
1992            "../../../docs/standards/CHIO_AUTONOMOUS_EXECUTION_EXAMPLE.json"
1993        ))
1994        .unwrap();
1995        let comparison: AutonomousComparisonReport = serde_json::from_str(include_str!(
1996            "../../../docs/standards/CHIO_AUTONOMOUS_COMPARISON_REPORT_EXAMPLE.json"
1997        ))
1998        .unwrap();
1999        let drift: AutonomousDriftReport = serde_json::from_str(include_str!(
2000            "../../../docs/standards/CHIO_AUTONOMOUS_DRIFT_REPORT_EXAMPLE.json"
2001        ))
2002        .unwrap();
2003        let matrix: AutonomousQualificationMatrix = serde_json::from_str(include_str!(
2004            "../../../docs/standards/CHIO_AUTONOMOUS_QUALIFICATION_MATRIX.json"
2005        ))
2006        .unwrap();
2007
2008        validate_autonomous_pricing_authority_envelope(&envelope).unwrap();
2009        validate_autonomous_pricing_decision(&decision).unwrap();
2010        validate_capital_pool_optimization(&optimization).unwrap();
2011        validate_capital_pool_simulation_report(&simulation).unwrap();
2012        validate_autonomous_execution_decision(&execution).unwrap();
2013        validate_autonomous_comparison_report(&comparison).unwrap();
2014        validate_autonomous_drift_report(&drift).unwrap();
2015        validate_autonomous_qualification_matrix(&matrix).unwrap();
2016    }
2017
2018    #[test]
2019    fn pricing_input_requires_capital_book_evidence() {
2020        let mut input = sample_input();
2021        input
2022            .evidence_refs
2023            .retain(|evidence| evidence.kind != AutonomousEvidenceKind::CapitalBook);
2024        assert!(matches!(
2025            validate_autonomous_pricing_input(&input),
2026            Err(AutonomyContractError::UnknownReference(_))
2027        ));
2028    }
2029
2030    #[test]
2031    fn pricing_input_requires_web3_evidence_when_settlement_state_present() {
2032        let mut input = sample_input();
2033        input
2034            .evidence_refs
2035            .retain(|evidence| evidence.kind != AutonomousEvidenceKind::Web3SettlementReceipt);
2036        assert!(matches!(
2037            validate_autonomous_pricing_input(&input),
2038            Err(AutonomyContractError::UnknownReference(_))
2039        ));
2040    }
2041
2042    #[test]
2043    fn delegated_authority_requires_reference() {
2044        let mut envelope = sample_authority_envelope();
2045        envelope.delegated_authority_reference = None;
2046        assert!(matches!(
2047            validate_autonomous_pricing_authority_envelope(&envelope),
2048            Err(AutonomyContractError::MissingField(
2049                "autonomous_authority_envelope.delegated_authority_reference"
2050            ))
2051        ));
2052    }
2053
2054    #[test]
2055    fn non_active_authority_cannot_permit_bind() {
2056        let mut envelope = sample_authority_envelope();
2057        envelope.automation_mode = AutonomousAutomationMode::Advisory;
2058        assert!(matches!(
2059            validate_autonomous_pricing_authority_envelope(&envelope),
2060            Err(AutonomyContractError::InvalidEnvelope(_))
2061        ));
2062    }
2063
2064    #[test]
2065    fn pricing_decision_rejects_duplicate_explanation_codes() {
2066        let mut decision = sample_decision();
2067        decision
2068            .explanation_factors
2069            .push(decision.explanation_factors[0].clone());
2070        assert!(matches!(
2071            validate_autonomous_pricing_decision(&decision),
2072            Err(AutonomyContractError::DuplicateValue(_))
2073        ));
2074    }
2075
2076    #[test]
2077    fn bind_decision_cannot_auto_approve_when_human_review_required() {
2078        let mut decision = sample_decision();
2079        decision.authority_envelope.requires_human_review_for_bind = true;
2080        assert!(matches!(
2081            validate_autonomous_pricing_decision(&decision),
2082            Err(AutonomyContractError::InvalidDecision(_))
2083        ));
2084    }
2085
2086    #[test]
2087    fn optimization_shift_capacity_requires_destination_ref() {
2088        let mut optimization = sample_optimization();
2089        optimization.recommendations[1].destination_ref = None;
2090        assert!(matches!(
2091            validate_capital_pool_optimization(&optimization),
2092            Err(AutonomyContractError::InvalidOptimization(_))
2093        ));
2094    }
2095
2096    #[test]
2097    fn capital_pool_simulation_requires_scenario_comparison_support() {
2098        let mut report = sample_simulation();
2099        report
2100            .candidate_optimization
2101            .support_boundary
2102            .scenario_comparison_supported = false;
2103        assert!(matches!(
2104            validate_capital_pool_simulation_report(&report),
2105            Err(AutonomyContractError::InvalidOptimization(_))
2106        ));
2107    }
2108
2109    #[test]
2110    fn comparison_report_manual_override_requires_reference() {
2111        let mut report = sample_comparison_report();
2112        report.disposition = AutonomousComparisonDisposition::ManualOverride;
2113        report.override_reference = None;
2114        assert!(matches!(
2115            validate_autonomous_comparison_report(&report),
2116            Err(AutonomyContractError::MissingField(
2117                "autonomous_comparison.override_reference"
2118            ))
2119        ));
2120    }
2121
2122    #[test]
2123    fn rollback_plan_rejects_duplicate_triggers() {
2124        let mut plan = sample_rollback_plan();
2125        plan.triggers.push(plan.triggers[0]);
2126        assert!(matches!(
2127            validate_autonomous_rollback_plan(&plan),
2128            Err(AutonomyContractError::DuplicateValue(_))
2129        ));
2130    }
2131
2132    #[test]
2133    fn qualification_matrix_requires_requirement_ids() {
2134        let mut matrix: AutonomousQualificationMatrix = serde_json::from_str(include_str!(
2135            "../../../docs/standards/CHIO_AUTONOMOUS_QUALIFICATION_MATRIX.json"
2136        ))
2137        .unwrap();
2138        matrix.cases[0].requirement_ids.clear();
2139        assert!(matches!(
2140            validate_autonomous_qualification_matrix(&matrix),
2141            Err(AutonomyContractError::InvalidQualificationCase(_))
2142        ));
2143    }
2144
2145    #[test]
2146    fn pricing_decision_rejects_future_training_cutoff() {
2147        let mut decision = sample_decision();
2148        decision.model.training_cutoff = decision.model.published_at + 1;
2149        assert!(matches!(
2150            validate_autonomous_pricing_decision(&decision),
2151            Err(AutonomyContractError::InvalidDecision(_))
2152        ));
2153    }
2154}