diff options
| author | soryu <soryu@soryu.co> | 2026-01-24 14:54:21 +0000 |
|---|---|---|
| committer | soryu <soryu@soryu.co> | 2026-01-24 14:54:21 +0000 |
| commit | 9b028d8085d308e43239972348ab117d473caf73 (patch) | |
| tree | 161125f08c5e3d4009d2404e70616d7782947b6a | |
| parent | 1abc91f1da9beb96a54e466102f97e8e8c0b70e5 (diff) | |
| download | soryu-9b028d8085d308e43239972348ab117d473caf73.tar.gz soryu-9b028d8085d308e43239972348ab117d473caf73.zip | |
Clean up questions after removing tasks or contracts
| -rw-r--r-- | makima/src/server/handlers/contracts.rs | 3 | ||||
| -rw-r--r-- | makima/src/server/handlers/mesh.rs | 3 | ||||
| -rw-r--r-- | makima/src/server/state.rs | 64 |
3 files changed, 70 insertions, 0 deletions
diff --git a/makima/src/server/handlers/contracts.rs b/makima/src/server/handlers/contracts.rs index 462b385..f16f33d 100644 --- a/makima/src/server/handlers/contracts.rs +++ b/makima/src/server/handlers/contracts.rs @@ -612,6 +612,9 @@ pub async fn delete_contract( } } + // Clean up any pending supervisor questions for this contract + state.remove_pending_questions_for_contract(id); + // Clean up all task worktrees BEFORE deleting the contract // (because CASCADE delete will remove tasks from DB) cleanup_contract_worktrees(pool, &state, id).await; diff --git a/makima/src/server/handlers/mesh.rs b/makima/src/server/handlers/mesh.rs index 3d05f35..3d64eb4 100644 --- a/makima/src/server/handlers/mesh.rs +++ b/makima/src/server/handlers/mesh.rs @@ -482,6 +482,9 @@ pub async fn delete_task( } } + // Clean up any pending supervisor questions for this task + state.remove_pending_questions_for_task(id); + match repository::delete_task_for_owner(pool, id, auth.owner_id).await { Ok(true) => StatusCode::NO_CONTENT.into_response(), Ok(false) => ( diff --git a/makima/src/server/state.rs b/makima/src/server/state.rs index 5b75281..32c0af3 100644 --- a/makima/src/server/state.rs +++ b/makima/src/server/state.rs @@ -797,6 +797,70 @@ impl AppState { self.question_responses.remove(&question_id); } + /// Remove all pending questions for a specific task. + /// + /// This should be called when a task is deleted to clean up orphaned questions. + /// Returns the number of questions removed. + pub fn remove_pending_questions_for_task(&self, task_id: Uuid) -> usize { + // Collect question IDs to remove + let question_ids: Vec<Uuid> = self + .pending_questions + .iter() + .filter(|entry| entry.value().task_id == task_id) + .map(|entry| entry.value().question_id) + .collect(); + + let count = question_ids.len(); + + // Remove pending questions and their responses + for question_id in question_ids { + self.pending_questions.remove(&question_id); + self.question_responses.remove(&question_id); + } + + if count > 0 { + tracing::info!( + task_id = %task_id, + count = count, + "Cleaned up pending questions for deleted task" + ); + } + + count + } + + /// Remove all pending questions for a specific contract. + /// + /// This should be called when a contract is deleted to clean up orphaned questions. + /// Returns the number of questions removed. + pub fn remove_pending_questions_for_contract(&self, contract_id: Uuid) -> usize { + // Collect question IDs to remove + let question_ids: Vec<Uuid> = self + .pending_questions + .iter() + .filter(|entry| entry.value().contract_id == contract_id) + .map(|entry| entry.value().question_id) + .collect(); + + let count = question_ids.len(); + + // Remove pending questions and their responses + for question_id in question_ids { + self.pending_questions.remove(&question_id); + self.question_responses.remove(&question_id); + } + + if count > 0 { + tracing::info!( + contract_id = %contract_id, + count = count, + "Cleaned up pending questions for deleted contract" + ); + } + + count + } + /// Register a new daemon connection. /// /// Returns the connection_id for later reference. |
