Skip to main content

everruns_core/
execution_loading.rs

1//! Neutral contracts for loading turn execution inputs.
2
3use crate::agent_definition::AgentDefinition;
4use crate::error::Result;
5use crate::harness_definition::HarnessDefinition;
6use crate::session::ExecutionSession;
7use crate::typed_id::{AgentId, HarnessId, SessionId};
8use async_trait::async_trait;
9
10/// Narrow agent-loading seam for turn execution (EVE-872, EVE-877).
11///
12/// Implementations project their stored agent records into the portable
13/// [`AgentDefinition`] — the authored execution configuration. Stored
14/// `Agent`/`AgentVersion` persistence records live in `everruns-platform` and
15/// never cross this boundary.
16///
17/// Contract: records that exist but cannot execute (archived or deleted) must
18/// yield an error from [`AgentStore::get_agent`], so lifecycle validation is
19/// enforced at the loading seam before host execution begins.
20#[async_trait]
21pub trait AgentStore: Send + Sync {
22    /// Get the portable execution definition for an agent by public id.
23    async fn get_agent(&self, agent_id: AgentId) -> Result<Option<AgentDefinition>>;
24
25    /// Execution-availability probe for dependency-blocker detection.
26    ///
27    /// Returns `None` when the agent exists and can execute. Hosted stores
28    /// override this to report
29    /// [`DependencyBlocker::AgentArchived`](crate::dependency_blocker::DependencyBlocker::AgentArchived) /
30    /// [`DependencyBlocker::AgentDeleted`](crate::dependency_blocker::DependencyBlocker::AgentDeleted)
31    /// from the stored lifecycle status;
32    /// the default treats a missing record as deleted.
33    async fn get_agent_blocker(
34        &self,
35        agent_id: AgentId,
36    ) -> Result<Option<crate::dependency_blocker::DependencyBlocker>> {
37        Ok(match self.get_agent(agent_id).await? {
38            Some(_) => None,
39            None => Some(crate::dependency_blocker::DependencyBlocker::AgentDeleted),
40        })
41    }
42}
43
44#[async_trait]
45impl<T: AgentStore + ?Sized> AgentStore for std::sync::Arc<T> {
46    async fn get_agent(&self, agent_id: AgentId) -> Result<Option<AgentDefinition>> {
47        (**self).get_agent(agent_id).await
48    }
49
50    async fn get_agent_blocker(
51        &self,
52        agent_id: AgentId,
53    ) -> Result<Option<crate::dependency_blocker::DependencyBlocker>> {
54        (**self).get_agent_blocker(agent_id).await
55    }
56}
57
58// ============================================================================
59// HarnessStore - For retrieving harness execution configurations
60// ============================================================================
61
62/// Narrow harness-loading seam for turn execution (EVE-872, EVE-881).
63///
64/// Implementations project their stored harness records into the portable
65/// [`HarnessDefinition`] — the effective execution environment configuration.
66/// Parent-chain inheritance is resolved *behind* this seam (root-to-leaf, via
67/// the same overlay merge semantics the runtime uses), so callers receive one
68/// effective layer. Stored `Harness` persistence records live in
69/// `everruns-platform` and never cross this boundary.
70///
71/// Contract: records that exist but cannot execute (archived or deleted) must
72/// yield an error from [`HarnessStore::get_harness`], so lifecycle validation
73/// is enforced at the loading seam before host execution begins.
74#[async_trait]
75pub trait HarnessStore: Send + Sync {
76    /// Get the effective (inheritance-resolved) execution definition for a
77    /// harness by id. Returns `Ok(None)` if the harness does not exist.
78    async fn get_harness(&self, harness_id: HarnessId) -> Result<Option<HarnessDefinition>>;
79
80    /// Execution-availability probe for dependency-blocker detection.
81    ///
82    /// Returns `None` when the harness exists and can execute. Hosted stores
83    /// override this to report [`DependencyBlocker::HarnessArchived`] /
84    /// [`DependencyBlocker::HarnessDeleted`] from the stored lifecycle status;
85    /// the default treats a missing record as deleted.
86    ///
87    /// [`DependencyBlocker::HarnessArchived`]: crate::dependency_blocker::DependencyBlocker::HarnessArchived
88    /// [`DependencyBlocker::HarnessDeleted`]: crate::dependency_blocker::DependencyBlocker::HarnessDeleted
89    async fn get_harness_blocker(
90        &self,
91        harness_id: HarnessId,
92    ) -> Result<Option<crate::dependency_blocker::DependencyBlocker>> {
93        Ok(match self.get_harness(harness_id).await? {
94            Some(_) => None,
95            None => Some(crate::dependency_blocker::DependencyBlocker::HarnessDeleted),
96        })
97    }
98}
99
100#[async_trait]
101impl<T: HarnessStore + ?Sized> HarnessStore for std::sync::Arc<T> {
102    async fn get_harness(&self, harness_id: HarnessId) -> Result<Option<HarnessDefinition>> {
103        (**self).get_harness(harness_id).await
104    }
105
106    async fn get_harness_blocker(
107        &self,
108        harness_id: HarnessId,
109    ) -> Result<Option<crate::dependency_blocker::DependencyBlocker>> {
110        (**self).get_harness_blocker(harness_id).await
111    }
112}
113
114// ============================================================================
115// SessionStore - For retrieving session information
116// ============================================================================
117
118/// Narrow session-loading seam for turn execution (EVE-872, EVE-882).
119///
120/// Implementations project their stored session records into the portable
121/// [`ExecutionSession`] — the session correlation values, per-session
122/// configuration layer, and neutral execution state a turn consumes. The
123/// persisted `Session` aggregate (facets, participants, ownership summaries,
124/// timestamps, UI metadata) lives in `everruns-platform` and never crosses
125/// this boundary.
126#[async_trait]
127pub trait SessionStore: Send + Sync {
128    /// Get the portable execution view for a session by ID.
129    async fn get_session(&self, session_id: SessionId) -> Result<Option<ExecutionSession>>;
130}
131
132#[async_trait]
133impl<T: SessionStore + ?Sized> SessionStore for std::sync::Arc<T> {
134    async fn get_session(&self, session_id: SessionId) -> Result<Option<ExecutionSession>> {
135        (**self).get_session(session_id).await
136    }
137}
138
139// EVE-897: `SessionMutator` moved to `everruns-platform`. Mutating stored
140// session metadata is a hosted control-plane service; the capability that
141// uses it resolves `SessionMutatorExt` from the typed extension bag.