Skip to main content

sloop/runner/
mod.rs

1//! Execution boundary for one flow stage at a time.
2//!
3//! The daemon decides what runs and in what order; the runner only executes
4//! one stage order and returns factual evidence. Runners never merge, access
5//! durable storage directly, or derive verdicts and run outcomes.
6
7use std::ffi::OsString;
8use std::path::PathBuf;
9
10pub mod local;
11
12#[derive(Debug, Clone)]
13pub struct StageOrder {
14    pub run_id: String,
15    pub stage: String,
16    pub execution: StageExecution,
17    pub worktree: PathBuf,
18    pub branch: String,
19    pub output_path: PathBuf,
20}
21
22#[derive(Debug, Clone)]
23pub enum StageExecution {
24    Agent(AgentLaunch),
25    Exec(ExecLaunch),
26}
27
28#[derive(Debug, Clone)]
29pub struct AgentLaunch {
30    pub argv: Vec<String>,
31    pub environment: Vec<(OsString, OsString)>,
32    pub repository: PathBuf,
33    pub worker_socket_path: PathBuf,
34}
35
36#[derive(Debug, Clone)]
37pub struct ExecLaunch {
38    pub argv: Vec<String>,
39    pub worker: Option<WorkerCredentials>,
40    /// Extra environment applied to the child. Empty for ordinary exec
41    /// stages; repair agents carry the agent environment here.
42    pub environment: Vec<(OsString, OsString)>,
43}
44
45#[derive(Debug, Clone, PartialEq, Eq)]
46pub struct WorkerCredentials {
47    pub socket: PathBuf,
48    pub token: String,
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub struct ProcessIdentity {
53    pub pid: u32,
54    pub start_time: Option<i64>,
55    pub process_group_id: u32,
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59pub struct ExecutionEvidence {
60    pub exit_code: Option<i32>,
61    pub started_at_ms: i64,
62    pub finished_at_ms: i64,
63    pub process: Option<ProcessIdentity>,
64    pub output_capture_complete: bool,
65    pub stragglers_killed: bool,
66}
67
68#[derive(Debug, Clone)]
69pub struct AgentProcessCheckpoint {
70    pub run_id: String,
71    pub branch: String,
72    pub worktree: PathBuf,
73    pub process: ProcessIdentity,
74    pub worker: WorkerCredentials,
75    pub started_at_ms: i64,
76}
77
78#[derive(Debug, Clone)]
79pub struct ExecProcessCheckpoint {
80    pub run_id: String,
81    pub stage: String,
82    pub process: ProcessIdentity,
83    pub started_at_ms: i64,
84}
85
86/// The only daemon-owned services available while a stage is executing.
87pub trait StageHooks {
88    type Error;
89
90    fn cancellation_requested(&self, run_id: &str) -> bool;
91
92    fn record_agent_process(&self, checkpoint: &AgentProcessCheckpoint) -> Result<(), Self::Error>;
93
94    fn record_exec_process(&self, checkpoint: &ExecProcessCheckpoint) -> Result<(), Self::Error>;
95}
96
97#[derive(Debug)]
98pub enum RunnerError<E> {
99    Execution(String),
100    Hook(E),
101}
102
103impl<E: std::fmt::Display> std::fmt::Display for RunnerError<E> {
104    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105        match self {
106            Self::Execution(message) => formatter.write_str(message),
107            Self::Hook(error) => error.fmt(formatter),
108        }
109    }
110}
111
112#[derive(Debug)]
113pub struct ExecutionFailure<E> {
114    pub evidence: ExecutionEvidence,
115    pub error: RunnerError<E>,
116}