Skip to main content

task_graph_mcp/
types.rs

1//! Core types for the Task Graph MCP Server.
2
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6/// Worker (session-based) - represents a connected worker.
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct Worker {
9    pub id: String,
10    pub tags: Vec<String>,
11    pub max_claims: i32,
12    pub registered_at: i64,
13    pub last_heartbeat: i64,
14}
15
16/// Worker info with additional runtime details for list_workers.
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct WorkerInfo {
19    pub id: String,
20    pub tags: Vec<String>,
21    pub max_claims: i32,
22    pub claim_count: i32,
23    pub current_thought: Option<String>,
24    pub registered_at: i64,
25    pub last_heartbeat: i64,
26}
27
28/// Task priority as an integer (higher = more important).
29/// Range: 0-10, where 10 is highest priority. Default is 5.
30pub type Priority = i32;
31
32/// Default priority (middle of 0-10 range).
33pub const PRIORITY_DEFAULT: Priority = 5;
34
35/// Parse a priority value, clamping to 0-10 range.
36pub fn parse_priority(s: &str) -> Priority {
37    s.parse().unwrap_or(PRIORITY_DEFAULT).clamp(0, 10)
38}
39
40/// Clamp priority to valid range.
41pub fn clamp_priority(p: Priority) -> Priority {
42    p.clamp(0, 10)
43}
44
45/// A task in the task graph.
46#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct Task {
48    pub id: String,
49    pub title: String,
50    pub description: Option<String>,
51    pub status: String,
52    pub priority: Priority,
53    pub worker_id: Option<String>,
54    pub claimed_at: Option<i64>,
55
56    // Affinity (tag-based claiming requirements)
57    pub needed_tags: Vec<String>,
58    pub wanted_tags: Vec<String>,
59
60    // Categorization/discovery tags
61    pub tags: Vec<String>,
62
63    // Estimation & tracking
64    pub points: Option<i32>,
65    pub time_estimate_ms: Option<i64>,
66    pub time_actual_ms: Option<i64>,
67    pub started_at: Option<i64>,
68    pub completed_at: Option<i64>,
69
70    // Live status
71    pub current_thought: Option<String>,
72
73    // Cost accounting
74    pub cost_usd: f64,
75    /// Fixed array of 8 integer metrics [metric_0..metric_7], aggregated on update
76    pub metrics: [i64; 8],
77
78    pub created_at: i64,
79    pub updated_at: i64,
80}
81
82/// A task with its children for tree operations.
83#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct TaskTree {
85    #[serde(flatten)]
86    pub task: Task,
87    pub children: Vec<TaskTree>,
88}
89
90
91
92/// Input for creating a task tree.
93/// Supports all fields from task creation, plus tree-specific fields.
94#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct TaskTreeInput {
96    /// Reference to an existing task ID to include in the tree.
97    /// If set, this node references an existing task rather than creating a new one.
98    /// Other fields are ignored when ref is set.
99    #[serde(rename = "ref")]
100    pub ref_id: Option<String>,
101    
102    /// Custom task ID (optional, UUID7 generated if not provided).
103    /// Ignored if ref is set.
104    pub id: Option<String>,
105    
106    /// Task title (required for new tasks, optional if ref is set).
107    #[serde(default)]
108    pub title: String,
109    
110    /// Task description.
111    pub description: Option<String>,
112    
113    /// Task priority.
114    pub priority: Option<Priority>,
115    
116    /// Story points / complexity estimate.
117    pub points: Option<i32>,
118    
119    /// Estimated duration in milliseconds.
120    pub time_estimate_ms: Option<i64>,
121    
122    /// Tags that claiming agent must have ALL of (AND logic).
123    pub needed_tags: Option<Vec<String>>,
124
125    /// Tags that claiming agent must have AT LEAST ONE of (OR logic).
126    pub wanted_tags: Option<Vec<String>>,
127    
128    /// Categorization/discovery tags for the task.
129    pub tags: Option<Vec<String>>,
130    
131    /// Child nodes in the tree.
132    #[serde(default)]
133    pub children: Vec<TaskTreeInput>,
134}
135
136/// A typed dependency between tasks.
137/// The dependency indicates that from_task_id affects to_task_id based on dep_type.
138#[derive(Debug, Clone, Serialize, Deserialize)]
139pub struct Dependency {
140    pub from_task_id: String,
141    pub to_task_id: String,
142    /// Dependency type: "blocks", "follows", "contains", or custom types.
143    pub dep_type: String,
144}
145
146/// An advisory file lock.
147#[derive(Debug, Clone, Serialize, Deserialize)]
148pub struct FileLock {
149    pub file_path: String,
150    pub worker_id: String,
151    pub reason: Option<String>,
152    pub locked_at: i64,
153    pub task_id: Option<String>,
154}
155
156/// A claim event for file coordination tracking.
157#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct ClaimEvent {
159    pub id: i64,
160    pub file_path: String,
161    pub worker_id: String,
162    pub event: ClaimEventType,
163    pub reason: Option<String>,
164    pub timestamp: i64,
165    pub end_timestamp: Option<i64>,
166    /// For release events: the ID of the corresponding claim event.
167    pub claim_id: Option<i64>,
168}
169
170/// A task state transition event for time tracking.
171#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct TaskStateEvent {
173    pub id: i64,
174    pub task_id: String,
175    pub worker_id: Option<String>,
176    pub event: String,
177    pub reason: Option<String>,
178    pub timestamp: i64,
179    pub end_timestamp: Option<i64>,
180}
181
182/// Type of claim event.
183#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
184#[serde(rename_all = "snake_case")]
185pub enum ClaimEventType {
186    Claimed,
187    Released,
188}
189
190impl ClaimEventType {
191    pub fn as_str(&self) -> &'static str {
192        match self {
193            ClaimEventType::Claimed => "claimed",
194            ClaimEventType::Released => "released",
195        }
196    }
197
198    pub fn from_str(s: &str) -> Option<Self> {
199        match s {
200            "claimed" => Some(ClaimEventType::Claimed),
201            "released" => Some(ClaimEventType::Released),
202            _ => None,
203        }
204    }
205}
206
207/// Result of polling claim updates.
208#[derive(Debug, Clone, Serialize, Deserialize)]
209pub struct ClaimUpdates {
210    pub new_claims: Vec<ClaimEvent>,
211    pub dropped_claims: Vec<ClaimEvent>,
212    pub sequence: i64,
213}
214
215/// An attachment on a task.
216/// Primary key is (task_id, order_index).
217/// If file_path is set, content is stored in the referenced file; otherwise content is inline.
218#[derive(Debug, Clone, Serialize, Deserialize)]
219pub struct Attachment {
220    pub task_id: String,
221    pub order_index: i32,
222    pub name: String,
223    pub mime_type: String,
224    pub content: String,
225    /// Path to the file containing the content (relative to media dir or absolute).
226    /// If set, content is read from this file; if None, content is stored inline.
227    #[serde(skip_serializing_if = "Option::is_none")]
228    pub file_path: Option<String>,
229    pub created_at: i64,
230}
231
232/// Attachment metadata (without content).
233/// Primary key is (task_id, order_index).
234#[derive(Debug, Clone, Serialize, Deserialize)]
235pub struct AttachmentMeta {
236    pub task_id: String,
237    pub order_index: i32,
238    pub name: String,
239    pub mime_type: String,
240    /// Path to the file containing the content (if stored as file).
241    #[serde(skip_serializing_if = "Option::is_none")]
242    pub file_path: Option<String>,
243    pub created_at: i64,
244}
245
246/// Aggregate statistics.
247#[derive(Debug, Clone, Serialize, Deserialize)]
248pub struct Stats {
249    pub total_tasks: i64,
250    /// Task counts by state (dynamic based on config).
251    pub tasks_by_status: HashMap<String, i64>,
252    pub total_points: i64,
253    pub completed_points: i64,
254    pub total_time_estimate_ms: i64,
255    pub total_time_actual_ms: i64,
256    pub total_cost_usd: f64,
257    /// Aggregated metrics [metric_0..metric_7]
258    pub total_metrics: [i64; 8],
259}
260
261/// Compact task representation for list views.
262#[derive(Debug, Clone, Serialize, Deserialize)]
263pub struct TaskSummary {
264    pub id: String,
265    pub title: String,
266    pub status: String,
267    pub priority: Priority,
268    pub worker_id: Option<String>,
269    pub points: Option<i32>,
270    pub current_thought: Option<String>,
271}
272
273/// Result of scanning the task graph from a starting task.
274/// Contains tasks organized by traversal direction.
275#[derive(Debug, Clone, Serialize, Deserialize)]
276pub struct ScanResult {
277    /// The task that was scanned from
278    pub root: Task,
279    /// Tasks that block this task (predecessors via blocks/follows)
280    pub before: Vec<Task>,
281    /// Tasks that this task blocks (successors via blocks/follows)
282    pub after: Vec<Task>,
283    /// Parent chain (ancestors via contains)
284    pub above: Vec<Task>,
285    /// Children tree (descendants via contains)
286    pub below: Vec<Task>,
287}
288
289
290/// Summary of disconnect operation.
291#[derive(Debug, Clone, Serialize, Deserialize)]
292pub struct DisconnectSummary {
293    /// Number of tasks that were released.
294    pub tasks_released: i32,
295    /// Number of file locks that were released.
296    pub files_released: i32,
297    /// The final status applied to released tasks.
298    pub final_status: String,
299}
300
301/// Summary of stale worker cleanup operation.
302#[derive(Debug, Clone, Serialize, Deserialize)]
303pub struct CleanupSummary {
304    /// Number of stale workers evicted.
305    pub workers_evicted: i32,
306    /// Total number of tasks released across all evicted workers.
307    pub tasks_released: i32,
308    /// Total number of file locks released across all evicted workers.
309    pub files_released: i32,
310    /// The final status applied to released tasks.
311    pub final_status: String,
312    /// IDs of evicted workers.
313    pub evicted_worker_ids: Vec<String>,
314}
315
316#[cfg(test)]
317mod tests {
318    // Priority tests removed - Priority is now a type alias for i32
319
320
321
322}