cordance_cortex/
builder.rs1use chrono::Utc;
15use cordance_core::pack::CordancePack;
16use cordance_core::receipt::{
17 AuthorityBoundary, CortexReceiptV1Candidate, ExecutionTrust, OperatorApproval, ReceiptBody,
18 RuntimeIntegrity, SourceAnchor, SourceContext, TruthCeiling,
19};
20use cordance_core::schema;
21use tracing::debug;
22
23const FORBIDDEN_USES: &[&str] = &[
25 "claim Cortex truth",
26 "claim Cortex admission",
27 "promote Cortex memory",
28 "promote Cortex doctrine",
29 "create trusted history",
30 "claim release acceptance",
31 "authorize runtime writes",
32 "claim P6 or P9 closure",
33 "accept or promote an ADR",
34];
35
36pub fn build_receipt(pack: &CordancePack) -> Result<CortexReceiptV1Candidate, crate::CortexError> {
42 debug!(
43 project = %pack.project.name,
44 "building cortex candidate receipt"
45 );
46
47 let context_id = if pack.source_lock.pack_id.is_empty() {
48 pack.project.name.clone()
49 } else {
50 pack.source_lock.pack_id.clone()
51 };
52
53 let source_anchors: Vec<SourceAnchor> = pack
54 .sources
55 .iter()
56 .filter(|r| !r.blocked)
57 .map(|r| SourceAnchor::new(r.id.clone(), r.path.clone(), r.sha256.clone()))
58 .collect();
59
60 let mut allowed_claim_language: Vec<String> = pack.residual_risk.clone();
61 allowed_claim_language
62 .push("cordance candidate receipt for cortex context-pack admit-cordance".into());
63 allowed_claim_language.push("candidate-only evidence".into());
64 allowed_claim_language.push("no durable promotion".into());
65 allowed_claim_language.push("no release acceptance".into());
66
67 let mut residual_risk: Vec<String> = vec![
68 "Cortex may reject or quarantine this receipt under its native parser.".into(),
69 "claim_ceiling=candidate_evidence_only".into(),
70 ];
71 for item in &pack.residual_risk {
73 if !residual_risk.contains(item) {
74 residual_risk.push(item.clone());
75 }
76 }
77
78 let source_context = SourceContext::new(
79 context_id,
80 TruthCeiling::CandidateEvidenceOnly,
81 "not_cleared_by_boundary_crossing".into(),
82 );
83
84 let execution_trust = ExecutionTrust::new(
85 "local_candidate_only".into(),
86 "deny_authority_grant".into(),
87 "not_field_level_bound_for_cortex".into(),
88 "not_field_level_bound_for_cortex".into(),
89 );
90
91 let runtime_integrity = RuntimeIntegrity::new(false, false, "not_requested".into());
92
93 let operator_approval = OperatorApproval::new(
94 true,
95 "not_supplied_for_cortex_promotion".into(),
96 "not_supplied_for_cortex_promotion".into(),
97 );
98
99 let generated_at = Utc::now();
106 let body = ReceiptBody::new(
107 format!("cordance-candidate-{}", generated_at.format("%Y-%m-%d")),
108 generated_at,
109 "candidate_only".into(),
110 "partial_structural_evidence".into(),
111 "repo_only_no_runtime_write".into(),
112 "not_bound_for_cortex_promotion".into(),
113 format!("cordance-pack-{}", pack.project.name),
114 source_context,
115 execution_trust,
116 runtime_integrity,
117 operator_approval,
118 allowed_claim_language,
119 FORBIDDEN_USES.iter().map(|s| (*s).to_owned()).collect(),
120 source_anchors,
121 residual_risk,
122 );
123
124 let receipt = CortexReceiptV1Candidate::new(
125 schema::CORDANCE_CORTEX_RECEIPT_V1_CANDIDATE.into(),
126 1,
127 "candidate_only".into(),
128 "cortex context-pack admit-cordance".into(),
129 AuthorityBoundary::candidate_only(),
130 body,
131 vec![
132 "Cortex field-level tool_provenance consumer absent".into(),
133 "Cortex operator approval hash binding absent".into(),
134 ],
135 );
136
137 crate::validator::validate_receipt(&receipt)?;
138 Ok(receipt)
139}