Skip to main content

termivar_scanner/decision_runner/
receipts.rs

1//! Immutable pre-commit failure and committed-evidence receipts.
2
3use super::{
4    DecisionActionOrigin, DecisionExecutionFailureKind, DecisionExecutionLimits,
5    DecisionExecutionRequest, DecisionExecutionStage, DecisionLoopCommand, DecisionOutcomeReport,
6    DecisionPlanningReport, Evidence, KnowledgeSnapshot, KnowledgeWrite, RuntimeLimitExceeded,
7    Serialize, VerificationCase,
8};
9
10/// Immutable audit receipt for an executor-reported pre-commit failure.
11///
12/// The receipt exists only after an executor was resolved and returned
13/// [`super::DecisionExecutorError`]. It does not represent route lookup, evidence
14/// validation, knowledge storage, or runtime wall-time failures.
15#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
16pub struct DecisionExecutionFailureReceipt {
17    pub(super) request: DecisionExecutionRequest,
18    pub(super) executor_id: String,
19    pub(super) diagnostic: String,
20    pub(super) kind: DecisionExecutionFailureKind,
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub(super) runtime_limit: Option<RuntimeLimitExceeded>,
23}
24
25impl DecisionExecutionFailureReceipt {
26    /// Returns the exact immutable request presented to the executor.
27    pub fn request(&self) -> &DecisionExecutionRequest {
28        &self.request
29    }
30
31    /// Returns the verification case whose action failed to execute.
32    pub fn case(&self) -> &VerificationCase {
33        self.request.case()
34    }
35
36    /// Returns the stable planned action identity.
37    pub fn action_id(&self) -> &str {
38        self.request.case().action_id()
39    }
40
41    /// Returns whether passive or active evidence was requested.
42    pub fn stage(&self) -> DecisionExecutionStage {
43        self.request.stage()
44    }
45
46    /// Returns the source of a passive action request.
47    pub fn origin(&self) -> Option<DecisionActionOrigin> {
48        self.request.origin()
49    }
50
51    /// Returns the scheduler delay honored before the failed execution.
52    pub fn delay_ms(&self) -> Option<u64> {
53        self.request.delay_ms()
54    }
55
56    /// Returns the host-owned resource allowances applied to the execution.
57    pub fn limits(&self) -> DecisionExecutionLimits {
58        self.request.limits()
59    }
60
61    /// Returns the resolved executor identity.
62    pub fn executor_id(&self) -> &str {
63        &self.executor_id
64    }
65
66    /// Returns the executor-supplied stable diagnostic.
67    pub fn diagnostic(&self) -> &str {
68        &self.diagnostic
69    }
70
71    /// Returns the structured reason execution produced no evidence.
72    pub fn kind(&self) -> DecisionExecutionFailureKind {
73        self.kind
74    }
75
76    /// Returns the host resource limit that refused the dispatch, if any.
77    pub fn runtime_limit(&self) -> Option<&RuntimeLimitExceeded> {
78        self.runtime_limit.as_ref()
79    }
80}
81
82/// A committed evidence batch and the snapshots needed by verification.
83#[derive(Debug, Clone)]
84pub struct DecisionEvidenceReceipt {
85    pub(super) case: VerificationCase,
86    pub(super) stage: DecisionExecutionStage,
87    pub(super) executor_id: String,
88    pub(super) evidence: Vec<Evidence>,
89    pub(super) writes: Vec<KnowledgeWrite>,
90    pub(super) baseline: Option<KnowledgeSnapshot>,
91    pub(super) after_execution: KnowledgeSnapshot,
92}
93
94impl DecisionEvidenceReceipt {
95    /// Returns the verification case whose action produced the evidence.
96    pub fn case(&self) -> &VerificationCase {
97        &self.case
98    }
99
100    /// Returns the verification stage collected by this execution.
101    pub fn stage(&self) -> DecisionExecutionStage {
102        self.stage
103    }
104
105    /// Returns the resolved executor identity.
106    pub fn executor_id(&self) -> &str {
107        &self.executor_id
108    }
109
110    /// Returns the exact evidence batch emitted by this execution.
111    pub fn evidence(&self) -> &[Evidence] {
112        &self.evidence
113    }
114
115    /// Returns one idempotent knowledge write result per emitted observation.
116    pub fn writes(&self) -> &[KnowledgeWrite] {
117        &self.writes
118    }
119
120    /// Iterates over the exact evidence/write set committed by this execution.
121    ///
122    /// The two values share one input-order position, so hosts do not need to
123    /// reconstruct the atomic batch by indexing separate slices.
124    pub fn write_set(&self) -> impl ExactSizeIterator<Item = (&Evidence, KnowledgeWrite)> + '_ {
125        debug_assert_eq!(self.evidence.len(), self.writes.len());
126        self.evidence.iter().zip(self.writes.iter().copied())
127    }
128
129    /// Returns the pre-probe snapshot for active verification.
130    pub fn baseline(&self) -> Option<&KnowledgeSnapshot> {
131        self.baseline.as_ref()
132    }
133
134    /// Returns the subject snapshot after the evidence batch was committed.
135    pub fn after_execution(&self) -> &KnowledgeSnapshot {
136        &self.after_execution
137    }
138
139    #[cfg(test)]
140    pub(crate) fn with_test_committed_batch(
141        &self,
142        evidence: Vec<Evidence>,
143        writes: Vec<KnowledgeWrite>,
144        after_execution: KnowledgeSnapshot,
145    ) -> Self {
146        Self {
147            case: self.case.clone(),
148            stage: self.stage,
149            executor_id: self.executor_id.clone(),
150            evidence,
151            writes,
152            baseline: self.baseline.clone(),
153            after_execution,
154        }
155    }
156}
157
158/// Result of executing one decision-loop command through the runner boundary.
159#[derive(Debug)]
160#[non_exhaustive]
161pub enum DecisionRunnerTurn {
162    /// A `Replan` command completed reasoning and utility planning.
163    Planning(Box<DecisionPlanningReport>),
164    /// An action was executed, recorded, and evaluated by a verifier.
165    Outcome {
166        /// Audit receipt for the committed observations.
167        evidence: Box<DecisionEvidenceReceipt>,
168        /// Verification, adaptive-policy, and next-command report.
169        decision: Box<DecisionOutcomeReport>,
170    },
171    /// A terminal or human-review command requires no executor work.
172    Terminal(DecisionLoopCommand),
173}