diff options
| author | soryu <soryu@soryu.co> | 2026-01-11 05:52:14 +0000 |
|---|---|---|
| committer | soryu <soryu@soryu.co> | 2026-01-15 00:21:16 +0000 |
| commit | 87044a747b47bd83249d61a45842c7f7b2eae56d (patch) | |
| tree | ef2000ce79ffcc2723ef841acef5aa1deb1d5378 /makima/src/db/models.rs | |
| parent | 077820c4167c168072d217a1b01df840463a12a8 (diff) | |
| download | soryu-87044a747b47bd83249d61a45842c7f7b2eae56d.tar.gz soryu-87044a747b47bd83249d61a45842c7f7b2eae56d.zip | |
Contract system
Diffstat (limited to 'makima/src/db/models.rs')
| -rw-r--r-- | makima/src/db/models.rs | 603 |
1 files changed, 601 insertions, 2 deletions
diff --git a/makima/src/db/models.rs b/makima/src/db/models.rs index 5064b97..e16c43f 100644 --- a/makima/src/db/models.rs +++ b/makima/src/db/models.rs @@ -60,6 +60,8 @@ pub enum BodyElement { alt: Option<String>, caption: Option<String>, }, + /// Raw markdown content - renders as formatted markdown, edits as raw text + Markdown { content: String }, } /// File record from the database. @@ -68,6 +70,10 @@ pub enum BodyElement { pub struct File { pub id: Uuid, pub owner_id: Uuid, + /// Contract this file belongs to (optional) + pub contract_id: Option<Uuid>, + /// Phase of the contract when file was added (e.g., "research", "specify") + pub contract_phase: Option<String>, pub name: String, pub description: Option<String>, #[sqlx(json)] @@ -80,6 +86,12 @@ pub struct File { pub body: Vec<BodyElement>, /// Version number for optimistic locking pub version: i32, + /// Path to linked repository file (e.g., "README.md", "docs/design.md") + pub repo_file_path: Option<String>, + /// When the file was last synced from the repository + pub repo_synced_at: Option<DateTime<Utc>>, + /// Sync status: 'none', 'synced', 'modified', 'conflict' + pub repo_sync_status: Option<String>, pub created_at: DateTime<Utc>, pub updated_at: DateTime<Utc>, } @@ -88,14 +100,24 @@ pub struct File { #[derive(Debug, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct CreateFileRequest { + /// Contract this file belongs to (required - files must belong to a contract) + pub contract_id: Uuid, /// Name of the file (auto-generated if not provided) pub name: Option<String>, /// Optional description pub description: Option<String>, - /// Transcript entries + /// Transcript entries (default to empty) + #[serde(default)] pub transcript: Vec<TranscriptEntry>, /// Storage location (e.g., s3://bucket/path) - not used yet pub location: Option<String>, + /// Initial body elements (e.g., from a template) + #[serde(default)] + pub body: Vec<BodyElement>, + /// Path to linked repository file (e.g., "README.md") + pub repo_file_path: Option<String>, + /// Contract phase this file belongs to (for deliverable tracking) + pub contract_phase: Option<String>, } /// Request payload for updating an existing file. @@ -114,6 +136,8 @@ pub struct UpdateFileRequest { pub body: Option<Vec<BodyElement>>, /// Version for optimistic locking (required for updates from frontend) pub version: Option<i32>, + /// Path to linked repository file (e.g., "README.md") + pub repo_file_path: Option<String>, } /// Response for file list endpoint. @@ -129,6 +153,12 @@ pub struct FileListResponse { #[serde(rename_all = "camelCase")] pub struct FileSummary { pub id: Uuid, + /// Contract this file belongs to + pub contract_id: Option<Uuid>, + /// Contract name (joined from contracts table) + pub contract_name: Option<String>, + /// Phase when file was added to contract + pub contract_phase: Option<String>, pub name: String, pub description: Option<String>, pub transcript_count: usize, @@ -136,6 +166,10 @@ pub struct FileSummary { pub duration: Option<f32>, /// Version number for optimistic locking pub version: i32, + /// Path to linked repository file + pub repo_file_path: Option<String>, + /// Sync status with repository + pub repo_sync_status: Option<String>, pub created_at: DateTime<Utc>, pub updated_at: DateTime<Utc>, } @@ -149,11 +183,16 @@ impl From<File> for FileSummary { .fold(0.0_f32, f32::max); Self { id: file.id, + contract_id: file.contract_id, + contract_name: None, // Not available from File alone, requires JOIN + contract_phase: file.contract_phase, name: file.name, description: file.description, transcript_count: file.transcript.len(), duration: if duration > 0.0 { Some(duration) } else { None }, version: file.version, + repo_file_path: file.repo_file_path, + repo_sync_status: file.repo_sync_status, created_at: file.created_at, updated_at: file.updated_at, } @@ -345,8 +384,10 @@ impl std::str::FromStr for MergeMode { pub struct Task { pub id: Uuid, pub owner_id: Uuid, + /// Contract this task belongs to (required for new tasks) + pub contract_id: Option<Uuid>, pub parent_task_id: Option<Uuid>, - /// Depth in task hierarchy: 0=orchestrator (top-level), 1=subtask (max) + /// Depth in task hierarchy (no longer constrained) pub depth: i32, pub name: String, pub description: Option<String>, @@ -354,6 +395,11 @@ pub struct Task { pub priority: i32, pub plan: String, + // Supervisor flag + /// True for contract supervisor tasks. Only supervisors can spawn new tasks. + #[serde(default)] + pub is_supervisor: bool, + // Daemon/container info pub daemon_id: Option<Uuid>, pub container_id: Option<String>, @@ -379,6 +425,30 @@ pub struct Task { pub last_output: Option<String>, pub error_message: Option<String>, + // Git checkpoint tracking + /// Git commit SHA of the most recent checkpoint + #[serde(skip_serializing_if = "Option::is_none")] + pub last_checkpoint_sha: Option<String>, + /// Number of checkpoints created by this task + #[serde(default)] + pub checkpoint_count: i32, + /// Message from the most recent checkpoint + #[serde(skip_serializing_if = "Option::is_none")] + pub checkpoint_message: Option<String>, + + // Conversation state for resumption + /// Saved conversation context for task/supervisor resumption + #[serde(skip_serializing_if = "Option::is_none")] + pub conversation_state: Option<serde_json::Value>, + + // Daemon migration tracking + /// Previous daemon if task was migrated + #[serde(skip_serializing_if = "Option::is_none")] + pub migrated_from_daemon_id: Option<Uuid>, + /// Most recent daemon that worked on this task + #[serde(skip_serializing_if = "Option::is_none")] + pub last_active_daemon_id: Option<Uuid>, + // Timestamps pub started_at: Option<DateTime<Utc>>, pub completed_at: Option<DateTime<Utc>>, @@ -413,6 +483,12 @@ impl Task { #[serde(rename_all = "camelCase")] pub struct TaskSummary { pub id: Uuid, + /// Contract this task belongs to + pub contract_id: Option<Uuid>, + /// Contract name (joined from contracts table) + pub contract_name: Option<String>, + /// Contract phase (joined from contracts table) + pub contract_phase: Option<String>, pub parent_task_id: Option<Uuid>, /// Depth in task hierarchy: 0=orchestrator (top-level), 1=subtask (max) pub depth: i32, @@ -422,10 +498,36 @@ pub struct TaskSummary { pub progress_summary: Option<String>, pub subtask_count: i64, pub version: i32, + /// True for contract supervisor tasks + #[serde(default)] + pub is_supervisor: bool, pub created_at: DateTime<Utc>, pub updated_at: DateTime<Utc>, } +/// Convert a full Task to a TaskSummary +impl From<Task> for TaskSummary { + fn from(task: Task) -> Self { + Self { + id: task.id, + contract_id: task.contract_id, + contract_name: None, // Not available from Task directly + contract_phase: None, // Not available from Task directly + parent_task_id: task.parent_task_id, + depth: task.depth, + name: task.name, + status: task.status, + priority: task.priority, + progress_summary: task.progress_summary, + subtask_count: 0, // Would need separate query + version: task.version, + is_supervisor: task.is_supervisor, + created_at: task.created_at, + updated_at: task.updated_at, + } + } +} + /// Response for task list endpoint #[derive(Debug, Serialize, ToSchema)] #[serde(rename_all = "camelCase")] @@ -438,6 +540,8 @@ pub struct TaskListResponse { #[derive(Debug, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct CreateTaskRequest { + /// Contract this task belongs to (required) + pub contract_id: Uuid, /// Name of the task pub name: String, /// Optional description @@ -446,6 +550,9 @@ pub struct CreateTaskRequest { pub plan: String, /// Parent task ID (for subtasks) pub parent_task_id: Option<Uuid>, + /// True for contract supervisor tasks. Only supervisors can spawn new tasks. + #[serde(default)] + pub is_supervisor: bool, /// Priority (higher = more urgent) #[serde(default)] pub priority: i32, @@ -466,6 +573,8 @@ pub struct CreateTaskRequest { /// Files to copy from parent task's worktree when starting #[serde(skip_serializing_if = "Option::is_none")] pub copy_files: Option<Vec<String>>, + /// Checkpoint SHA to branch from (optional) + pub checkpoint_sha: Option<String>, } /// Request payload for updating a task @@ -482,6 +591,8 @@ pub struct UpdateTaskRequest { pub error_message: Option<String>, pub merge_mode: Option<String>, pub pr_url: Option<String>, + /// Repository URL for the task (e.g., when updating supervisor with repo info) + pub repository_url: Option<String>, /// Path to user's local repository (outside ~/.makima) pub target_repo_path: Option<String>, /// Action on completion: "none", "branch", "merge", "pr" @@ -733,6 +844,47 @@ pub struct MeshChatHistoryResponse { } // ============================================================================= +// Contract Chat History Types +// ============================================================================= + +/// Conversation thread for contract chat (scoped to a specific contract) +#[derive(Debug, Clone, FromRow, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ContractChatConversation { + pub id: Uuid, + pub contract_id: Uuid, + pub owner_id: Uuid, + pub name: Option<String>, + pub is_active: bool, + pub created_at: DateTime<Utc>, + pub updated_at: DateTime<Utc>, +} + +/// Individual message in a contract chat conversation +#[derive(Debug, Clone, FromRow, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ContractChatMessageRecord { + pub id: Uuid, + pub conversation_id: Uuid, + pub role: String, + pub content: String, + /// Tool calls made during this message (JSON, nullable) + pub tool_calls: Option<serde_json::Value>, + /// Pending questions requiring user response (JSON, nullable) + pub pending_questions: Option<serde_json::Value>, + pub created_at: DateTime<Utc>, +} + +/// Response for contract chat history endpoint +#[derive(Debug, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ContractChatHistoryResponse { + pub contract_id: Uuid, + pub conversation_id: Uuid, + pub messages: Vec<ContractChatMessageRecord>, +} + +// ============================================================================= // Merge API Types // ============================================================================= @@ -834,3 +986,450 @@ pub struct MergeCompleteCheckResponse { /// Count of skipped branches pub skipped_count: u32, } + +// ============================================================================= +// Contract Types +// ============================================================================= + +/// Contract phase for workflow progression +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "lowercase")] +pub enum ContractPhase { + Research, + Specify, + Plan, + Execute, + Review, +} + +impl std::fmt::Display for ContractPhase { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ContractPhase::Research => write!(f, "research"), + ContractPhase::Specify => write!(f, "specify"), + ContractPhase::Plan => write!(f, "plan"), + ContractPhase::Execute => write!(f, "execute"), + ContractPhase::Review => write!(f, "review"), + } + } +} + +impl std::str::FromStr for ContractPhase { + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s.to_lowercase().as_str() { + "research" => Ok(ContractPhase::Research), + "specify" => Ok(ContractPhase::Specify), + "plan" => Ok(ContractPhase::Plan), + "execute" => Ok(ContractPhase::Execute), + "review" => Ok(ContractPhase::Review), + _ => Err(format!("Unknown contract phase: {}", s)), + } + } +} + +/// Contract status +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "lowercase")] +pub enum ContractStatus { + Active, + Completed, + Archived, +} + +impl std::fmt::Display for ContractStatus { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ContractStatus::Active => write!(f, "active"), + ContractStatus::Completed => write!(f, "completed"), + ContractStatus::Archived => write!(f, "archived"), + } + } +} + +impl std::str::FromStr for ContractStatus { + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s.to_lowercase().as_str() { + "active" => Ok(ContractStatus::Active), + "completed" => Ok(ContractStatus::Completed), + "archived" => Ok(ContractStatus::Archived), + _ => Err(format!("Unknown contract status: {}", s)), + } + } +} + +/// Repository source type +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "lowercase")] +pub enum RepositorySourceType { + /// Existing remote repo (GitHub, GitLab, etc) + Remote, + /// Existing local repo + Local, + /// New repo created/managed by Makima daemon + Managed, +} + +impl std::fmt::Display for RepositorySourceType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + RepositorySourceType::Remote => write!(f, "remote"), + RepositorySourceType::Local => write!(f, "local"), + RepositorySourceType::Managed => write!(f, "managed"), + } + } +} + +impl std::str::FromStr for RepositorySourceType { + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s.to_lowercase().as_str() { + "remote" => Ok(RepositorySourceType::Remote), + "local" => Ok(RepositorySourceType::Local), + "managed" => Ok(RepositorySourceType::Managed), + _ => Err(format!("Unknown repository source type: {}", s)), + } + } +} + +/// Repository status (for managed repos) +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "lowercase")] +pub enum RepositoryStatus { + /// Repo is usable + Ready, + /// Waiting for daemon to create + Pending, + /// Daemon is creating the repo + Creating, + /// Creation failed + Failed, +} + +impl std::fmt::Display for RepositoryStatus { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + RepositoryStatus::Ready => write!(f, "ready"), + RepositoryStatus::Pending => write!(f, "pending"), + RepositoryStatus::Creating => write!(f, "creating"), + RepositoryStatus::Failed => write!(f, "failed"), + } + } +} + +impl std::str::FromStr for RepositoryStatus { + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s.to_lowercase().as_str() { + "ready" => Ok(RepositoryStatus::Ready), + "pending" => Ok(RepositoryStatus::Pending), + "creating" => Ok(RepositoryStatus::Creating), + "failed" => Ok(RepositoryStatus::Failed), + _ => Err(format!("Unknown repository status: {}", s)), + } + } +} + +/// Contract record from the database +#[derive(Debug, Clone, FromRow, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct Contract { + pub id: Uuid, + pub owner_id: Uuid, + pub name: String, + pub description: Option<String>, + pub phase: String, + pub status: String, + /// The long-running supervisor task that orchestrates this contract + #[serde(skip_serializing_if = "Option::is_none")] + pub supervisor_task_id: Option<Uuid>, + pub version: i32, + pub created_at: DateTime<Utc>, + pub updated_at: DateTime<Utc>, +} + +impl Contract { + /// Parse phase string to ContractPhase enum + pub fn phase_enum(&self) -> Result<ContractPhase, String> { + self.phase.parse() + } + + /// Parse status string to ContractStatus enum + pub fn status_enum(&self) -> Result<ContractStatus, String> { + self.status.parse() + } +} + +/// Contract repository record from the database +#[derive(Debug, Clone, FromRow, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ContractRepository { + pub id: Uuid, + pub contract_id: Uuid, + pub name: String, + pub repository_url: Option<String>, + pub local_path: Option<String>, + pub source_type: String, + pub status: String, + pub is_primary: bool, + pub created_at: DateTime<Utc>, + pub updated_at: DateTime<Utc>, +} + +impl ContractRepository { + /// Parse source_type string to RepositorySourceType enum + pub fn source_type_enum(&self) -> Result<RepositorySourceType, String> { + self.source_type.parse() + } + + /// Parse status string to RepositoryStatus enum + pub fn status_enum(&self) -> Result<RepositoryStatus, String> { + self.status.parse() + } +} + +/// Summary of a contract for list views +#[derive(Debug, Clone, FromRow, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ContractSummary { + pub id: Uuid, + pub name: String, + pub description: Option<String>, + pub phase: String, + pub status: String, + pub file_count: i64, + pub task_count: i64, + pub repository_count: i64, + pub version: i32, + pub created_at: DateTime<Utc>, +} + +/// Contract with all relations for detail view +#[derive(Debug, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ContractWithRelations { + #[serde(flatten)] + pub contract: Contract, + pub repositories: Vec<ContractRepository>, + pub files: Vec<FileSummary>, + pub tasks: Vec<TaskSummary>, +} + +/// Response for contract list endpoint +#[derive(Debug, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ContractListResponse { + pub contracts: Vec<ContractSummary>, + pub total: i64, +} + +/// Request payload for creating a new contract +#[derive(Debug, Clone, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct CreateContractRequest { + /// Name of the contract + pub name: String, + /// Optional description + pub description: Option<String>, + /// Initial phase to start in (defaults to "research") + /// Valid values: "research", "specify", "plan", "execute", "review" + #[serde(default)] + pub initial_phase: Option<String>, +} + +/// Request payload for updating a contract +#[derive(Debug, Default, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct UpdateContractRequest { + pub name: Option<String>, + pub description: Option<String>, + pub phase: Option<String>, + pub status: Option<String>, + /// Supervisor task ID for contract orchestration + #[serde(skip_serializing_if = "Option::is_none")] + pub supervisor_task_id: Option<Uuid>, + /// Version for optimistic locking + pub version: Option<i32>, +} + +/// Request to add a remote repository to a contract +#[derive(Debug, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct AddRemoteRepositoryRequest { + pub name: String, + pub repository_url: String, + #[serde(default)] + pub is_primary: bool, +} + +/// Request to add a local repository to a contract +#[derive(Debug, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct AddLocalRepositoryRequest { + pub name: String, + pub local_path: String, + #[serde(default)] + pub is_primary: bool, +} + +/// Request to create a managed repository (daemon will create it) +#[derive(Debug, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct CreateManagedRepositoryRequest { + pub name: String, + #[serde(default)] + pub is_primary: bool, +} + +/// Request to change contract phase +#[derive(Debug, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ChangePhaseRequest { + pub phase: String, +} + +/// Contract event record from the database +#[derive(Debug, Clone, FromRow, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ContractEvent { + pub id: Uuid, + pub contract_id: Uuid, + pub event_type: String, + pub previous_phase: Option<String>, + pub new_phase: Option<String>, + #[sqlx(json)] + pub event_data: Option<serde_json::Value>, + pub created_at: DateTime<Utc>, +} + +/// Response for contract events list endpoint +#[derive(Debug, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ContractEventListResponse { + pub events: Vec<ContractEvent>, + pub total: i64, +} + +// ============================================================================ +// Task Checkpoints (for git checkpoint tracking) +// ============================================================================ + +/// Task checkpoint record - represents a git commit checkpoint +#[derive(Debug, Clone, FromRow, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct TaskCheckpoint { + pub id: Uuid, + pub task_id: Uuid, + /// Sequential checkpoint number within this task + pub checkpoint_number: i32, + /// Git commit SHA + pub commit_sha: String, + /// Git branch name + pub branch_name: String, + /// Commit message + pub message: String, + /// Files changed in this commit: [{path, action: 'A'|'M'|'D'}] + #[sqlx(json)] + pub files_changed: Option<serde_json::Value>, + /// Lines added in this commit + pub lines_added: Option<i32>, + /// Lines removed in this commit + pub lines_removed: Option<i32>, + pub created_at: DateTime<Utc>, +} + +/// Request to create a checkpoint +#[derive(Debug, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct CreateCheckpointRequest { + /// Commit message + pub message: String, +} + +/// Response for checkpoint list endpoint +#[derive(Debug, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct CheckpointListResponse { + pub checkpoints: Vec<TaskCheckpoint>, + pub total: i64, +} + +// ============================================================================ +// Supervisor State (for supervisor resumability) +// ============================================================================ + +/// Supervisor state for contract supervisor tasks +/// Enables resumption after interruption +#[derive(Debug, Clone, FromRow, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct SupervisorState { + pub id: Uuid, + pub contract_id: Uuid, + pub task_id: Uuid, + /// Full Claude conversation history for resumption + #[sqlx(json)] + pub conversation_history: serde_json::Value, + /// Last checkpoint this supervisor created + pub last_checkpoint_id: Option<Uuid>, + /// Tasks the supervisor is waiting on + #[sqlx(try_from = "Vec<Uuid>")] + pub pending_task_ids: Vec<Uuid>, + /// Current contract phase when supervisor was last active + pub phase: String, + /// When supervisor was last active + pub last_activity: DateTime<Utc>, + pub created_at: DateTime<Utc>, + pub updated_at: DateTime<Utc>, +} + +/// Request to update supervisor state +#[derive(Debug, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct UpdateSupervisorStateRequest { + /// Updated conversation history + pub conversation_history: Option<serde_json::Value>, + /// Tasks the supervisor is waiting on + pub pending_task_ids: Option<Vec<Uuid>>, + /// Current contract phase + pub phase: Option<String>, +} + +// ============================================================================ +// Daemon Task Assignments (for multi-daemon support) +// ============================================================================ + +/// Daemon task assignment record +#[derive(Debug, Clone, FromRow, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct DaemonTaskAssignment { + pub id: Uuid, + pub daemon_id: Uuid, + pub task_id: Uuid, + pub assigned_at: DateTime<Utc>, + /// Status: 'active', 'migrating', 'completed' + pub status: String, +} + +/// Extended daemon info for selection +#[derive(Debug, Clone, FromRow, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct DaemonWithCapacity { + pub id: Uuid, + pub owner_id: Uuid, + pub connection_id: String, + pub hostname: Option<String>, + pub machine_id: Option<String>, + pub max_concurrent_tasks: i32, + pub current_task_count: i32, + pub capacity_score: Option<i32>, + pub task_queue_length: Option<i32>, + pub supports_migration: Option<bool>, + pub status: String, + pub last_heartbeat_at: DateTime<Utc>, + pub connected_at: DateTime<Utc>, +} |
