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