Skip to main content

ic_backup/policy/effect_order/
mod.rs

1//! Pure readiness over declared completion identities; no completion proof or dispatch.
2
3use crate::model::{
4    artifacts::ArtifactChecksumRecord,
5    effect_graph::{EffectGraphError, EffectGraphRecord, EffectNodeRecord, MAX_EFFECT_OPERATIONS},
6};
7use std::collections::BTreeSet;
8use thiserror::Error;
9
10/// Passive declared progress supplied by the qualified receipt-owning caller.
11#[derive(Clone, Debug)]
12pub struct EffectProgressRequest {
13    /// Exact graph operations whose prerequisites and completion evidence the caller owns.
14    pub completed_operations: Vec<u64>,
15}
16
17/// Read-only dependency projection; readiness grants no effect authority or paid allowance.
18#[derive(Clone, Debug)]
19pub struct EffectReadinessView<'a> {
20    /// Exact original full dependency graph digest.
21    pub graph: ArtifactChecksumRecord,
22    /// Number of admitted declared completed operations, not authenticated receipts.
23    pub completed_operations: usize,
24    /// Incomplete nodes with every declared prerequisite completed, in planning order.
25    pub ready: Vec<&'a EffectNodeRecord>,
26    /// Incomplete nodes with at least one uncompleted prerequisite, in planning order.
27    pub blocked: Vec<&'a EffectNodeRecord>,
28}
29
30/// Validate causal declared progress and project ready/blocked nodes without IO.
31///
32/// The caller owns qualified actual receipts. Declared completion never establishes
33/// current authority, command custody, settlement or full terminal completion.
34///
35/// # Errors
36/// Rejects excessive/duplicate/unknown completed identities and unmet prerequisites.
37pub fn readiness<'a>(
38    graph: &'a EffectGraphRecord,
39    request: &EffectProgressRequest,
40) -> Result<EffectReadinessView<'a>, EffectOrderError> {
41    if request.completed_operations.len() > MAX_EFFECT_OPERATIONS {
42        return Err(EffectOrderError::TooManyCompleted);
43    }
44    let mut completed = BTreeSet::new();
45    for sequence in &request.completed_operations {
46        graph.node(*sequence)?;
47        if !completed.insert(*sequence) {
48            return Err(EffectOrderError::DuplicateCompleted(*sequence));
49        }
50    }
51    for sequence in &completed {
52        let node = graph.node(*sequence)?;
53        if let Some(dependency) = node
54            .depends_on()
55            .iter()
56            .find(|dependency| !completed.contains(dependency))
57        {
58            return Err(EffectOrderError::UnmetDependency {
59                operation_sequence: *sequence,
60                dependency: *dependency,
61            });
62        }
63    }
64    let mut ready = Vec::new();
65    let mut blocked = Vec::new();
66    for node in graph
67        .ordered_nodes()
68        .filter(|node| !completed.contains(&node.operation_sequence()))
69    {
70        if node
71            .depends_on()
72            .iter()
73            .all(|dependency| completed.contains(dependency))
74        {
75            ready.push(node);
76        } else {
77            blocked.push(node);
78        }
79    }
80    Ok(EffectReadinessView {
81        graph: graph.digest(),
82        completed_operations: completed.len(),
83        ready,
84        blocked,
85    })
86}
87
88/// Typed declared progress inconsistency, before scheduling or effects.
89#[derive(Debug, Error)]
90pub enum EffectOrderError {
91    /// Declared completed list exceeds the maintained graph bound.
92    #[error("completed effects exceed {MAX_EFFECT_OPERATIONS} operations")]
93    TooManyCompleted,
94    /// One exact operation is declared completed more than once.
95    #[error("duplicate completed effect {0}")]
96    DuplicateCompleted(u64),
97    /// A declared completed operation lacks a declared completed prerequisite.
98    #[error("completed effect {operation_sequence} has unmet dependency {dependency}")]
99    UnmetDependency {
100        /// Exact declared completed operation.
101        operation_sequence: u64,
102        /// Missing declared completed prerequisite.
103        dependency: u64,
104    },
105    /// Exact operation identity is absent from its owning graph.
106    #[error(transparent)]
107    Graph(#[from] EffectGraphError),
108}
109
110#[cfg(test)]
111mod tests;