diff options
Diffstat (limited to 'makima/src/db')
| -rw-r--r-- | makima/src/db/models.rs | 13 | ||||
| -rw-r--r-- | makima/src/db/repository.rs | 42 |
2 files changed, 50 insertions, 5 deletions
diff --git a/makima/src/db/models.rs b/makima/src/db/models.rs index 8ab3a10..40d4109 100644 --- a/makima/src/db/models.rs +++ b/makima/src/db/models.rs @@ -1194,6 +1194,11 @@ pub struct Contract { /// 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, pub version: i32, pub created_at: DateTime<Utc>, pub updated_at: DateTime<Utc>, @@ -1314,6 +1319,11 @@ pub struct CreateContractRequest { /// - 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>, } /// Request payload for updating a contract @@ -1327,6 +1337,9 @@ pub struct UpdateContractRequest { /// 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>, /// Version for optimistic locking pub version: Option<i32>, } diff --git a/makima/src/db/repository.rs b/makima/src/db/repository.rs index 0e85be1..92b2048 100644 --- a/makima/src/db/repository.rs +++ b/makima/src/db/repository.rs @@ -2052,10 +2052,12 @@ pub async fn create_contract_for_owner( ))); } + let autonomous_loop = req.autonomous_loop.unwrap_or(false); + sqlx::query_as::<_, Contract>( r#" - INSERT INTO contracts (owner_id, name, description, contract_type, phase) - VALUES ($1, $2, $3, $4, $5) + INSERT INTO contracts (owner_id, name, description, contract_type, phase, autonomous_loop) + VALUES ($1, $2, $3, $4, $5, $6) RETURNING * "#, ) @@ -2064,6 +2066,7 @@ pub async fn create_contract_for_owner( .bind(&req.description) .bind(contract_type) .bind(phase) + .bind(autonomous_loop) .fetch_one(pool) .await } @@ -2162,14 +2165,15 @@ pub async fn update_contract_for_owner( let phase = req.phase.unwrap_or(existing.phase); let status = req.status.unwrap_or(existing.status); let supervisor_task_id = req.supervisor_task_id.or(existing.supervisor_task_id); + let autonomous_loop = req.autonomous_loop.unwrap_or(existing.autonomous_loop); let result = if req.version.is_some() { sqlx::query_as::<_, Contract>( r#" UPDATE contracts SET name = $3, description = $4, phase = $5, status = $6, - supervisor_task_id = $7, version = version + 1, updated_at = NOW() - WHERE id = $1 AND owner_id = $2 AND version = $8 + supervisor_task_id = $7, autonomous_loop = $8, version = version + 1, updated_at = NOW() + WHERE id = $1 AND owner_id = $2 AND version = $9 RETURNING * "#, ) @@ -2180,6 +2184,7 @@ pub async fn update_contract_for_owner( .bind(&phase) .bind(&status) .bind(supervisor_task_id) + .bind(autonomous_loop) .bind(req.version.unwrap()) .fetch_optional(pool) .await? @@ -2188,7 +2193,7 @@ pub async fn update_contract_for_owner( r#" UPDATE contracts SET name = $3, description = $4, phase = $5, status = $6, - supervisor_task_id = $7, version = version + 1, updated_at = NOW() + supervisor_task_id = $7, autonomous_loop = $8, version = version + 1, updated_at = NOW() WHERE id = $1 AND owner_id = $2 RETURNING * "#, @@ -2200,6 +2205,7 @@ pub async fn update_contract_for_owner( .bind(&phase) .bind(&status) .bind(supervisor_task_id) + .bind(autonomous_loop) .fetch_optional(pool) .await? }; @@ -2591,6 +2597,32 @@ pub async fn list_tasks_in_contract( .await } +/// Minimal task info for worktree cleanup operations. +#[derive(Debug, Clone, sqlx::FromRow)] +pub struct TaskWorktreeInfo { + pub id: Uuid, + pub daemon_id: Option<Uuid>, + pub overlay_path: Option<String>, +} + +/// List tasks in a contract with their daemon/worktree info. +/// Used for cleaning up worktrees when a contract is completed or deleted. +pub async fn list_contract_tasks_with_worktree_info( + pool: &PgPool, + contract_id: Uuid, +) -> Result<Vec<TaskWorktreeInfo>, sqlx::Error> { + sqlx::query_as::<_, TaskWorktreeInfo>( + r#" + SELECT id, daemon_id, overlay_path + FROM tasks + WHERE contract_id = $1 AND (daemon_id IS NOT NULL OR overlay_path IS NOT NULL) + "#, + ) + .bind(contract_id) + .fetch_all(pool) + .await +} + // ============================================================================= // Contract Events // ============================================================================= |
