Skip to main content

ito_core/orchestrate/
gates.rs

1use crate::orchestrate::plan::PlannedGate;
2use crate::orchestrate::types::GatePolicy;
3
4/// Non-skippable gate proving a dispatch checkout is based on accepted main.
5pub const GATE_IMPLEMENTATION_READINESS: &str = "implementation-readiness";
6pub(crate) const GATE_APPLY_COMPLETE: &str = "apply-complete";
7pub(crate) const GATE_FORMAT: &str = "format";
8pub(crate) const GATE_LINT: &str = "lint";
9pub(crate) const GATE_TESTS: &str = "tests";
10pub(crate) const GATE_STYLE: &str = "style";
11pub(crate) const GATE_CODE_REVIEW: &str = "code-review";
12pub(crate) const GATE_SECURITY_REVIEW: &str = "security-review";
13
14/// Default orchestrator gate order.
15pub fn default_gate_order() -> Vec<String> {
16    vec![
17        GATE_IMPLEMENTATION_READINESS.to_string(),
18        GATE_APPLY_COMPLETE.to_string(),
19        GATE_FORMAT.to_string(),
20        GATE_LINT.to_string(),
21        GATE_TESTS.to_string(),
22        GATE_STYLE.to_string(),
23        GATE_CODE_REVIEW.to_string(),
24        GATE_SECURITY_REVIEW.to_string(),
25    ]
26}
27
28/// Return a gate order with implementation readiness present exactly once at the front.
29pub fn ensure_implementation_readiness_first(gates: &[String]) -> Vec<String> {
30    let mut ordered = gates.to_vec();
31    ordered.retain(|gate| gate != GATE_IMPLEMENTATION_READINESS);
32    ordered.insert(0, GATE_IMPLEMENTATION_READINESS.to_string());
33    ordered
34}
35
36pub(crate) fn ensure_planned_implementation_readiness_first(
37    gates: &[PlannedGate],
38) -> Vec<PlannedGate> {
39    let mut ordered = gates.to_vec();
40    ordered.retain(|gate| gate.name != GATE_IMPLEMENTATION_READINESS);
41    ordered.insert(
42        0,
43        PlannedGate {
44            name: GATE_IMPLEMENTATION_READINESS.to_string(),
45            policy: GatePolicy::Run,
46        },
47    );
48    ordered
49}
50
51/// Minimal remediation payload constructed after a gate failure.
52#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
53pub struct RemediationPacket {
54    /// Canonical change id.
55    pub change_id: String,
56    /// Gate that failed.
57    pub failed_gate: String,
58    /// Error payload captured from the gate.
59    pub error: String,
60    /// Gate names to rerun (readiness + failed gate + downstream run gates).
61    pub rerun_gates: Vec<String>,
62}
63
64/// Construct a remediation packet for a specific gate failure.
65pub fn remediation_packet_for_failure(
66    change_id: &str,
67    gates: &[PlannedGate],
68    failed_gate: &str,
69    error: &str,
70) -> RemediationPacket {
71    let gates = ensure_planned_implementation_readiness_first(gates);
72    let failed_index = gates.iter().position(|gate| gate.name == failed_gate);
73    let rerun = match failed_index {
74        Some(failed_index) => {
75            let mut rerun = vec![GATE_IMPLEMENTATION_READINESS.to_string()];
76            for gate in gates.iter().skip(failed_index) {
77                if gate.name == GATE_IMPLEMENTATION_READINESS {
78                    continue;
79                }
80                if gate.name == failed_gate || gate.policy == GatePolicy::Run {
81                    rerun.push(gate.name.clone());
82                }
83            }
84            rerun
85        }
86        None => Vec::new(),
87    };
88
89    RemediationPacket {
90        change_id: change_id.to_string(),
91        failed_gate: failed_gate.to_string(),
92        error: error.to_string(),
93        rerun_gates: rerun,
94    }
95}
96
97#[cfg(test)]
98#[path = "gates_tests.rs"]
99mod gates_tests;