1use std::path::PathBuf;
8
9use clap::{Args, Subcommand};
10use serde::Serialize;
11
12use crate::commands::record::RecordProfile;
13
14#[derive(Debug, Clone, Args, Serialize)]
15pub struct TrackingArgs {
16 #[command(subcommand)]
17 pub command: TrackingCommand,
18}
19
20#[derive(Debug, Clone, Subcommand, Serialize)]
21pub enum TrackingCommand {
22 #[command(
26 after_help = "State payload replacement policy: this command targets the active payload contract only. Old state payload formats require one-off migration/repair outside the main CLI; no long-term v2 reader or mixed old/new stream reconciliation is provided."
27 )]
28 Status(Box<TrackingStatusArgs>),
29
30 Run(Box<TrackingRunArgs>),
32
33 Checkpoint(Box<TrackingCheckpointArgs>),
35
36 #[command(name = "close-ready")]
39 #[command(
40 after_help = "State payload replacement policy: this command targets the active payload contract only. Old state payload formats require one-off migration/repair outside the main CLI; no long-term v2 reader or mixed old/new stream reconciliation is provided."
41 )]
42 CloseReady(Box<TrackingCloseReadyArgs>),
43}
44
45#[derive(Debug, Clone, Args, Serialize)]
46pub struct TrackingRunArgs {
47 #[command(subcommand)]
48 pub command: TrackingRunCommand,
49}
50
51#[derive(Debug, Clone, Subcommand, Serialize)]
52pub enum TrackingRunCommand {
53 Init(Box<TrackingRunInitArgs>),
56
57 Update(Box<TrackingRunUpdateArgs>),
60}
61
62#[derive(Debug, Clone, Args, Serialize)]
63pub struct TrackingRunInitArgs {
64 #[arg(long = "provider-repo", value_name = "owner/repo")]
66 pub provider_repo: String,
67
68 #[arg(long, value_name = "number")]
70 pub issue: u64,
71
72 #[arg(long, value_enum, default_value_t = RecordProfile::Tracking)]
74 pub profile: RecordProfile,
75
76 #[arg(long, value_name = "dir")]
78 pub bundle: Option<PathBuf>,
79
80 #[arg(long = "execution-state-file", value_name = "path")]
82 pub execution_state_file: Option<PathBuf>,
83
84 #[arg(long, value_name = "id")]
86 pub task: Option<String>,
87
88 #[arg(long, value_name = "number")]
90 pub sprint: Option<i32>,
91
92 #[arg(long, value_name = "name")]
94 pub branch: Option<String>,
95
96 #[arg(long, value_name = "path")]
98 pub worktree: Option<PathBuf>,
99
100 #[arg(long = "linked-pr", value_name = "ref")]
102 pub linked_pr: Option<String>,
103
104 #[arg(long = "run-id", value_name = "id")]
106 pub run_id: Option<String>,
107
108 #[arg(long = "now", value_name = "rfc3339")]
112 pub now: Option<String>,
113
114 #[arg(long = "out", value_name = "path")]
116 pub out: Option<PathBuf>,
117}
118
119#[derive(Debug, Clone, Args, Serialize)]
120pub struct TrackingRunUpdateArgs {
121 #[arg(long = "run-state", value_name = "path")]
123 pub run_state: PathBuf,
124
125 #[arg(long, value_enum)]
127 pub phase: Option<RunPhaseArg>,
128
129 #[arg(long = "selected-task", value_name = "id")]
131 pub selected_task: Option<String>,
132
133 #[arg(long, value_name = "name")]
135 pub branch: Option<String>,
136
137 #[arg(long = "linked-pr", value_name = "ref")]
139 pub linked_pr: Option<String>,
140
141 #[arg(long = "validation-overall", value_name = "status")]
143 pub validation_overall: Option<String>,
144
145 #[arg(long = "validation-command", value_name = "command")]
147 pub validation_command: Option<String>,
148
149 #[arg(long = "validation-status", value_name = "status")]
151 pub validation_status: Option<String>,
152
153 #[arg(long = "validation-evidence", value_name = "path")]
155 pub validation_evidence: Option<String>,
156
157 #[arg(long = "review-decision", value_name = "decision")]
159 pub review_decision: Option<String>,
160
161 #[arg(long = "review-lens", value_name = "lens")]
163 pub review_lens: Vec<String>,
164
165 #[arg(long = "review-outcome-comment", value_name = "url-or-path")]
167 pub review_outcome_comment: Option<String>,
168
169 #[arg(long = "review-findings-file", value_name = "path")]
171 pub review_findings_file: Option<PathBuf>,
172
173 #[arg(long, value_name = "text")]
175 pub note: Option<String>,
176
177 #[arg(long = "now", value_name = "rfc3339")]
181 pub now: Option<String>,
182}
183
184#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, clap::ValueEnum)]
185pub enum RunPhaseArg {
186 Initial,
187 Implementing,
188 Validating,
189 Reviewing,
190 Blocked,
191 ReadyForClose,
192 Closed,
193}
194
195impl RunPhaseArg {
196 pub fn as_str(self) -> &'static str {
197 match self {
198 Self::Initial => "initial",
199 Self::Implementing => "implementing",
200 Self::Validating => "validating",
201 Self::Reviewing => "reviewing",
202 Self::Blocked => "blocked",
203 Self::ReadyForClose => "ready_for_close",
204 Self::Closed => "closed",
205 }
206 }
207}
208
209#[derive(Debug, Clone, Args, Serialize)]
210pub struct TrackingCheckpointArgs {
211 #[arg(long = "provider-repo", value_name = "owner/repo")]
213 pub provider_repo: Option<String>,
214
215 #[arg(long, value_name = "number")]
217 pub issue: Option<u64>,
218
219 #[arg(long, value_enum, default_value_t = RecordProfile::Tracking)]
221 pub profile: RecordProfile,
222
223 #[arg(long = "run-state", value_name = "path")]
225 pub run_state: PathBuf,
226
227 #[arg(long, value_name = "roles", default_value = "state")]
229 pub post: String,
230
231 #[arg(long = "repair-dashboard", default_value_t = false)]
233 pub repair_dashboard: bool,
234
235 #[arg(long, value_name = "dir")]
237 pub fixture: Option<PathBuf>,
238
239 #[arg(long = "body-file", value_name = "path")]
241 pub body_file: Option<PathBuf>,
242
243 #[arg(long = "comments-json", value_name = "path")]
245 pub comments_json: Option<PathBuf>,
246
247 #[arg(long = "live", default_value_t = false)]
259 pub live: bool,
260
261 #[arg(long = "expect-visible", default_value_t = true)]
263 pub expect_visible: bool,
264
265 #[arg(long = "rendered-out", value_name = "dir")]
268 pub rendered_out: Option<PathBuf>,
269}
270
271#[derive(Debug, Clone, Args, Serialize)]
272pub struct TrackingCloseReadyArgs {
273 #[arg(long = "provider-repo", value_name = "owner/repo")]
275 pub provider_repo: Option<String>,
276
277 #[arg(long, value_name = "number")]
279 pub issue: Option<u64>,
280
281 #[arg(long, value_enum, default_value_t = RecordProfile::Tracking)]
283 pub profile: RecordProfile,
284
285 #[arg(long = "run-state", value_name = "path")]
287 pub run_state: Option<PathBuf>,
288
289 #[arg(long = "linked-pr", value_name = "ref")]
291 pub linked_pr: Vec<String>,
292
293 #[arg(long, value_name = "text")]
295 pub approval: Option<String>,
296
297 #[arg(long, value_name = "dir")]
299 pub fixture: Option<PathBuf>,
300
301 #[arg(long = "body-file", value_name = "path")]
303 pub body_file: Option<PathBuf>,
304
305 #[arg(long = "comments-json", value_name = "path")]
307 pub comments_json: Option<PathBuf>,
308
309 #[arg(long = "expect-visible", default_value_t = true)]
311 pub expect_visible: bool,
312}
313
314#[derive(Debug, Clone, Args, Serialize)]
315pub struct TrackingStatusArgs {
316 #[arg(long, value_name = "owner/repo")]
318 pub provider_repo: Option<String>,
319
320 #[arg(long, value_name = "number")]
322 pub issue: Option<u64>,
323
324 #[arg(long, value_enum, default_value_t = RecordProfile::Tracking)]
326 pub profile: RecordProfile,
327
328 #[arg(long = "body-file", value_name = "path")]
330 pub body_file: Option<PathBuf>,
331
332 #[arg(long = "comments-json", value_name = "path")]
334 pub comments_json: Option<PathBuf>,
335
336 #[arg(long, value_name = "dir")]
338 pub fixture: Option<PathBuf>,
339
340 #[arg(long = "run-state", value_name = "path")]
342 pub run_state: Option<PathBuf>,
343
344 #[arg(long, value_name = "dir")]
346 pub bundle: Option<PathBuf>,
347
348 #[arg(long = "expect-visible", default_value_t = false)]
351 pub expect_visible: bool,
352}
353
354#[cfg(test)]
355mod tests {
356 use super::RunPhaseArg;
357 use pretty_assertions::assert_eq;
358
359 #[test]
363 fn run_phase_arg_as_str_matches_wire_contract() {
364 assert_eq!(RunPhaseArg::Initial.as_str(), "initial");
365 assert_eq!(RunPhaseArg::Implementing.as_str(), "implementing");
366 assert_eq!(RunPhaseArg::Validating.as_str(), "validating");
367 assert_eq!(RunPhaseArg::Reviewing.as_str(), "reviewing");
368 assert_eq!(RunPhaseArg::Blocked.as_str(), "blocked");
369 assert_eq!(RunPhaseArg::ReadyForClose.as_str(), "ready_for_close");
370 assert_eq!(RunPhaseArg::Closed.as_str(), "closed");
371 }
372}