Skip to main content

codetether_agent/ralph/
state_store.rs

1//! Ralph State Store - trait and types for persistent run state
2//!
3//! Abstracts Ralph run state persistence so it can be backed by
4//! in-memory storage (tests/local), HTTP API (dashboard integration),
5//! or any future backend.
6
7use async_trait::async_trait;
8use serde::{Deserialize, Serialize};
9
10use super::types::{Prd, ProgressEntry, RalphConfig, RalphStatus};
11
12/// Persistent state for a single Ralph run
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct RalphRunState {
15    pub run_id: String,
16    pub okr_id: Option<String>,
17    pub prd: Prd,
18    pub config: RalphConfig,
19    pub status: RalphStatus,
20    pub current_iteration: usize,
21    pub max_iterations: usize,
22    #[serde(default)]
23    pub progress_log: Vec<ProgressEntry>,
24    #[serde(default)]
25    pub story_results: Vec<StoryResultEntry>,
26    pub error: Option<String>,
27    pub created_at: String,
28    pub started_at: Option<String>,
29    pub completed_at: Option<String>,
30}
31
32/// Result of a single story execution
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct StoryResultEntry {
35    pub story_id: String,
36    pub title: String,
37    pub passed: bool,
38    pub iteration: usize,
39    pub error: Option<String>,
40}
41
42/// Lightweight summary for listing runs
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct RalphRunSummary {
45    pub run_id: String,
46    pub okr_id: Option<String>,
47    pub status: RalphStatus,
48    pub passed: usize,
49    pub total: usize,
50    pub current_iteration: usize,
51    pub created_at: String,
52}
53
54/// Abstraction over Ralph run state persistence.
55///
56/// All methods are fire-and-forget from the caller's perspective —
57/// implementations should log errors internally rather than failing
58/// the Ralph pipeline.
59#[async_trait]
60pub trait RalphStateStore: Send + Sync {
61    /// Create a new run record
62    async fn create_run(&self, state: &RalphRunState) -> anyhow::Result<()>;
63
64    /// Update the run status
65    async fn update_status(&self, run_id: &str, status: RalphStatus) -> anyhow::Result<()>;
66
67    /// Record iteration progress
68    async fn update_iteration(&self, run_id: &str, iteration: usize) -> anyhow::Result<()>;
69
70    /// Record a story result
71    async fn record_story_result(
72        &self,
73        run_id: &str,
74        result: &StoryResultEntry,
75    ) -> anyhow::Result<()>;
76
77    /// Append a progress log entry
78    async fn append_progress(&self, run_id: &str, entry: &ProgressEntry) -> anyhow::Result<()>;
79
80    /// Update the PRD (e.g. after marking stories passed)
81    async fn update_prd(&self, run_id: &str, prd: &Prd) -> anyhow::Result<()>;
82
83    /// Set error message
84    async fn set_error(&self, run_id: &str, error: &str) -> anyhow::Result<()>;
85
86    /// Mark run as completed
87    async fn complete_run(&self, run_id: &str, status: RalphStatus) -> anyhow::Result<()>;
88
89    /// Get full run state
90    async fn get_run(&self, run_id: &str) -> anyhow::Result<Option<RalphRunState>>;
91
92    /// List all runs (summary only)
93    async fn list_runs(&self) -> anyhow::Result<Vec<RalphRunSummary>>;
94}