diff options
Diffstat (limited to 'makima/src/db/models.rs')
| -rw-r--r-- | makima/src/db/models.rs | 905 |
1 files changed, 2 insertions, 903 deletions
diff --git a/makima/src/db/models.rs b/makima/src/db/models.rs index 3fb9667..bfb8bf3 100644 --- a/makima/src/db/models.rs +++ b/makima/src/db/models.rs @@ -111,10 +111,6 @@ 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)] @@ -141,8 +137,6 @@ 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 @@ -157,8 +151,6 @@ pub struct CreateFileRequest { 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. @@ -194,12 +186,6 @@ 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, @@ -224,9 +210,6 @@ 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(), @@ -425,8 +408,6 @@ 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 (no longer constrained) pub depth: i32, @@ -436,11 +417,6 @@ 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>, @@ -565,14 +541,6 @@ 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>, - /// Contract status (joined from contracts table): 'active', 'completed', 'archived' - pub contract_status: Option<String>, pub parent_task_id: Option<Uuid>, /// Depth in task hierarchy: 0=orchestrator (top-level), 1=subtask (max) pub depth: i32, @@ -582,9 +550,6 @@ 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, /// Whether this task is hidden from the UI (user dismissed it) #[serde(default)] pub hidden: bool, @@ -597,10 +562,6 @@ 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 - contract_status: None, // Not available from Task directly parent_task_id: task.parent_task_id, depth: task.depth, name: task.name, @@ -609,7 +570,6 @@ impl From<Task> for TaskSummary { progress_summary: task.progress_summary, subtask_count: 0, // Would need separate query version: task.version, - is_supervisor: task.is_supervisor, hidden: task.hidden, created_at: task.created_at, updated_at: task.updated_at, @@ -629,8 +589,6 @@ pub struct TaskListResponse { #[derive(Debug, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct CreateTaskRequest { - /// Contract this task belongs to (optional for branched/anonymous tasks) - pub contract_id: Option<Uuid>, /// Name of the task pub name: String, /// Optional description @@ -639,9 +597,6 @@ 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, @@ -668,9 +623,6 @@ pub struct CreateTaskRequest { pub branched_from_task_id: Option<Uuid>, /// Conversation history to initialize the task with (JSON array of messages) pub conversation_history: Option<serde_json::Value>, - /// Task ID whose worktree this task shares. When set, this task reuses the supervisor's - /// worktree instead of creating its own, and should NOT have its worktree deleted during cleanup. - pub supervisor_worktree_task_id: Option<Uuid>, /// Directive this task belongs to (for directive-driven tasks) pub directive_id: Option<Uuid>, /// Directive step this task executes @@ -935,87 +887,8 @@ pub struct TaskOutputResponse { pub task_id: Uuid, } -// ============================================================================= -// Mesh Chat History Types -// ============================================================================= - -/// Mesh chat conversation for persisting history -#[derive(Debug, Clone, FromRow, Serialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct MeshChatConversation { - pub 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 mesh chat conversation -#[derive(Debug, Clone, FromRow, Serialize, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct MeshChatMessageRecord { - pub id: Uuid, - pub conversation_id: Uuid, - pub role: String, - pub content: String, - pub context_type: String, - pub context_task_id: Option<Uuid>, - /// 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 chat history endpoint -#[derive(Debug, Serialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct MeshChatHistoryResponse { - pub conversation_id: Uuid, - pub messages: Vec<MeshChatMessageRecord>, -} - -// ============================================================================= -// 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>, -} +// (MeshChat* + ContractChat* types removed alongside their dead +// tables/handlers — see migration 20260517000000.) // ============================================================================= // Merge API Types @@ -1120,772 +993,6 @@ pub struct MergeCompleteCheckResponse { pub skipped_count: u32, } -// ============================================================================= -// Contract Type Templates (User-defined) -// ============================================================================= - -/// A phase definition within a contract template -#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct PhaseDefinition { - /// Phase identifier (e.g., "research", "plan", "execute") - pub id: String, - /// Display name for the phase - pub name: String, - /// Order in the workflow (0-indexed) - pub order: i32, -} - -/// A deliverable definition within a phase -#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct DeliverableDefinition { - /// Deliverable identifier (e.g., "plan-document", "pull-request") - pub id: String, - /// Display name for the deliverable - pub name: String, - /// Priority: "required", "recommended", or "optional" - #[serde(default = "default_priority")] - pub priority: String, -} - -fn default_priority() -> String { - "required".to_string() -} - -/// Phase configuration stored on a contract (copied from template at creation) -#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct PhaseConfig { - /// Ordered list of phases in the workflow - pub phases: Vec<PhaseDefinition>, - /// Default starting phase - pub default_phase: String, - /// Deliverables per phase: { "phase_id": [deliverables] } - #[serde(default)] - pub deliverables: std::collections::HashMap<String, Vec<DeliverableDefinition>>, -} - -/// Contract type template record from the database -#[derive(Debug, Clone, FromRow, Serialize, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct ContractTypeTemplateRecord { - pub id: Uuid, - pub owner_id: Uuid, - pub name: String, - pub description: Option<String>, - #[sqlx(json)] - pub phases: Vec<PhaseDefinition>, - pub default_phase: String, - #[sqlx(json)] - pub deliverables: Option<std::collections::HashMap<String, Vec<DeliverableDefinition>>>, - pub version: i32, - pub created_at: DateTime<Utc>, - pub updated_at: DateTime<Utc>, -} - -/// Request to create a new contract type template -#[derive(Debug, Clone, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct CreateTemplateRequest { - pub name: String, - pub description: Option<String>, - pub phases: Vec<PhaseDefinition>, - pub default_phase: String, - pub deliverables: Option<std::collections::HashMap<String, Vec<DeliverableDefinition>>>, -} - -/// Request to update a contract type template -#[derive(Debug, Clone, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct UpdateTemplateRequest { - pub name: Option<String>, - pub description: Option<String>, - pub phases: Option<Vec<PhaseDefinition>>, - pub default_phase: Option<String>, - pub deliverables: Option<std::collections::HashMap<String, Vec<DeliverableDefinition>>>, - /// Version for optimistic locking - pub version: Option<i32>, -} - -/// Summary of a contract type template for list views -#[derive(Debug, Clone, Serialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct ContractTypeTemplateSummary { - pub id: Uuid, - pub name: String, - pub description: Option<String>, - pub phases: Vec<PhaseDefinition>, - pub default_phase: String, - pub is_builtin: bool, - pub version: i32, - pub created_at: DateTime<Utc>, -} - -// ============================================================================= -// Contract Types -// ============================================================================= - -/// Contract type determines the workflow and required documents -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)] -#[serde(rename_all = "lowercase")] -pub enum ContractType { - /// Simple Plan -> Execute workflow (default) - /// - Plan phase: requires a "Plan" document - /// - Execute phase: requires a "PR" document - Simple, - /// Specification-based development with TDD - /// - Research: requires "Research Notes" document - /// - Specify: requires "Requirements Document" - /// - Plan: requires "Plan" document - /// - Execute: requires "PR" document - /// - Review: requires "Release Notes" document - Specification, - /// Execute-only workflow with no deliverables - /// - Only has "execute" phase - /// - NO deliverables at all - just execute tasks directly - Execute, -} - -impl Default for ContractType { - fn default() -> Self { - ContractType::Simple - } -} - -impl std::fmt::Display for ContractType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ContractType::Simple => write!(f, "simple"), - ContractType::Specification => write!(f, "specification"), - ContractType::Execute => write!(f, "execute"), - } - } -} - -impl std::str::FromStr for ContractType { - type Err = String; - - fn from_str(s: &str) -> Result<Self, Self::Err> { - match s.to_lowercase().as_str() { - "simple" => Ok(ContractType::Simple), - "specification" => Ok(ContractType::Specification), - "execute" => Ok(ContractType::Execute), - _ => Err(format!("Unknown contract type: {}", s)), - } - } -} - -/// 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>, - /// Contract type: "simple" or "specification" - pub contract_type: 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>, - /// Whether tasks for this contract should run in autonomous loop mode. - /// When enabled, tasks will automatically restart with --continue if they exit - /// without a COMPLETION_GATE indicating ready: true. - #[serde(default)] - pub autonomous_loop: bool, - /// Whether to wait for user confirmation before progressing to the next phase. - /// When enabled, the supervisor will pause and ask the user to review and approve - /// phase outputs (like plans, requirements, etc.) before continuing. - #[serde(default)] - pub phase_guard: bool, - /// Completed deliverables per phase. - /// Structure: { "plan": ["plan-document"], "execute": ["pull-request"] } - #[sqlx(json)] - #[serde(default)] - pub completed_deliverables: serde_json::Value, - /// Whether this contract operates in local-only mode. - /// When enabled, automatic completion actions (branch, merge, pr) are skipped, - /// allowing users to manually handle code changes via patch files or other means. - #[serde(default)] - pub local_only: bool, - /// Whether to auto-merge to target branch locally when local_only mode is enabled. - /// When both local_only and auto_merge_local are true, completed task changes will be - /// automatically merged to the master/main branch locally (without pushing or creating PRs). - #[serde(default)] - pub auto_merge_local: bool, - /// Phase configuration copied from template at contract creation (raw JSON). - /// When present, this overrides the built-in contract type phases. - /// Use `get_phase_config()` to get the parsed PhaseConfig. - #[serde(skip_serializing_if = "Option::is_none")] - pub phase_config: Option<serde_json::Value>, - pub version: i32, - pub created_at: DateTime<Utc>, - pub updated_at: DateTime<Utc>, -} - -impl Contract { - /// Parse contract_type string to ContractType enum - pub fn contract_type_enum(&self) -> Result<ContractType, String> { - self.contract_type.parse() - } - - /// 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() - } - - /// Get valid phase IDs for this contract (as strings) - pub fn valid_phase_ids(&self) -> Vec<String> { - // Check phase_config first (for custom templates) - if let Some(config) = self.get_phase_config() { - let mut phases: Vec<_> = config.phases.iter().collect(); - phases.sort_by_key(|p| p.order); - return phases.iter().map(|p| p.id.clone()).collect(); - } - - // Fall back to built-in contract types - match self.contract_type.as_str() { - "simple" => vec!["plan".to_string(), "execute".to_string()], - "specification" => vec![ - "research".to_string(), - "specify".to_string(), - "plan".to_string(), - "execute".to_string(), - "review".to_string(), - ], - "execute" => vec!["execute".to_string()], - _ => vec!["plan".to_string(), "execute".to_string()], - } - } - - /// Get valid phases for this contract type (as ContractPhase enums) - /// Note: For custom templates with non-standard phases, this only returns - /// phases that map to the ContractPhase enum. - pub fn valid_phases(&self) -> Vec<ContractPhase> { - self.valid_phase_ids() - .iter() - .filter_map(|id| id.parse::<ContractPhase>().ok()) - .collect() - } - - /// Get the initial phase ID for this contract type (as string) - pub fn initial_phase_id(&self) -> String { - // Check phase_config first (for custom templates) - if let Some(config) = self.get_phase_config() { - return config.default_phase.clone(); - } - - // Fall back to built-in contract types - match self.contract_type.as_str() { - "specification" => "research".to_string(), - "execute" => "execute".to_string(), - _ => "plan".to_string(), - } - } - - /// Get the initial phase for this contract type (as ContractPhase enum) - pub fn initial_phase(&self) -> ContractPhase { - self.initial_phase_id() - .parse() - .unwrap_or(ContractPhase::Plan) - } - - /// Get the terminal phase ID for this contract type (as string) - pub fn terminal_phase_id(&self) -> String { - // Check phase_config first (for custom templates) - if let Some(config) = self.get_phase_config() { - // Last phase in sorted order is the terminal phase - let mut phases: Vec<_> = config.phases.iter().collect(); - phases.sort_by_key(|p| p.order); - if let Some(last) = phases.last() { - return last.id.clone(); - } - } - - // Fall back to built-in contract types - match self.contract_type.as_str() { - "specification" => "review".to_string(), - _ => "execute".to_string(), - } - } - - /// Get the terminal phase for this contract type (phase where contract can be completed) - pub fn terminal_phase(&self) -> ContractPhase { - self.terminal_phase_id() - .parse() - .unwrap_or(ContractPhase::Execute) - } - - /// Check if a phase ID is valid for this contract - pub fn is_valid_phase(&self, phase_id: &str) -> bool { - self.valid_phase_ids().contains(&phase_id.to_string()) - } - - /// Get the phase configuration for custom templates - pub fn get_phase_config(&self) -> Option<PhaseConfig> { - self.phase_config - .as_ref() - .and_then(|v| serde_json::from_value(v.clone()).ok()) - } - - /// Get completed deliverable IDs for a specific phase - pub fn get_completed_deliverables(&self, phase: &str) -> Vec<String> { - self.completed_deliverables - .get(phase) - .and_then(|v| v.as_array()) - .map(|arr| { - arr.iter() - .filter_map(|v| v.as_str().map(String::from)) - .collect() - }) - .unwrap_or_default() - } - - /// Check if a specific deliverable is marked as complete for a phase - pub fn is_deliverable_complete(&self, phase: &str, deliverable_id: &str) -> bool { - self.get_completed_deliverables(phase) - .contains(&deliverable_id.to_string()) - } -} - -/// 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>, - /// Contract type: "simple" or "specification" - pub contract_type: String, - pub phase: String, - pub status: String, - /// Supervisor task ID for contract orchestration - pub supervisor_task_id: Option<Uuid>, - /// When true, tasks do not auto-execute completion actions and work stays in worktrees. - #[serde(default)] - pub local_only: bool, - /// When true with local_only, automatically merge completed tasks to target branch locally. - #[serde(default)] - pub auto_merge_local: bool, - 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>, - /// Contract type: "simple" (default), "specification", "execute", or a custom template name. - /// For built-in types: - /// - simple: Plan -> Execute workflow - /// - specification: Research -> Specify -> Plan -> Execute -> Review - /// - execute: Execute only - /// For custom templates, use the template name or provide template_id. - #[serde(default)] - pub contract_type: Option<String>, - /// UUID of a custom template to use. If provided, this takes precedence over contract_type. - /// The template's phase configuration will be copied to the contract. - #[serde(default)] - pub template_id: Option<Uuid>, - /// Initial phase to start in (defaults based on contract_type or template) - /// - simple: defaults to "plan" - /// - specification: defaults to "research" - #[serde(default)] - pub initial_phase: Option<String>, - /// Enable autonomous loop mode for tasks in this contract. - /// When enabled, tasks automatically restart with --continue if they exit - /// without a COMPLETION_GATE indicating ready: true. - #[serde(default)] - pub autonomous_loop: Option<bool>, - /// Enable phase guard mode for this contract. - /// When enabled, the supervisor will pause and ask the user to review and approve - /// phase outputs before progressing to the next phase. - #[serde(default)] - pub phase_guard: Option<bool>, - /// Enable local-only mode for this contract. - /// When enabled, automatic completion actions (branch, merge, pr) are skipped, - /// allowing users to manually handle code changes via patch files or other means. - #[serde(default)] - pub local_only: Option<bool>, - /// Enable auto-merge to target branch locally when local_only mode is enabled. - /// When both local_only and auto_merge_local are true, completed task changes will be - /// automatically merged to the master/main branch locally (without pushing or creating PRs). - #[serde(default)] - pub auto_merge_local: Option<bool>, -} - -/// 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>, - /// Enable or disable autonomous loop mode for tasks in this contract. - #[serde(default)] - pub autonomous_loop: Option<bool>, - /// Enable or disable phase guard mode for this contract. - /// When enabled, the supervisor will pause and ask the user to review and approve - /// phase outputs before progressing to the next phase. - #[serde(default)] - pub phase_guard: Option<bool>, - /// Enable or disable local-only mode for this contract. - /// When enabled, automatic completion actions (branch, merge, pr) are skipped, - /// allowing users to manually handle code changes via patch files or other means. - #[serde(default)] - pub local_only: Option<bool>, - /// Enable or disable auto-merge to target branch locally when local_only mode is enabled. - /// When both local_only and auto_merge_local are true, completed task changes will be - /// automatically merged to the master/main branch locally (without pushing or creating PRs). - #[serde(default)] - pub auto_merge_local: Option<bool>, - /// 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, - /// If phase_guard is enabled, this must be true to confirm the transition. - /// If not provided or false, returns phase deliverables for review. - #[serde(default)] - pub confirmed: Option<bool>, - /// User feedback for changes (used when not confirming) - #[serde(skip_serializing_if = "Option::is_none")] - pub feedback: Option<String>, - /// Expected version for optimistic locking. If provided, the phase change - /// will only succeed if the current contract version matches. - #[serde(skip_serializing_if = "Option::is_none")] - pub expected_version: Option<i32>, -} - -/// Result of a phase change operation, supporting explicit conflict detection. -#[derive(Debug, Clone)] -pub enum PhaseChangeResult { - /// Phase change succeeded, returning the updated contract - Success(Contract), - /// Version conflict: the contract was modified concurrently - VersionConflict { - /// The version the client expected - expected: i32, - /// The actual current version in the database - actual: i32, - /// The current phase of the contract - current_phase: String, - }, - /// Validation failed (e.g., invalid phase transition) - ValidationFailed { - /// Human-readable reason for the failure - reason: String, - /// List of missing requirements for the phase transition - missing_requirements: Vec<String>, - }, - /// The caller is not authorized to change this contract's phase - Unauthorized, - /// The contract was not found - NotFound, -} - -/// Response for phase transition when phase_guard is enabled -#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct PhaseTransitionRequest { - /// Current contract phase - pub current_phase: String, - /// Requested next phase - pub next_phase: String, - /// Summary of phase deliverables/outputs - pub deliverables_summary: String, - /// List of files created in this phase - pub phase_files: Vec<PhaseFileInfo>, - /// List of completed tasks in this phase - pub phase_tasks: Vec<PhaseTaskInfo>, - /// Whether user confirmation is required - pub requires_confirmation: bool, - /// Unique ID for tracking this transition request - pub transition_id: String, -} - -/// File info for phase transition review -#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct PhaseFileInfo { - pub id: Uuid, - pub name: String, - pub description: Option<String>, -} - -/// Task info for phase transition review -#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] -#[serde(rename_all = "camelCase")] -pub struct PhaseTaskInfo { - pub id: Uuid, - pub name: String, - pub status: 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) @@ -2173,11 +1280,9 @@ pub struct ConversationSnapshot { pub struct HistoryEvent { pub id: Uuid, pub owner_id: Uuid, - pub contract_id: Option<Uuid>, pub task_id: Option<Uuid>, pub event_type: String, pub event_subtype: Option<String>, - pub phase: Option<String>, #[sqlx(json)] pub event_data: serde_json::Value, pub created_at: DateTime<Utc>, @@ -2221,7 +1326,6 @@ pub struct ToolCallInfo { #[derive(Debug, Deserialize, ToSchema, Default)] #[serde(rename_all = "camelCase")] pub struct HistoryQueryFilters { - pub phase: Option<String>, pub event_types: Option<Vec<String>>, #[serde(default, deserialize_with = "flexible_datetime::deserialize")] pub from: Option<DateTime<Utc>>, @@ -2766,11 +1870,6 @@ pub struct DirectiveStep { /// Status: pending, ready, running, completed, failed, skipped pub status: String, pub task_id: Option<Uuid>, - /// Optional contract ID for contract-backed execution. - pub contract_id: Option<Uuid>, - /// Optional contract type (e.g. "simple", "specification", "execute"). - /// When set, the orchestrator creates a contract instead of a standalone task. - pub contract_type: Option<String>, pub order_index: i32, pub generation: i32, pub started_at: Option<DateTime<Utc>>, |
