From 15b6e5fba161a194fe5427d7d29b0c4286423260 Mon Sep 17 00:00:00 2001 From: soryu Date: Tue, 10 Feb 2026 14:50:07 +0000 Subject: Add auto-PR creation for remote repos in directives --- makima/src/db/models.rs | 7 +++ makima/src/db/repository.rs | 123 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 128 insertions(+), 2 deletions(-) (limited to 'makima/src/db') diff --git a/makima/src/db/models.rs b/makima/src/db/models.rs index 9159fd5..542339f 100644 --- a/makima/src/db/models.rs +++ b/makima/src/db/models.rs @@ -2711,6 +2711,9 @@ pub struct Directive { pub local_path: Option, pub base_branch: Option, pub orchestrator_task_id: Option, + pub pr_url: Option, + pub pr_branch: Option, + pub completion_task_id: Option, pub goal_updated_at: DateTime, pub started_at: Option>, pub version: i32, @@ -2758,6 +2761,8 @@ pub struct DirectiveSummary { pub status: String, pub repository_url: Option, pub orchestrator_task_id: Option, + pub pr_url: Option, + pub completion_task_id: Option, pub version: i32, pub created_at: DateTime, pub updated_at: DateTime, @@ -2797,6 +2802,8 @@ pub struct UpdateDirectiveRequest { pub local_path: Option, pub base_branch: Option, pub orchestrator_task_id: Option, + pub pr_url: Option, + pub pr_branch: Option, pub version: Option, } diff --git a/makima/src/db/repository.rs b/makima/src/db/repository.rs index 323e74e..358ab48 100644 --- a/makima/src/db/repository.rs +++ b/makima/src/db/repository.rs @@ -4991,7 +4991,8 @@ pub async fn list_directives_for_owner( r#" SELECT d.id, d.owner_id, d.title, d.goal, d.status, d.repository_url, - d.orchestrator_task_id, d.version, d.created_at, d.updated_at, + d.orchestrator_task_id, d.pr_url, d.completion_task_id, + d.version, d.created_at, d.updated_at, COALESCE((SELECT COUNT(*) FROM directive_steps WHERE directive_id = d.id), 0) as total_steps, COALESCE((SELECT COUNT(*) FROM directive_steps WHERE directive_id = d.id AND status = 'completed'), 0) as completed_steps, COALESCE((SELECT COUNT(*) FROM directive_steps WHERE directive_id = d.id AND status = 'running'), 0) as running_steps, @@ -5043,12 +5044,15 @@ pub async fn update_directive_for_owner( let local_path = req.local_path.as_deref().or(current.local_path.as_deref()); let base_branch = req.base_branch.as_deref().or(current.base_branch.as_deref()); let orchestrator_task_id = req.orchestrator_task_id.or(current.orchestrator_task_id); + let pr_url = req.pr_url.as_deref().or(current.pr_url.as_deref()); + let pr_branch = req.pr_branch.as_deref().or(current.pr_branch.as_deref()); let result = sqlx::query_as::<_, Directive>( r#" UPDATE directives SET title = $3, goal = $4, status = $5, repository_url = $6, local_path = $7, - base_branch = $8, orchestrator_task_id = $9, version = version + 1, updated_at = NOW() + base_branch = $8, orchestrator_task_id = $9, pr_url = $10, pr_branch = $11, + version = version + 1, updated_at = NOW() WHERE id = $1 AND owner_id = $2 RETURNING * "#, @@ -5062,6 +5066,8 @@ pub async fn update_directive_for_owner( .bind(local_path) .bind(base_branch) .bind(orchestrator_task_id) + .bind(pr_url) + .bind(pr_branch) .fetch_optional(pool) .await .map_err(RepositoryError::Database)?; @@ -5095,6 +5101,119 @@ pub async fn delete_directive_for_owner( Ok(result.rows_affected() > 0) } +// ============================================================================= +// Directive Completion Helpers +// ============================================================================= + +/// Row type for completed step tasks. +#[derive(Debug, Clone, sqlx::FromRow)] +pub struct CompletedStepTask { + pub step_name: String, + pub task_id: Uuid, + pub task_name: String, +} + +/// Row type for directive completion task status check. +#[derive(Debug, Clone, sqlx::FromRow)] +pub struct DirectiveCompletionCheck { + pub directive_id: Uuid, + pub completion_task_id: Uuid, + pub task_status: String, + pub pr_url: Option, +} + +/// Get idle directives that need a completion task spawned. +/// Conditions: status = 'idle', no completion_task_id, has repository_url, +/// and has at least one completed step with a task_id. +pub async fn get_idle_directives_needing_completion( + pool: &PgPool, +) -> Result, sqlx::Error> { + sqlx::query_as::<_, Directive>( + r#" + SELECT d.* + FROM directives d + WHERE d.status = 'idle' + AND d.completion_task_id IS NULL + AND d.repository_url IS NOT NULL + AND EXISTS ( + SELECT 1 FROM directive_steps ds + WHERE ds.directive_id = d.id + AND ds.status = 'completed' + AND ds.task_id IS NOT NULL + ) + "#, + ) + .fetch_all(pool) + .await +} + +/// Get directives with active completion tasks, joined with task status. +pub async fn get_completion_tasks_to_check( + pool: &PgPool, +) -> Result, sqlx::Error> { + sqlx::query_as::<_, DirectiveCompletionCheck>( + r#" + SELECT d.id as directive_id, d.completion_task_id, t.status as task_status, d.pr_url + FROM directives d + JOIN tasks t ON t.id = d.completion_task_id + WHERE d.completion_task_id IS NOT NULL + "#, + ) + .fetch_all(pool) + .await +} + +/// Assign a completion task to a directive. +pub async fn assign_completion_task( + pool: &PgPool, + directive_id: Uuid, + task_id: Uuid, +) -> Result<(), sqlx::Error> { + sqlx::query( + r#"UPDATE directives SET completion_task_id = $2, updated_at = NOW() WHERE id = $1"#, + ) + .bind(directive_id) + .bind(task_id) + .execute(pool) + .await?; + Ok(()) +} + +/// Clear the completion task from a directive. +pub async fn clear_completion_task( + pool: &PgPool, + directive_id: Uuid, +) -> Result<(), sqlx::Error> { + sqlx::query( + r#"UPDATE directives SET completion_task_id = NULL, updated_at = NOW() WHERE id = $1"#, + ) + .bind(directive_id) + .execute(pool) + .await?; + Ok(()) +} + +/// Get completed step tasks for a directive (steps that have completed with an assigned task). +pub async fn get_completed_step_tasks( + pool: &PgPool, + directive_id: Uuid, +) -> Result, sqlx::Error> { + sqlx::query_as::<_, CompletedStepTask>( + r#" + SELECT ds.name as step_name, ds.task_id, t.name as task_name + FROM directive_steps ds + JOIN tasks t ON t.id = ds.task_id + WHERE ds.directive_id = $1 + AND ds.status = 'completed' + AND ds.task_id IS NOT NULL + ORDER BY ds.order_index, ds.created_at + "#, + ) + .bind(directive_id) + .fetch_all(pool) + .await +} + // ============================================================================= // Directive Step CRUD // ============================================================================= -- cgit v1.2.3