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, value_name = "text")]
163 pub note: Option<String>,
164
165 #[arg(long = "now", value_name = "rfc3339")]
169 pub now: Option<String>,
170}
171
172#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, clap::ValueEnum)]
173pub enum RunPhaseArg {
174 Initial,
175 Implementing,
176 Validating,
177 Reviewing,
178 Blocked,
179 ReadyForClose,
180 Closed,
181}
182
183impl RunPhaseArg {
184 pub fn as_str(self) -> &'static str {
185 match self {
186 Self::Initial => "initial",
187 Self::Implementing => "implementing",
188 Self::Validating => "validating",
189 Self::Reviewing => "reviewing",
190 Self::Blocked => "blocked",
191 Self::ReadyForClose => "ready_for_close",
192 Self::Closed => "closed",
193 }
194 }
195}
196
197#[derive(Debug, Clone, Args, Serialize)]
198pub struct TrackingCheckpointArgs {
199 #[arg(long = "provider-repo", value_name = "owner/repo")]
201 pub provider_repo: Option<String>,
202
203 #[arg(long, value_name = "number")]
205 pub issue: Option<u64>,
206
207 #[arg(long, value_enum, default_value_t = RecordProfile::Tracking)]
209 pub profile: RecordProfile,
210
211 #[arg(long = "run-state", value_name = "path")]
213 pub run_state: PathBuf,
214
215 #[arg(long, value_name = "roles", default_value = "state")]
217 pub post: String,
218
219 #[arg(long = "repair-dashboard", default_value_t = false)]
221 pub repair_dashboard: bool,
222
223 #[arg(long, value_name = "dir")]
225 pub fixture: Option<PathBuf>,
226
227 #[arg(long = "body-file", value_name = "path")]
229 pub body_file: Option<PathBuf>,
230
231 #[arg(long = "comments-json", value_name = "path")]
233 pub comments_json: Option<PathBuf>,
234
235 #[arg(long = "live", default_value_t = false)]
247 pub live: bool,
248
249 #[arg(long = "expect-visible", default_value_t = true)]
251 pub expect_visible: bool,
252
253 #[arg(long = "rendered-out", value_name = "dir")]
256 pub rendered_out: Option<PathBuf>,
257}
258
259#[derive(Debug, Clone, Args, Serialize)]
260pub struct TrackingCloseReadyArgs {
261 #[arg(long = "provider-repo", value_name = "owner/repo")]
263 pub provider_repo: Option<String>,
264
265 #[arg(long, value_name = "number")]
267 pub issue: Option<u64>,
268
269 #[arg(long, value_enum, default_value_t = RecordProfile::Tracking)]
271 pub profile: RecordProfile,
272
273 #[arg(long = "run-state", value_name = "path")]
275 pub run_state: Option<PathBuf>,
276
277 #[arg(long = "linked-pr", value_name = "ref")]
279 pub linked_pr: Vec<String>,
280
281 #[arg(long, value_name = "text")]
283 pub approval: Option<String>,
284
285 #[arg(long, value_name = "dir")]
287 pub fixture: Option<PathBuf>,
288
289 #[arg(long = "body-file", value_name = "path")]
291 pub body_file: Option<PathBuf>,
292
293 #[arg(long = "comments-json", value_name = "path")]
295 pub comments_json: Option<PathBuf>,
296
297 #[arg(long = "expect-visible", default_value_t = true)]
299 pub expect_visible: bool,
300}
301
302#[derive(Debug, Clone, Args, Serialize)]
303pub struct TrackingStatusArgs {
304 #[arg(long, value_name = "owner/repo")]
306 pub provider_repo: Option<String>,
307
308 #[arg(long, value_name = "number")]
310 pub issue: Option<u64>,
311
312 #[arg(long, value_enum, default_value_t = RecordProfile::Tracking)]
314 pub profile: RecordProfile,
315
316 #[arg(long = "body-file", value_name = "path")]
318 pub body_file: Option<PathBuf>,
319
320 #[arg(long = "comments-json", value_name = "path")]
322 pub comments_json: Option<PathBuf>,
323
324 #[arg(long, value_name = "dir")]
326 pub fixture: Option<PathBuf>,
327
328 #[arg(long = "run-state", value_name = "path")]
330 pub run_state: Option<PathBuf>,
331
332 #[arg(long, value_name = "dir")]
334 pub bundle: Option<PathBuf>,
335
336 #[arg(long = "expect-visible", default_value_t = false)]
339 pub expect_visible: bool,
340}
341
342#[cfg(test)]
343mod tests {
344 use super::RunPhaseArg;
345 use pretty_assertions::assert_eq;
346
347 #[test]
351 fn run_phase_arg_as_str_matches_wire_contract() {
352 assert_eq!(RunPhaseArg::Initial.as_str(), "initial");
353 assert_eq!(RunPhaseArg::Implementing.as_str(), "implementing");
354 assert_eq!(RunPhaseArg::Validating.as_str(), "validating");
355 assert_eq!(RunPhaseArg::Reviewing.as_str(), "reviewing");
356 assert_eq!(RunPhaseArg::Blocked.as_str(), "blocked");
357 assert_eq!(RunPhaseArg::ReadyForClose.as_str(), "ready_for_close");
358 assert_eq!(RunPhaseArg::Closed.as_str(), "closed");
359 }
360}