Skip to main content

bot_forge/execution/
mod.rs

1//! Validated plan execution and external command boundary.
2//!
3//! [`select`] performs user-facing selection over a frozen plan, [`preview_plan`] detects current
4//! state, and [`execute`] performs the approved side effects. Execution consumes crate-level
5//! cancellation and telemetry capabilities, delegates installation mechanics to typed backends,
6//! and emits lifecycle facts through the neutral event capability before presentation.
7
8mod apt_mirror;
9pub(crate) mod command;
10pub(crate) mod environment;
11mod executor;
12pub(crate) mod install;
13pub(crate) mod managed_cargo;
14pub(crate) mod managed_git;
15pub(crate) mod policy;
16pub(crate) mod retry;
17pub(crate) mod runtime;
18pub(crate) mod scheduler;
19pub(crate) mod verifier;
20mod windows_job;
21
22use crate::events::{LifecycleEvent, emit as emit_lifecycle, is_active};
23use crate::ui::{progress_completed, progress_started, progress_update};
24
25#[cfg(test)]
26pub(crate) static TEST_ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
27
28/// Ordered progress facts produced while one selected plan is executing.
29pub(crate) enum ExecutionEvent {
30    Started {
31        step: usize,
32        total: usize,
33        component: String,
34    },
35    Progress {
36        step: usize,
37        total: usize,
38        component: String,
39        elapsed_secs: u64,
40        recent: String,
41    },
42    Completed {
43        step: usize,
44        total: usize,
45        component: String,
46        elapsed_secs: u64,
47    },
48}
49
50impl ExecutionEvent {
51    /// Convert UI progress into the corresponding machine lifecycle event.
52    pub(crate) fn lifecycle(&self) -> LifecycleEvent {
53        match self {
54            Self::Started { .. } => LifecycleEvent::Queued,
55            Self::Progress {
56                elapsed_secs,
57                recent,
58                ..
59            } => LifecycleEvent::Progress {
60                elapsed_ms: u128::from(*elapsed_secs) * 1000,
61                message: recent.clone(),
62            },
63            Self::Completed { elapsed_secs, .. } => LifecycleEvent::Completed {
64                elapsed_ms: u128::from(*elapsed_secs) * 1000,
65            },
66        }
67    }
68}
69
70/// Consumer of execution progress events.
71pub(crate) trait EventSink {
72    /// Receive one ordered progress event.
73    fn emit(&mut self, event: &ExecutionEvent);
74}
75
76/// Event sink that forwards machine events and renders human progress when JSONL is inactive.
77pub(crate) struct UiEventSink;
78
79impl EventSink for UiEventSink {
80    fn emit(&mut self, event: &ExecutionEvent) {
81        let component = match event {
82            ExecutionEvent::Started { component, .. }
83            | ExecutionEvent::Progress { component, .. }
84            | ExecutionEvent::Completed { component, .. } => component,
85        };
86        emit_lifecycle(None, Some(component), None, event.lifecycle());
87        if !is_active() {
88            match event {
89                ExecutionEvent::Started {
90                    step,
91                    total,
92                    component,
93                } => progress_started(*step, *total, component),
94                ExecutionEvent::Progress {
95                    step,
96                    total,
97                    component,
98                    elapsed_secs,
99                    recent,
100                } => progress_update(*step, *total, component, *elapsed_secs, recent),
101                ExecutionEvent::Completed {
102                    step,
103                    total,
104                    component,
105                    elapsed_secs,
106                } => progress_completed(*step, *total, component, *elapsed_secs),
107            }
108        }
109    }
110}
111
112pub use crate::execution::executor::{ExecutionSelection, PreparedExecution, execute, select};
113pub use crate::execution::verifier::preview_plan;