use std::path::PathBuf;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct WorkerAttempt {
pub number: u32,
pub outcome: String,
pub summary: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct WorkerAssignment {
pub id: String,
pub title: String,
pub description: String,
pub acceptance: Option<String>,
pub verify: Option<String>,
pub notes: Option<String>,
pub decisions: Vec<String>,
pub dependencies: Vec<String>,
pub paths: Vec<String>,
pub files: Vec<String>,
pub attempts: Vec<WorkerAttempt>,
pub workspace_root: PathBuf,
pub model: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct WorkerResult {
pub unit_id: String,
pub status: WorkerStatus,
pub summary: Option<String>,
pub error: Option<String>,
pub tool_count: usize,
pub turns: usize,
pub tokens: Option<u64>,
pub cost: Option<f64>,
pub model: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum WorkerStatus {
AwaitingVerify,
Completed,
Blocked,
Failed,
Cancelled,
}
impl WorkerStatus {
pub fn as_str(self) -> &'static str {
match self {
Self::AwaitingVerify => "awaiting_verify",
Self::Completed => "completed",
Self::Blocked => "blocked",
Self::Failed => "failed",
Self::Cancelled => "cancelled",
}
}
pub fn lifecycle_label(self) -> &'static str {
match self {
Self::AwaitingVerify => "candidate complete ยท awaiting verify",
Self::Completed => "completed",
Self::Blocked => "blocked",
Self::Failed => "failed",
Self::Cancelled => "cancelled",
}
}
}
impl std::fmt::Display for WorkerStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.lifecycle_label())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum VerifierStatus {
Passed,
Failed,
Skipped,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ArtifactKind {
VerifyOutput,
DiffScopeSummary,
Patch,
ReviewRecord,
Log,
Other,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ArtifactRef {
pub artifact_id: String,
pub kind: ArtifactKind,
pub locator: String,
pub run_id: Option<String>,
pub unit_id: Option<String>,
pub stage: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct VerifierResult {
pub verifier_name: String,
pub status: VerifierStatus,
pub command: Option<String>,
pub exit_code: Option<i32>,
pub summary: Option<String>,
pub artifact_refs: Vec<ArtifactRef>,
pub started_at: Option<String>,
pub finished_at: Option<String>,
pub run_id: Option<String>,
pub unit_id: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct EvidenceBundleRef {
pub bundle_id: String,
pub unit_id: String,
pub run_id: Option<String>,
pub artifact_refs: Vec<ArtifactRef>,
pub summary: Option<String>,
}
pub mod worker {
pub use super::{WorkerAssignment, WorkerAttempt, WorkerResult, WorkerStatus};
}
pub mod runner {}
pub mod evidence {
pub use super::{ArtifactKind, ArtifactRef, EvidenceBundleRef, VerifierResult, VerifierStatus};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn worker_status_serializes_as_snake_case() {
let json = serde_json::to_string(&WorkerStatus::AwaitingVerify).unwrap();
assert_eq!(json, "\"awaiting_verify\"");
}
#[test]
fn verifier_result_round_trips_with_artifact_refs() {
let verifier = VerifierResult {
verifier_name: "unit.verify".to_string(),
status: VerifierStatus::Failed,
command: Some("cargo test".to_string()),
exit_code: Some(1),
summary: Some("verify failed".to_string()),
artifact_refs: vec![ArtifactRef {
artifact_id: "artifact-1".to_string(),
kind: ArtifactKind::VerifyOutput,
locator: "mana://units/9/artifacts/verify-output".to_string(),
run_id: Some("run-1".to_string()),
unit_id: Some("9".to_string()),
stage: Some("verify".to_string()),
}],
started_at: None,
finished_at: None,
run_id: Some("run-1".to_string()),
unit_id: Some("9".to_string()),
};
let json = serde_json::to_string(&verifier).unwrap();
let round_trip: VerifierResult = serde_json::from_str(&json).unwrap();
assert_eq!(round_trip, verifier);
}
}