summaryrefslogtreecommitdiff
path: root/makima/src/db
diff options
context:
space:
mode:
Diffstat (limited to 'makima/src/db')
-rw-r--r--makima/src/db/models.rs8
-rw-r--r--makima/src/db/repository.rs103
2 files changed, 110 insertions, 1 deletions
diff --git a/makima/src/db/models.rs b/makima/src/db/models.rs
index 169f468..66c0a30 100644
--- a/makima/src/db/models.rs
+++ b/makima/src/db/models.rs
@@ -2717,7 +2717,6 @@ pub struct Directive {
pub memory_enabled: bool,
pub goal_updated_at: DateTime<Utc>,
pub started_at: Option<DateTime<Utc>>,
- pub memory_enabled: bool,
pub version: i32,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
@@ -2820,6 +2819,13 @@ pub struct UpdateGoalRequest {
pub goal: String,
}
+/// Response for cleanup_directive_tasks.
+#[derive(Debug, Serialize, ToSchema)]
+#[serde(rename_all = "camelCase")]
+pub struct CleanupTasksResponse {
+ pub deleted: i64,
+}
+
/// Request to create a directive step.
#[derive(Debug, Deserialize, ToSchema)]
#[serde(rename_all = "camelCase")]
diff --git a/makima/src/db/repository.rs b/makima/src/db/repository.rs
index 95460f7..127f4cd 100644
--- a/makima/src/db/repository.rs
+++ b/makima/src/db/repository.rs
@@ -5105,6 +5105,68 @@ pub async fn delete_directive_for_owner(
Ok(result.rows_affected() > 0)
}
+/// Clean up terminal tasks associated with a directive.
+///
+/// Deletes tasks in terminal states (completed, failed, merged, done, interrupted)
+/// that belong to this directive, excluding tasks currently referenced by
+/// `completion_task_id` or `orchestrator_task_id` on the directive.
+/// NULLs out `task_id` on directive_steps for deleted tasks.
+pub async fn cleanup_directive_tasks(
+ pool: &PgPool,
+ owner_id: Uuid,
+ directive_id: Uuid,
+) -> Result<i64, sqlx::Error> {
+ // NULL out task_id on steps that reference terminal tasks we're about to delete
+ sqlx::query(
+ r#"
+ UPDATE directive_steps
+ SET task_id = NULL
+ WHERE directive_id = $1
+ AND task_id IS NOT NULL
+ AND task_id IN (
+ SELECT t.id FROM tasks t
+ WHERE t.directive_id = $1
+ AND t.owner_id = $2
+ AND t.status IN ('completed', 'failed', 'merged', 'done', 'interrupted')
+ AND t.id NOT IN (
+ SELECT COALESCE(d.completion_task_id, '00000000-0000-0000-0000-000000000000')
+ FROM directives d WHERE d.id = $1
+ UNION
+ SELECT COALESCE(d.orchestrator_task_id, '00000000-0000-0000-0000-000000000000')
+ FROM directives d WHERE d.id = $1
+ )
+ )
+ "#,
+ )
+ .bind(directive_id)
+ .bind(owner_id)
+ .execute(pool)
+ .await?;
+
+ // Delete terminal tasks not currently referenced by the directive
+ let result = sqlx::query(
+ r#"
+ DELETE FROM tasks
+ WHERE directive_id = $1
+ AND owner_id = $2
+ AND status IN ('completed', 'failed', 'merged', 'done', 'interrupted')
+ AND id NOT IN (
+ SELECT COALESCE(d.completion_task_id, '00000000-0000-0000-0000-000000000000')
+ FROM directives d WHERE d.id = $1
+ UNION
+ SELECT COALESCE(d.orchestrator_task_id, '00000000-0000-0000-0000-000000000000')
+ FROM directives d WHERE d.id = $1
+ )
+ "#,
+ )
+ .bind(directive_id)
+ .bind(owner_id)
+ .execute(pool)
+ .await?;
+
+ Ok(result.rows_affected() as i64)
+}
+
// =============================================================================
// Directive Completion Helpers
// =============================================================================
@@ -5499,6 +5561,7 @@ pub struct StepForDispatch {
pub task_plan: Option<String>,
pub order_index: i32,
pub generation: i32,
+ pub depends_on: Vec<Uuid>,
// Directive fields
pub owner_id: Uuid,
pub directive_title: String,
@@ -5521,6 +5584,7 @@ pub async fn get_ready_steps_for_dispatch(
ds.task_plan,
ds.order_index,
ds.generation,
+ ds.depends_on,
d.owner_id,
d.title AS directive_title,
d.repository_url,
@@ -5538,6 +5602,45 @@ pub async fn get_ready_steps_for_dispatch(
.await
}
+/// Task info for a dependency step (step → linked task).
+#[derive(Debug, Clone, sqlx::FromRow)]
+pub struct DependencyTaskInfo {
+ pub step_id: Uuid,
+ pub task_id: Uuid,
+ pub task_name: String,
+}
+
+/// Resolve dependency step UUIDs to their linked task IDs and names.
+/// Returns results in the same order as the input `depends_on` slice.
+pub async fn get_step_dependency_tasks(
+ pool: &PgPool,
+ depends_on: &[Uuid],
+) -> Result<Vec<DependencyTaskInfo>, sqlx::Error> {
+ if depends_on.is_empty() {
+ return Ok(vec![]);
+ }
+ let rows = sqlx::query_as::<_, DependencyTaskInfo>(
+ r#"
+ SELECT ds.id AS step_id, t.id AS task_id, t.name AS task_name
+ FROM directive_steps ds
+ JOIN tasks t ON t.id = ds.task_id
+ WHERE ds.id = ANY($1)
+ "#,
+ )
+ .bind(depends_on)
+ .fetch_all(pool)
+ .await?;
+
+ // Re-order to match input ordering
+ let mut ordered = Vec::with_capacity(depends_on.len());
+ for dep_id in depends_on {
+ if let Some(row) = rows.iter().find(|r| r.step_id == *dep_id) {
+ ordered.push(row.clone());
+ }
+ }
+ Ok(ordered)
+}
+
/// A running step joined with its task's current status.
#[derive(Debug, Clone, sqlx::FromRow)]
pub struct RunningStepWithTask {