ralph_workflow/checkpoint/
run_context.rs1use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct RunContext {
19 pub run_id: String,
21 pub parent_run_id: Option<String>,
23 pub resume_count: u32,
25 pub actual_developer_runs: u32,
27 pub actual_reviewer_runs: u32,
29}
30
31impl RunContext {
32 pub fn new() -> Self {
34 Self {
35 run_id: uuid::Uuid::new_v4().to_string(),
36 parent_run_id: None,
37 resume_count: 0,
38 actual_developer_runs: 0,
39 actual_reviewer_runs: 0,
40 }
41 }
42
43 pub fn from_checkpoint(checkpoint: &super::PipelineCheckpoint) -> Self {
45 Self {
46 run_id: uuid::Uuid::new_v4().to_string(), parent_run_id: Some(checkpoint.run_id.clone()),
48 resume_count: checkpoint.resume_count + 1,
49 actual_developer_runs: checkpoint.actual_developer_runs,
50 actual_reviewer_runs: checkpoint.actual_reviewer_runs,
51 }
52 }
53
54 #[cfg(test)]
56 pub fn with_developer_runs(mut self, runs: u32) -> Self {
57 self.actual_developer_runs = runs;
58 self
59 }
60
61 #[cfg(test)]
63 pub fn with_reviewer_runs(mut self, runs: u32) -> Self {
64 self.actual_reviewer_runs = runs;
65 self
66 }
67
68 pub fn record_developer_iteration(&mut self) {
70 self.actual_developer_runs += 1;
71 }
72
73 pub fn record_reviewer_pass(&mut self) {
75 self.actual_reviewer_runs += 1;
76 }
77}
78
79impl Default for RunContext {
80 fn default() -> Self {
81 Self::new()
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88 use crate::checkpoint::state::{
89 AgentConfigSnapshot, CheckpointParams, CliArgsSnapshot, PipelineCheckpoint, PipelinePhase,
90 RebaseState,
91 };
92
93 #[test]
94 fn test_run_context_new() {
95 let ctx = RunContext::new();
96 assert!(!ctx.run_id.is_empty());
97 assert!(ctx.parent_run_id.is_none());
98 assert_eq!(ctx.resume_count, 0);
99 assert_eq!(ctx.actual_developer_runs, 0);
100 assert_eq!(ctx.actual_reviewer_runs, 0);
101 }
102
103 #[test]
104 fn test_run_context_from_checkpoint() {
105 let cli_args =
107 CliArgsSnapshot::new(5, 2, "test".to_string(), None, false, true, 2, false, None);
108 let dev_config =
109 AgentConfigSnapshot::new("claude".into(), "cmd".into(), "-o".into(), None, true);
110 let rev_config =
111 AgentConfigSnapshot::new("codex".into(), "cmd".into(), "-o".into(), None, true);
112
113 let original_run_id = uuid::Uuid::new_v4().to_string();
114 let checkpoint = PipelineCheckpoint::from_params(CheckpointParams {
115 phase: PipelinePhase::Development,
116 iteration: 2,
117 total_iterations: 5,
118 reviewer_pass: 0,
119 total_reviewer_passes: 2,
120 developer_agent: "claude",
121 reviewer_agent: "codex",
122 cli_args,
123 developer_agent_config: dev_config,
124 reviewer_agent_config: rev_config,
125 rebase_state: RebaseState::default(),
126 git_user_name: None,
127 git_user_email: None,
128 run_id: &original_run_id,
129 parent_run_id: None,
130 resume_count: 1,
131 actual_developer_runs: 2,
132 actual_reviewer_runs: 0,
133 });
134
135 let run_ctx = RunContext::from_checkpoint(&checkpoint);
136
137 assert_ne!(
138 run_ctx.run_id, original_run_id,
139 "new run_id should be generated"
140 );
141 assert_eq!(run_ctx.parent_run_id, Some(original_run_id));
142 assert_eq!(run_ctx.resume_count, 2, "resume_count should increment");
143 assert_eq!(run_ctx.actual_developer_runs, 2);
144 assert_eq!(run_ctx.actual_reviewer_runs, 0);
145 }
146
147 #[test]
148 fn test_run_context_with_developer_runs() {
149 let ctx = RunContext::new().with_developer_runs(5);
150 assert_eq!(ctx.actual_developer_runs, 5);
151 }
152
153 #[test]
154 fn test_run_context_with_reviewer_runs() {
155 let ctx = RunContext::new().with_reviewer_runs(3);
156 assert_eq!(ctx.actual_reviewer_runs, 3);
157 }
158}