ralph_workflow/reducer/event/review.rs
1// NOTE: split from reducer/event.rs to keep the main file under line limits.
2use serde::{Deserialize, Serialize};
3
4/// Review phase events.
5///
6/// Events related to code review passes and fix attempts. The review phase
7/// runs reviewer agents to identify issues and (by default) the same reviewer
8/// agent chain to apply any required fixes.
9///
10/// # State Transitions
11///
12/// - `PhaseStarted`: Sets phase to Review, resets pass counter
13/// - `PassStarted`: Resets agent chain for the pass
14/// - `Completed(issues_found=false)`: Advances to next pass or CommitMessage
15/// - `Completed(issues_found=true)`: Triggers fix attempt
16/// - `FixAttemptCompleted`: Transitions to CommitMessage
17/// - `PhaseCompleted`: Transitions to CommitMessage
18#[derive(Clone, Serialize, Deserialize, Debug)]
19pub enum ReviewEvent {
20 /// Review phase has started.
21 PhaseStarted,
22 /// A review pass has started.
23 PassStarted {
24 /// The pass number starting.
25 pass: u32,
26 },
27
28 /// Review context prepared for a pass.
29 ///
30 /// Emitted after `Effect::PrepareReviewContext` completes.
31 ContextPrepared {
32 /// The pass number the context was prepared for.
33 pass: u32,
34 },
35
36 /// Review prompt prepared for a pass.
37 ///
38 /// Emitted after `Effect::PrepareReviewPrompt` completes.
39 PromptPrepared {
40 pass: u32,
41 },
42
43 /// Reviewer agent was invoked for a pass.
44 ///
45 /// Emitted after `Effect::InvokeReviewAgent` completes.
46 AgentInvoked {
47 pass: u32,
48 },
49
50 /// Review issues XML exists and was read successfully for the pass.
51 ///
52 /// Emitted after `Effect::ExtractReviewIssuesXml` completes.
53 IssuesXmlExtracted {
54 pass: u32,
55 },
56 /// Review issues XML missing for the pass.
57 ///
58 /// Emitted after `Effect::ExtractReviewIssuesXml` when the XML was absent.
59 IssuesXmlMissing {
60 pass: u32,
61 /// The invalid output attempt count.
62 attempt: u32,
63 /// Error detail if file read failed for a reason other than NotFound.
64 error_detail: Option<String>,
65 },
66
67 /// Review issues XML validated for a pass.
68 ///
69 /// This event is an observation: the XML was valid and the handler determined
70 /// whether issues were found and whether this was an explicit clean-no-issues output.
71 IssuesXmlValidated {
72 pass: u32,
73 issues_found: bool,
74 clean_no_issues: bool,
75 issues: Vec<String>,
76 no_issues_found: Option<String>,
77 },
78
79 /// ISSUES.md was written for a pass.
80 IssuesMarkdownWritten {
81 pass: u32,
82 },
83
84 /// Review issue snippets were extracted for a pass.
85 IssueSnippetsExtracted {
86 pass: u32,
87 },
88
89 /// Review issues XML archived for a pass.
90 IssuesXmlArchived {
91 pass: u32,
92 },
93
94 /// Review issues XML cleaned before invoking the reviewer agent.
95 IssuesXmlCleaned {
96 pass: u32,
97 },
98
99 /// Fix prompt prepared for a review pass.
100 FixPromptPrepared {
101 pass: u32,
102 },
103
104 /// Fix agent was invoked for a review pass.
105 FixAgentInvoked {
106 pass: u32,
107 },
108
109 /// Fix result XML exists and was read successfully for the pass.
110 FixResultXmlExtracted {
111 pass: u32,
112 },
113 /// Fix result XML missing for the pass.
114 FixResultXmlMissing {
115 pass: u32,
116 /// The invalid output attempt count.
117 attempt: u32,
118 /// Detailed error message from file read failure (if not NotFound).
119 ///
120 /// This field is populated when the file exists but cannot be read
121 /// (e.g., permission denied, I/O error). It's None for simple NotFound.
122 error_detail: Option<String>,
123 },
124
125 /// Fix result XML validated for a pass.
126 FixResultXmlValidated {
127 pass: u32,
128 status: crate::reducer::state::FixStatus,
129 summary: Option<String>,
130 },
131
132 /// Fix result XML cleaned before invoking the fix agent.
133 FixResultXmlCleaned {
134 pass: u32,
135 },
136
137 /// Fix outcome applied for a pass.
138 FixOutcomeApplied {
139 pass: u32,
140 },
141
142 FixResultXmlArchived {
143 pass: u32,
144 },
145 /// A review pass completed with results.
146 Completed {
147 /// The pass number that completed.
148 pass: u32,
149 /// Whether issues were found requiring fixes.
150 issues_found: bool,
151 },
152 /// A fix attempt for issues has started.
153 FixAttemptStarted {
154 /// The pass number this fix is for.
155 pass: u32,
156 },
157 /// A fix attempt completed.
158 FixAttemptCompleted {
159 /// The pass number this fix was for.
160 pass: u32,
161 /// Whether changes were made.
162 changes_made: bool,
163 },
164 /// Review phase completed, all passes done.
165 PhaseCompleted {
166 /// Whether the phase exited early (before all passes).
167 early_exit: bool,
168 },
169 /// Review pass found no issues - clean exit.
170 ///
171 /// Emitted when a review pass completes with no issues found.
172 /// This is distinct from `Completed { issues_found: false }` in that
173 /// it explicitly signals a clean pass for UI/logging purposes.
174 PassCompletedClean {
175 /// The pass number that completed.
176 pass: u32,
177 },
178 /// Review output validation failed (XSD/XML parsing error).
179 ///
180 /// Emitted when review output cannot be parsed. Reducer decides
181 /// whether to retry or switch agents.
182 ///
183 /// The `error_detail` field contains the formatted validation error that will
184 /// be shown to the agent in the XSD retry prompt. This error should be
185 /// actionable and guide the agent toward producing valid XML.
186 OutputValidationFailed {
187 /// The pass number.
188 pass: u32,
189 /// Current invalid output attempt number.
190 attempt: u32,
191 /// Detailed error message from XSD validation, formatted for AI retry.
192 ///
193 /// This is the output of `XsdValidationError::format_for_ai_retry()` and
194 /// includes:
195 /// - Error type and location
196 /// - What was expected vs. what was found
197 /// - Actionable suggestions for fixing
198 /// - Examples of correct format
199 error_detail: Option<String>,
200 },
201
202 /// Fix attempt completed with incomplete status, needs continuation.
203 ///
204 /// Emitted when fix output is valid XML but indicates work is not complete
205 /// (status is "issues_remain"). Triggers a continuation with new session.
206 FixContinuationTriggered {
207 /// The pass number this fix was for.
208 pass: u32,
209 /// Status from the agent (typically IssuesRemain).
210 status: crate::reducer::state::FixStatus,
211 /// Summary of what was accomplished.
212 summary: Option<String>,
213 },
214
215 /// Fix continuation succeeded after multiple attempts.
216 ///
217 /// Emitted when a fix continuation finally reaches a complete state
218 /// (all_issues_addressed or no_issues_found).
219 FixContinuationSucceeded {
220 /// The pass number this fix was for.
221 pass: u32,
222 /// Total number of continuation attempts it took.
223 ///
224 /// Note: This field is not used by the reducer for state transitions, but
225 /// is kept for observability (event logs, checkpoint serialization, debugging).
226 total_attempts: u32,
227 },
228
229 /// Fix continuation budget exhausted.
230 ///
231 /// Emitted when fix continuations have been exhausted without reaching
232 /// a complete state. Policy decides whether to proceed to commit or abort.
233 FixContinuationBudgetExhausted {
234 /// The pass number this fix was for.
235 pass: u32,
236 /// Total number of continuation attempts made.
237 total_attempts: u32,
238 /// The last status received (typically IssuesRemain).
239 last_status: crate::reducer::state::FixStatus,
240 },
241
242 /// Fix output validation failed (XSD/XML parsing error).
243 ///
244 /// Emitted when fix output cannot be parsed. Reducer decides
245 /// whether to retry or switch agents.
246 FixOutputValidationFailed {
247 /// The pass number this fix was for.
248 pass: u32,
249 /// Current invalid output attempt number.
250 attempt: u32,
251 /// Detailed error message from XSD validation, formatted for AI retry.
252 ///
253 /// This is the output of `XsdValidationError::format_for_ai_retry()` and includes:
254 /// - Error type and location
255 /// - What was expected vs. what was found
256 /// - Actionable suggestions for fixing
257 /// - Examples of correct format
258 error_detail: Option<String>,
259 },
260}