1use std::path::PathBuf;
10
11use serde::{Deserialize, Serialize};
12
13#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15pub struct WorkerAttempt {
16 pub number: u32,
17 pub outcome: String,
18 pub summary: String,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
23pub struct WorkerAssignment {
24 pub id: String,
26 pub title: String,
28 pub description: String,
30 pub design: Option<String>,
32 pub acceptance: Option<String>,
34 pub verify: Option<String>,
36 pub verify_timeout_secs: Option<u64>,
38 pub fail_first: bool,
40 pub notes: Option<String>,
42 pub decisions: Vec<String>,
44 pub dependencies: Vec<String>,
46 pub paths: Vec<String>,
48 pub files: Vec<String>,
50 pub attempts: Vec<WorkerAttempt>,
52 pub workspace_root: PathBuf,
54 pub model: Option<String>,
56}
57
58#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
60pub struct WorkerResult {
61 pub unit_id: String,
63 pub status: WorkerStatus,
65 pub summary: Option<String>,
67 pub error: Option<String>,
69 pub tool_count: usize,
71 pub turns: usize,
73 pub tokens: Option<u64>,
75 pub cost: Option<f64>,
77 pub model: Option<String>,
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
83#[serde(rename_all = "snake_case")]
84pub enum WorkerStatus {
85 AwaitingVerify,
89 Completed,
91 Blocked,
93 Failed,
95 Cancelled,
97}
98
99impl WorkerStatus {
100 pub fn as_str(self) -> &'static str {
101 match self {
102 Self::AwaitingVerify => "awaiting_verify",
103 Self::Completed => "completed",
104 Self::Blocked => "blocked",
105 Self::Failed => "failed",
106 Self::Cancelled => "cancelled",
107 }
108 }
109
110 pub fn lifecycle_label(self) -> &'static str {
111 match self {
112 Self::AwaitingVerify => "candidate complete ยท awaiting verify",
113 Self::Completed => "completed",
114 Self::Blocked => "blocked",
115 Self::Failed => "failed",
116 Self::Cancelled => "cancelled",
117 }
118 }
119}
120
121impl std::fmt::Display for WorkerStatus {
122 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123 f.write_str(self.lifecycle_label())
124 }
125}
126
127#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
129#[serde(rename_all = "snake_case")]
130pub enum VerifierStatus {
131 Passed,
132 Failed,
133 Skipped,
134}
135
136#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
138#[serde(rename_all = "snake_case")]
139pub enum ArtifactKind {
140 VerifyOutput,
141 DiffScopeSummary,
142 Patch,
143 ReviewRecord,
144 Log,
145 Other,
146}
147
148#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
150pub struct ArtifactRef {
151 pub artifact_id: String,
152 pub kind: ArtifactKind,
153 pub locator: String,
155 pub run_id: Option<String>,
156 pub unit_id: Option<String>,
157 pub stage: Option<String>,
158}
159
160#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
162pub struct VerifierResult {
163 pub verifier_name: String,
164 pub status: VerifierStatus,
165 pub command: Option<String>,
166 pub exit_code: Option<i32>,
167 pub summary: Option<String>,
168 pub artifact_refs: Vec<ArtifactRef>,
169 pub started_at: Option<String>,
170 pub finished_at: Option<String>,
171 pub run_id: Option<String>,
172 pub unit_id: Option<String>,
173}
174
175#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
177pub struct EvidenceBundleRef {
178 pub bundle_id: String,
179 pub unit_id: String,
180 pub run_id: Option<String>,
181 pub artifact_refs: Vec<ArtifactRef>,
182 pub summary: Option<String>,
183}
184
185pub mod worker {
187 pub use super::{WorkerAssignment, WorkerAttempt, WorkerResult, WorkerStatus};
188}
189
190pub mod runner {}
192
193pub mod evidence {
195 pub use super::{ArtifactKind, ArtifactRef, EvidenceBundleRef, VerifierResult, VerifierStatus};
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 #[test]
203 fn worker_status_serializes_as_snake_case() {
204 let json = serde_json::to_string(&WorkerStatus::AwaitingVerify).unwrap();
205 assert_eq!(json, "\"awaiting_verify\"");
206 }
207
208 #[test]
209 fn verifier_result_round_trips_with_artifact_refs() {
210 let verifier = VerifierResult {
211 verifier_name: "unit.verify".to_string(),
212 status: VerifierStatus::Failed,
213 command: Some("cargo test".to_string()),
214 exit_code: Some(1),
215 summary: Some("verify failed".to_string()),
216 artifact_refs: vec![ArtifactRef {
217 artifact_id: "artifact-1".to_string(),
218 kind: ArtifactKind::VerifyOutput,
219 locator: "mana://units/9/artifacts/verify-output".to_string(),
220 run_id: Some("run-1".to_string()),
221 unit_id: Some("9".to_string()),
222 stage: Some("verify".to_string()),
223 }],
224 started_at: None,
225 finished_at: None,
226 run_id: Some("run-1".to_string()),
227 unit_id: Some("9".to_string()),
228 };
229
230 let json = serde_json::to_string(&verifier).unwrap();
231 let round_trip: VerifierResult = serde_json::from_str(&json).unwrap();
232 assert_eq!(round_trip, verifier);
233 }
234}