1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
6#[serde(rename_all = "snake_case")]
7pub enum SpecStatus {
8 #[default]
9 Pending,
10 InProgress,
11 Paused,
12 Completed,
13 Failed,
14 NeedsAttention,
15 Ready,
16 Blocked,
17 Cancelled,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
22#[serde(rename_all = "snake_case")]
23pub enum ApprovalStatus {
24 #[default]
25 Pending,
26 Approved,
27 Rejected,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize, Default)]
32pub struct Approval {
33 #[serde(default)]
35 pub required: bool,
36 #[serde(default)]
38 pub status: ApprovalStatus,
39 #[serde(skip_serializing_if = "Option::is_none")]
41 pub by: Option<String>,
42 #[serde(skip_serializing_if = "Option::is_none")]
44 pub at: Option<String>,
45}
46
47#[derive(Debug, Clone)]
49pub struct BlockingDependency {
50 pub spec_id: String,
52 pub title: Option<String>,
54 pub status: SpecStatus,
56 pub completed_at: Option<String>,
58 pub is_sibling: bool,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct SpecFrontmatter {
64 #[serde(default = "default_type")]
65 pub r#type: String,
66 #[serde(default)]
67 pub status: SpecStatus,
68 #[serde(skip_serializing_if = "Option::is_none")]
69 pub depends_on: Option<Vec<String>>,
70 #[serde(skip_serializing_if = "Option::is_none")]
71 pub labels: Option<Vec<String>>,
72 #[serde(skip_serializing_if = "Option::is_none")]
73 pub target_files: Option<Vec<String>>,
74 #[serde(skip_serializing_if = "Option::is_none")]
75 pub context: Option<Vec<String>>,
76 #[serde(skip_serializing_if = "Option::is_none")]
77 pub prompt: Option<String>,
78 #[serde(skip_serializing_if = "Option::is_none")]
79 pub branch: Option<String>,
80 #[serde(skip_serializing_if = "Option::is_none")]
81 pub commits: Option<Vec<String>>,
82 #[serde(skip_serializing_if = "Option::is_none")]
83 pub completed_at: Option<String>,
84 #[serde(skip_serializing_if = "Option::is_none")]
85 pub model: Option<String>,
86 #[serde(skip_serializing_if = "Option::is_none")]
88 pub tracks: Option<Vec<String>>,
89 #[serde(skip_serializing_if = "Option::is_none")]
91 pub informed_by: Option<Vec<String>>,
92 #[serde(skip_serializing_if = "Option::is_none")]
93 pub origin: Option<Vec<String>>,
94 #[serde(skip_serializing_if = "Option::is_none")]
95 pub schedule: Option<String>,
96 #[serde(skip_serializing_if = "Option::is_none")]
98 pub source_branch: Option<String>,
99 #[serde(skip_serializing_if = "Option::is_none")]
100 pub target_branch: Option<String>,
101 #[serde(skip_serializing_if = "Option::is_none")]
102 pub conflicting_files: Option<Vec<String>>,
103 #[serde(skip_serializing_if = "Option::is_none")]
104 pub blocked_specs: Option<Vec<String>>,
105 #[serde(skip_serializing_if = "Option::is_none")]
106 pub original_spec: Option<String>,
107 #[serde(skip_serializing_if = "Option::is_none")]
109 pub last_verified: Option<String>,
110 #[serde(skip_serializing_if = "Option::is_none")]
111 pub verification_status: Option<String>,
112 #[serde(skip_serializing_if = "Option::is_none")]
113 pub verification_failures: Option<Vec<String>>,
114 #[serde(skip_serializing_if = "Option::is_none")]
116 pub replayed_at: Option<String>,
117 #[serde(skip_serializing_if = "Option::is_none")]
118 pub replay_count: Option<u32>,
119 #[serde(skip_serializing_if = "Option::is_none")]
120 pub original_completed_at: Option<String>,
121 #[serde(skip_serializing_if = "Option::is_none")]
123 pub derived_fields: Option<Vec<String>>,
124 #[serde(skip_serializing_if = "Option::is_none")]
126 pub approval: Option<Approval>,
127 #[serde(skip_serializing_if = "Option::is_none")]
129 pub members: Option<Vec<String>>,
130 #[serde(skip_serializing_if = "Option::is_none")]
132 pub output_schema: Option<String>,
133 #[serde(skip_serializing_if = "Option::is_none")]
135 pub public: Option<bool>,
136 #[serde(skip_serializing_if = "Option::is_none")]
138 pub retry_state: Option<crate::retry::RetryState>,
139}
140
141pub(crate) fn default_type() -> String {
142 "code".to_string()
143}
144
145impl Default for SpecFrontmatter {
146 fn default() -> Self {
147 Self {
148 r#type: default_type(),
149 status: SpecStatus::Pending,
150 depends_on: None,
151 labels: None,
152 target_files: None,
153 context: None,
154 prompt: None,
155 branch: None,
156 commits: None,
157 completed_at: None,
158 model: None,
159 tracks: None,
160 informed_by: None,
161 origin: None,
162 schedule: None,
163 source_branch: None,
164 target_branch: None,
165 conflicting_files: None,
166 blocked_specs: None,
167 original_spec: None,
168 last_verified: None,
169 verification_status: None,
170 verification_failures: None,
171 replayed_at: None,
172 replay_count: None,
173 original_completed_at: None,
174 derived_fields: None,
175 approval: None,
176 members: None,
177 output_schema: None,
178 public: None,
179 retry_state: None,
180 }
181 }
182}