services_hardening_reconciliation_outcome_contract.rs (4747B)
1 #![forbid(unsafe_code)] 2 3 use rhi::RHI_RECONCILIATION_OUTCOME_CONTRACT_VERSION; 4 use serde_json::json; 5 6 const CONTRACT: &str = 7 include_str!("../contracts/services_hardening/reconciliation_outcome.v1.json"); 8 const ROOT: &str = include_str!("../src/lib.rs"); 9 const SOURCE: &str = include_str!("../src/reconciliation_reducer.rs"); 10 const README: &str = include_str!("../README"); 11 12 #[test] 13 fn machine_contract_freezes_the_complete_step_194_boundary() { 14 let contract: serde_json::Value = serde_json::from_str(CONTRACT).expect("contract"); 15 assert_eq!(contract["schema"], "radroots.rhi.reconciliation-outcome"); 16 assert_eq!(contract["schema_version"], 1); 17 assert_eq!( 18 contract["contract_version"], 19 RHI_RECONCILIATION_OUTCOME_CONTRACT_VERSION 20 ); 21 assert_eq!( 22 contract["coverage"], 23 json!(["missing", "partial", "scope_satisfied", "unsupported"]) 24 ); 25 assert_eq!( 26 contract["outcome"], 27 json!(["valid", "invalid", "indeterminate"]) 28 ); 29 assert_eq!( 30 contract["precedence"], 31 json!([ 32 "manifest_coverage", 33 "projection_digest_availability", 34 "shared_evidence_state", 35 "reducer_issue_or_claim_ambiguity", 36 "claim_presence", 37 "clean_active_claim", 38 "clean_cancelled_claim", 39 "unresolved_claim" 40 ]) 41 ); 42 assert_eq!( 43 contract["decision"]["clean_active_claim"], 44 json!(["valid", "scope_satisfied"]) 45 ); 46 assert_eq!( 47 contract["decision"]["clean_cancelled_claim"], 48 json!(["invalid", "agreement_claim_cancelled"]) 49 ); 50 for branch in [ 51 "missing", 52 "partial", 53 "unsupported", 54 "projection_digest_unavailable", 55 "shared_evidence_missing", 56 "shared_evidence_partial", 57 "shared_schema_unsupported", 58 "reducer_issue_or_claim_ambiguity", 59 "claim_missing", 60 "claim_unresolved", 61 ] { 62 assert_eq!( 63 contract["decision"][branch][0], "indeterminate", 64 "{branch} must remain fail closed" 65 ); 66 } 67 assert_eq!(contract["reason_inventory"]["cardinality"], 1); 68 assert_eq!(contract["reason_inventory"]["closed"], true); 69 assert_eq!( 70 contract["reason_inventory"]["codes"], 71 json!([ 72 "required_evidence_missing", 73 "required_source_incomplete", 74 "required_source_unsupported", 75 "projection_digest_unavailable", 76 "governing_schema_unsupported", 77 "reducer_issue_unresolved", 78 "agreement_claim_missing", 79 "agreement_claim_unresolved", 80 "scope_satisfied", 81 "agreement_claim_cancelled" 82 ]) 83 ); 84 assert_eq!(contract["result"]["caller_forgeable"], false); 85 assert_eq!(contract["result"]["retains_projection"], true); 86 for effect in [ 87 "sqlite", 88 "filesystem", 89 "source_or_relay", 90 "network", 91 "task_spawn", 92 "ambient_clock", 93 "ambient_entropy", 94 ] { 95 assert_eq!(contract["effects"][effect], false, "effect {effect}"); 96 } 97 } 98 99 #[test] 100 fn outcome_boundary_is_sealed_typed_redacted_and_total() { 101 for required in [ 102 "RhiReconciliationEvaluation", 103 "RhiReconciliationCoverage", 104 "RhiReconciliationOutcome", 105 "RhiReconciliationReasonCode", 106 "RHI_RECONCILIATION_OUTCOME_CONTRACT_VERSION", 107 "evaluate_rhi_reconciliation_claim", 108 ] { 109 assert!(ROOT.contains(required), "root API is missing {required}"); 110 } 111 for required in [ 112 "projection: RhiReconciliationProjection", 113 "claim_mutation_id: MutationId", 114 "reason_codes: [RhiReconciliationReasonCode; 1]", 115 "fn classify_evaluation(", 116 "RadrootsTradeEvidenceStateV1::UnsupportedVersion", 117 "RhiReconciliationCoverage::ScopeSatisfied", 118 "(Invalid, AgreementClaimCancelled)", 119 "(Indeterminate, AgreementClaimUnresolved)", 120 ] { 121 assert!( 122 SOURCE.contains(required), 123 "outcome boundary is missing {required}" 124 ); 125 } 126 for forbidden in [ 127 "sqlx::", 128 "std::fs", 129 "std::net", 130 "tokio::", 131 "SystemTime", 132 "thread_rng", 133 "OsRng", 134 "pub fn new(", 135 "pub const fn new(", 136 "Result<RhiReconciliationEvaluation", 137 ] { 138 assert!( 139 !SOURCE.contains(forbidden), 140 "outcome boundary gained forbidden authority {forbidden}" 141 ); 142 } 143 assert!(README.contains("## Pure reconciliation reducer")); 144 assert!(README.contains( 145 "[`reconciliation_outcome.v1.json`](contracts/services_hardening/reconciliation_outcome.v1.json)" 146 )); 147 }