diff options
| author | soryu <soryu@soryu.co> | 2026-02-16 01:24:19 +0000 |
|---|---|---|
| committer | soryu <soryu@soryu.co> | 2026-02-16 01:31:31 +0000 |
| commit | 0676468e3e69ff36f1e509d775f191dd41f6080b (patch) | |
| tree | 624fb2e69827299ae9e571240e53d226124f55dc /makima/src/db/repository.rs | |
| parent | a9da99085bc0b1f94e13cb27639915fd1398ccbe (diff) | |
| download | soryu-0676468e3e69ff36f1e509d775f191dd41f6080b.tar.gz soryu-0676468e3e69ff36f1e509d775f191dd41f6080b.zip | |
Ensure directives replan on goal change
Diffstat (limited to 'makima/src/db/repository.rs')
| -rw-r--r-- | makima/src/db/repository.rs | 38 |
1 files changed, 36 insertions, 2 deletions
diff --git a/makima/src/db/repository.rs b/makima/src/db/repository.rs index 4aa09dc..b5888c9 100644 --- a/makima/src/db/repository.rs +++ b/makima/src/db/repository.rs @@ -5051,6 +5051,7 @@ pub async fn update_directive_for_owner( let title = req.title.as_deref().unwrap_or(¤t.title); let goal = req.goal.as_deref().unwrap_or(¤t.goal); + let goal_changed = goal != current.goal; let status = req.status.as_deref().unwrap_or(¤t.status); let repository_url = req.repository_url.as_deref().or(current.repository_url.as_deref()); let local_path = req.local_path.as_deref().or(current.local_path.as_deref()); @@ -5066,6 +5067,7 @@ pub async fn update_directive_for_owner( SET title = $3, goal = $4, status = $5, repository_url = $6, local_path = $7, base_branch = $8, orchestrator_task_id = $9, pr_url = $10, pr_branch = $11, reconcile_mode = $12, + goal_updated_at = CASE WHEN $13 THEN NOW() ELSE goal_updated_at END, version = version + 1, updated_at = NOW() WHERE id = $1 AND owner_id = $2 RETURNING * @@ -5083,6 +5085,7 @@ pub async fn update_directive_for_owner( .bind(pr_url) .bind(pr_branch) .bind(reconcile_mode) + .bind(goal_changed) .fetch_optional(pool) .await .map_err(RepositoryError::Database)?; @@ -5574,7 +5577,9 @@ pub async fn check_directive_idle( Ok(result.rows_affected() > 0) } -/// Update a directive's goal and bump goal_updated_at. Reactivates if idle. +/// Update a directive's goal and bump goal_updated_at. +/// Reactivates idle/paused directives and clears any stale orchestrator task +/// so that replanning triggers on the next tick. pub async fn update_directive_goal( pool: &PgPool, owner_id: Uuid, @@ -5586,7 +5591,8 @@ pub async fn update_directive_goal( UPDATE directives SET goal = $3, goal_updated_at = NOW(), - status = CASE WHEN status = 'idle' THEN 'active' ELSE status END, + status = CASE WHEN status IN ('idle', 'paused') THEN 'active' ELSE status END, + orchestrator_task_id = NULL, updated_at = NOW(), version = version + 1 WHERE id = $1 AND owner_id = $2 @@ -5913,6 +5919,34 @@ pub async fn clear_orchestrator_task( Ok(()) } +/// Cancel old planning tasks for a directive. +/// Marks any non-terminal planning/re-planning tasks as interrupted, +/// excluding the given new task. Identifies planning tasks by name prefix +/// ("Plan: " or "Re-plan: ") to avoid cancelling completion/verification tasks. +pub async fn cancel_old_planning_tasks( + pool: &PgPool, + directive_id: Uuid, + exclude_task_id: Uuid, +) -> Result<u64, sqlx::Error> { + let result = sqlx::query( + r#" + UPDATE tasks + SET status = 'interrupted', + completed_at = NOW(), + updated_at = NOW() + WHERE directive_id = $1 + AND id != $2 + AND (name LIKE 'Plan: %' OR name LIKE 'Re-plan: %') + AND status NOT IN ('completed', 'failed', 'merged', 'done', 'interrupted') + "#, + ) + .bind(directive_id) + .bind(exclude_task_id) + .execute(pool) + .await?; + Ok(result.rows_affected()) +} + /// Link a task to a step without changing step status. pub async fn link_task_to_step( pool: &PgPool, |
