Skip to main content

everruns_core/
dependency_blocker.rs

1// Dependency blocker detection
2//
3// Decision: Shared module in core so both workers and activities don't
4// duplicate harness/agent status checks and error messages.
5
6use crate::error::Result;
7use crate::traits::{AgentStore, HarnessStore};
8use crate::typed_id::{AgentId, HarnessId};
9use crate::{AgentStatus, HarnessStatus};
10
11/// Reason why execution was blocked before it started.
12#[derive(Debug, Clone, Copy)]
13pub enum DependencyBlocker {
14    HarnessArchived,
15    HarnessDeleted,
16    AgentArchived,
17    AgentDeleted,
18}
19
20impl DependencyBlocker {
21    /// User-facing message explaining why execution stopped.
22    pub fn message(self) -> &'static str {
23        match self {
24            DependencyBlocker::HarnessArchived => {
25                "Execution stopped because the assigned harness was archived."
26            }
27            DependencyBlocker::HarnessDeleted => {
28                "Execution stopped because the assigned harness was deleted."
29            }
30            DependencyBlocker::AgentArchived => {
31                "Execution stopped because the assigned agent was archived."
32            }
33            DependencyBlocker::AgentDeleted => {
34                "Execution stopped because the assigned agent was deleted."
35            }
36        }
37    }
38
39    /// Error code for events.
40    pub fn error_code(self) -> &'static str {
41        "dependency_unavailable"
42    }
43}
44
45/// Check if a harness/agent dependency is available for execution.
46///
47/// Returns `Some(blocker)` if execution should be blocked.
48pub async fn detect_dependency_blocker(
49    harness_store: &dyn HarnessStore,
50    agent_store: &dyn AgentStore,
51    harness_id: HarnessId,
52    agent_id: Option<AgentId>,
53) -> Result<Option<DependencyBlocker>> {
54    let harness_chain = harness_store.get_harness_chain(harness_id).await?;
55    match harness_chain.last() {
56        Some(leaf) => match leaf.status {
57            HarnessStatus::Active => {}
58            HarnessStatus::Archived => return Ok(Some(DependencyBlocker::HarnessArchived)),
59            HarnessStatus::Deleted => return Ok(Some(DependencyBlocker::HarnessDeleted)),
60        },
61        None => return Ok(Some(DependencyBlocker::HarnessDeleted)),
62    }
63
64    if let Some(agent_id) = agent_id {
65        match agent_store.get_agent(agent_id).await? {
66            Some(agent) => match agent.status {
67                AgentStatus::Active => {}
68                AgentStatus::Archived => return Ok(Some(DependencyBlocker::AgentArchived)),
69                AgentStatus::Deleted => return Ok(Some(DependencyBlocker::AgentDeleted)),
70            },
71            None => return Ok(Some(DependencyBlocker::AgentDeleted)),
72        }
73    }
74
75    Ok(None)
76}