chicago_tdd_tools/swarm/
task.rs

1//! Task Receipt System for Swarm Operations
2//!
3//! Provides proof of work done by swarm agents. Each task generates a receipt
4//! that proves: what was done, by whom, when, and the result.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9/// Status of a task in the swarm
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
11pub enum TaskStatus {
12    /// Queued and waiting for execution
13    Queued,
14    /// Currently being executed
15    Executing,
16    /// Completed successfully
17    Completed,
18    /// Failed with error
19    Failed,
20    /// Cancelled before execution
21    Cancelled,
22}
23
24impl std::fmt::Display for TaskStatus {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        match self {
27            Self::Queued => write!(f, "Queued"),
28            Self::Executing => write!(f, "Executing"),
29            Self::Completed => write!(f, "Completed"),
30            Self::Failed => write!(f, "Failed"),
31            Self::Cancelled => write!(f, "Cancelled"),
32        }
33    }
34}
35
36/// A request for a task to be executed by swarm members
37#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct TaskRequest {
39    /// Unique task ID
40    pub id: String,
41    /// Sector(s) to execute in (Academic, Claims, etc.)
42    pub sectors: Vec<String>,
43    /// Operation to perform
44    pub operation: String,
45    /// Input data
46    pub input: String,
47    /// Priority (higher = execute first)
48    pub priority: u32,
49    /// Deadline for execution
50    pub deadline: String,
51}
52
53impl TaskRequest {
54    /// Create a new task request
55    #[must_use]
56    pub fn new(id: String, sector: String, operation: String, input: String) -> Self {
57        Self {
58            id,
59            sectors: vec![sector],
60            operation,
61            input,
62            priority: 0,
63            deadline: "2099-12-31T23:59:59Z".to_string(),
64        }
65    }
66
67    /// Set priority for this task
68    #[must_use]
69    pub const fn with_priority(mut self, priority: u32) -> Self {
70        self.priority = priority;
71        self
72    }
73
74    /// Add a sector to execute in
75    #[must_use]
76    pub fn add_sector(mut self, sector: String) -> Self {
77        if !self.sectors.contains(&sector) {
78            self.sectors.push(sector);
79        }
80        self
81    }
82}
83
84/// Proof of task completion
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct TaskReceipt {
87    /// Task ID
88    pub task_id: String,
89    /// Agent that executed the task
90    pub agent_id: String,
91    /// Sectors executed in
92    pub sectors: Vec<String>,
93    /// Status of execution
94    pub status: TaskStatus,
95    /// Result data
96    pub result: String,
97    /// Execution time in milliseconds
98    pub execution_time_ms: u64,
99    /// Timestamp of completion
100    pub timestamp: String,
101    /// Merkle root of result (for determinism verification)
102    pub result_merkle: String,
103    /// Metadata from execution
104    pub metadata: HashMap<String, String>,
105}
106
107impl TaskReceipt {
108    /// Create a new task receipt
109    #[must_use]
110    pub fn new(
111        task_id: String,
112        agent_id: String,
113        sectors: Vec<String>,
114        status: TaskStatus,
115        result: String,
116    ) -> Self {
117        Self {
118            task_id,
119            agent_id,
120            sectors,
121            status,
122            result,
123            execution_time_ms: 0,
124            timestamp: chrono::Utc::now().to_rfc3339(),
125            result_merkle: String::new(),
126            metadata: HashMap::new(),
127        }
128    }
129
130    /// Set execution time
131    #[must_use]
132    pub const fn with_execution_time(mut self, ms: u64) -> Self {
133        self.execution_time_ms = ms;
134        self
135    }
136
137    /// Set merkle root of result
138    #[must_use]
139    pub fn with_merkle(mut self, merkle: String) -> Self {
140        self.result_merkle = merkle;
141        self
142    }
143
144    /// Add metadata
145    #[must_use]
146    pub fn add_metadata(mut self, key: String, value: String) -> Self {
147        self.metadata.insert(key, value);
148        self
149    }
150
151    /// Check if receipt indicates success
152    #[must_use]
153    pub fn is_success(&self) -> bool {
154        self.status == TaskStatus::Completed
155    }
156}
157
158/// Task queue for swarm operations
159#[derive(Debug, Clone)]
160pub struct TaskQueue {
161    /// Tasks queued for execution
162    tasks: Vec<TaskRequest>,
163    /// Completed task receipts
164    receipts: Vec<TaskReceipt>,
165}
166
167impl TaskQueue {
168    /// Create a new task queue
169    #[must_use]
170    pub const fn new() -> Self {
171        Self { tasks: Vec::new(), receipts: Vec::new() }
172    }
173
174    /// Enqueue a task
175    pub fn enqueue(&mut self, task: TaskRequest) {
176        self.tasks.push(task);
177        // Sort by priority (higher first)
178        self.tasks.sort_by(|a, b| b.priority.cmp(&a.priority));
179    }
180
181    /// Dequeue the next task
182    pub fn dequeue(&mut self) -> Option<TaskRequest> {
183        if self.tasks.is_empty() {
184            None
185        } else {
186            Some(self.tasks.remove(0))
187        }
188    }
189
190    /// Record task completion
191    pub fn record_receipt(&mut self, receipt: TaskReceipt) {
192        self.receipts.push(receipt);
193    }
194
195    /// Get task count
196    #[must_use]
197    pub const fn task_count(&self) -> usize {
198        self.tasks.len()
199    }
200
201    /// Get receipt count
202    #[must_use]
203    pub const fn receipt_count(&self) -> usize {
204        self.receipts.len()
205    }
206
207    /// Get all receipts
208    #[must_use]
209    pub fn receipts(&self) -> &[TaskReceipt] {
210        &self.receipts
211    }
212}
213
214impl Default for TaskQueue {
215    fn default() -> Self {
216        Self::new()
217    }
218}
219
220#[cfg(test)]
221mod tests {
222    use super::*;
223
224    #[test]
225    fn test_task_request_creation() {
226        let task = TaskRequest::new(
227            "task-001".to_string(),
228            "Academic".to_string(),
229            "desk-review".to_string(),
230            "paper data".to_string(),
231        );
232
233        assert_eq!(task.id, "task-001");
234        assert_eq!(task.sectors, vec!["Academic"]);
235        assert_eq!(task.priority, 0);
236    }
237
238    #[test]
239    fn test_task_with_priority() {
240        let task = TaskRequest::new(
241            "task-001".to_string(),
242            "Academic".to_string(),
243            "desk-review".to_string(),
244            "paper data".to_string(),
245        )
246        .with_priority(100);
247
248        assert_eq!(task.priority, 100);
249    }
250
251    #[test]
252    fn test_task_multi_sector() {
253        let task = TaskRequest::new(
254            "task-001".to_string(),
255            "Academic".to_string(),
256            "review".to_string(),
257            "data".to_string(),
258        )
259        .add_sector("Claims".to_string());
260
261        assert_eq!(task.sectors.len(), 2);
262        assert!(task.sectors.contains(&"Academic".to_string()));
263        assert!(task.sectors.contains(&"Claims".to_string()));
264    }
265
266    #[test]
267    fn test_task_receipt_creation() {
268        let receipt = TaskReceipt::new(
269            "task-001".to_string(),
270            "agent-1".to_string(),
271            vec!["Academic".to_string()],
272            TaskStatus::Completed,
273            "success".to_string(),
274        );
275
276        assert_eq!(receipt.task_id, "task-001");
277        assert_eq!(receipt.agent_id, "agent-1");
278        assert!(receipt.is_success());
279    }
280
281    #[test]
282    fn test_task_queue() {
283        let mut queue = TaskQueue::new();
284
285        queue.enqueue(TaskRequest::new(
286            "t1".to_string(),
287            "Academic".to_string(),
288            "op".to_string(),
289            "data".to_string(),
290        ));
291
292        queue.enqueue(
293            TaskRequest::new(
294                "t2".to_string(),
295                "Claims".to_string(),
296                "op".to_string(),
297                "data".to_string(),
298            )
299            .with_priority(10),
300        );
301
302        // Higher priority should be dequeued first
303        let task = queue.dequeue().unwrap();
304        assert_eq!(task.id, "t2");
305    }
306
307    #[test]
308    fn test_task_status_display() {
309        assert_eq!(TaskStatus::Queued.to_string(), "Queued");
310        assert_eq!(TaskStatus::Completed.to_string(), "Completed");
311        assert_eq!(TaskStatus::Failed.to_string(), "Failed");
312    }
313}