From 62f305dc84f0f0d29d0965d0441942fb1f09274f Mon Sep 17 00:00:00 2001 From: soryu Date: Tue, 17 Feb 2026 02:02:59 +0000 Subject: feat: soryu-co/soryu: Handle completed orders during plan-orders flow --- .../frontend/src/components/orders/OrderDetail.tsx | 3 +- .../frontend/src/components/orders/OrderList.tsx | 5 ++- makima/frontend/src/lib/api.ts | 2 +- ...0260217000000_add_order_under_review_status.sql | 4 ++ makima/src/db/models.rs | 1 + makima/src/db/repository.rs | 30 ++++++++++++- makima/src/orchestration/directive.rs | 6 ++- makima/src/server/handlers/directives.rs | 51 ++++++++++++++++++++++ 8 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 makima/migrations/20260217000000_add_order_under_review_status.sql diff --git a/makima/frontend/src/components/orders/OrderDetail.tsx b/makima/frontend/src/components/orders/OrderDetail.tsx index 9d4c00c..12c87d1 100644 --- a/makima/frontend/src/components/orders/OrderDetail.tsx +++ b/makima/frontend/src/components/orders/OrderDetail.tsx @@ -11,6 +11,7 @@ import type { const STATUS_BADGE: Record = { open: { color: "text-[#75aafc] border-[rgba(117,170,252,0.4)]", label: "OPEN" }, in_progress: { color: "text-yellow-400 border-yellow-800", label: "IN PROGRESS" }, + under_review: { color: "bg-purple-400/20 text-purple-400 border-purple-800", label: "UNDER REVIEW" }, done: { color: "text-emerald-400 border-emerald-800", label: "DONE" }, archived: { color: "text-[#556677] border-[#2a3a5a]", label: "ARCHIVED" }, }; @@ -31,7 +32,7 @@ const TYPE_OPTIONS: { value: OrderType; color: string; label: string }[] = [ { value: "improvement", color: "text-emerald-400", label: "Improvement" }, ]; -const STATUS_OPTIONS: OrderStatus[] = ["open", "in_progress", "done", "archived"]; +const STATUS_OPTIONS: OrderStatus[] = ["open", "in_progress", "under_review", "done", "archived"]; interface OrderDetailProps { order: Order; diff --git a/makima/frontend/src/components/orders/OrderList.tsx b/makima/frontend/src/components/orders/OrderList.tsx index 1d279f7..213a332 100644 --- a/makima/frontend/src/components/orders/OrderList.tsx +++ b/makima/frontend/src/components/orders/OrderList.tsx @@ -4,6 +4,7 @@ import type { Order, OrderStatus, OrderPriority, OrderType } from "../../lib/api const STATUS_BADGE: Record = { open: { color: "text-[#75aafc] border-[rgba(117,170,252,0.4)]", label: "OPEN" }, in_progress: { color: "text-yellow-400 border-yellow-800", label: "IN PROGRESS" }, + under_review: { color: "text-purple-400 border-purple-800", label: "UNDER REVIEW" }, done: { color: "text-emerald-400 border-emerald-800", label: "DONE" }, archived: { color: "text-[#556677] border-[#2a3a5a]", label: "ARCHIVED" }, }; @@ -35,7 +36,7 @@ interface OrderListProps { onTypeFilter: (t: OrderType | undefined) => void; } -const STATUS_OPTIONS: (OrderStatus | "all")[] = ["all", "open", "in_progress", "done", "archived"]; +const STATUS_OPTIONS: (OrderStatus | "all")[] = ["all", "open", "in_progress", "under_review", "done", "archived"]; const TYPE_OPTIONS: (OrderType | "all")[] = ["all", "feature", "bug", "spike", "chore", "improvement"]; export function OrderList({ @@ -105,7 +106,7 @@ export function OrderList({ : "text-[#556677] hover:text-[#7788aa] border border-transparent" }`} > - {s === "all" ? "ALL" : s === "in_progress" ? "WIP" : s.toUpperCase()} + {s === "all" ? "ALL" : s === "in_progress" ? "WIP" : s === "under_review" ? "REVIEW" : s.toUpperCase()} ))} diff --git a/makima/frontend/src/lib/api.ts b/makima/frontend/src/lib/api.ts index 17bc20f..ed628f7 100644 --- a/makima/frontend/src/lib/api.ts +++ b/makima/frontend/src/lib/api.ts @@ -3281,7 +3281,7 @@ export async function pickUpOrders(directiveId: string): Promise, + pub order_id: Option, } /// Request to update a directive step. diff --git a/makima/src/db/repository.rs b/makima/src/db/repository.rs index cb6a0c6..bc61bee 100644 --- a/makima/src/db/repository.rs +++ b/makima/src/db/repository.rs @@ -5363,6 +5363,19 @@ pub async fn get_last_completed_step_task_id( // Directive Step CRUD // ============================================================================= +/// Get a single directive step by ID. +pub async fn get_directive_step( + pool: &PgPool, + step_id: Uuid, +) -> Result, sqlx::Error> { + sqlx::query_as::<_, DirectiveStep>( + r#"SELECT * FROM directive_steps WHERE id = $1"#, + ) + .bind(step_id) + .fetch_optional(pool) + .await +} + /// List all steps for a directive, ordered by order_index. pub async fn list_directive_steps( pool: &PgPool, @@ -5387,7 +5400,7 @@ pub async fn create_directive_step( req: CreateDirectiveStepRequest, ) -> Result { let generation = req.generation.unwrap_or(1); - sqlx::query_as::<_, DirectiveStep>( + let step = sqlx::query_as::<_, DirectiveStep>( r#" INSERT INTO directive_steps (directive_id, name, description, task_plan, depends_on, order_index, generation) VALUES ($1, $2, $3, $4, $5, $6, $7) @@ -5402,7 +5415,20 @@ pub async fn create_directive_step( .bind(req.order_index) .bind(generation) .fetch_one(pool) - .await + .await?; + + // Link the order to this step if an order_id was provided + if let Some(order_id) = req.order_id { + let _ = sqlx::query( + "UPDATE orders SET directive_step_id = $1, updated_at = NOW() WHERE id = $2", + ) + .bind(step.id) + .bind(order_id) + .execute(pool) + .await; + } + + Ok(step) } /// Batch create multiple directive steps. diff --git a/makima/src/orchestration/directive.rs b/makima/src/orchestration/directive.rs index 020c2e4..0a812ce 100644 --- a/makima/src/orchestration/directive.rs +++ b/makima/src/orchestration/directive.rs @@ -1513,7 +1513,7 @@ pub fn build_order_pickup_prompt( Review them and create steps to fulfil them.\n\n"); for (i, order) in orders.iter().enumerate() { prompt.push_str(&format!( - " {}. [{}] [{}] {} (id: {})\n", + " {}. [{}] [{}] {}\n orderId: {}\n", i + 1, order.priority, order.order_type, @@ -1644,13 +1644,15 @@ For each order (or group of related orders), create one or more steps: - dependsOn: UUIDs of steps this depends on (use IDs from previous add-step responses) - orderIndex: Execution phase number. Steps only start after ALL steps with a lower orderIndex complete. Steps with the same orderIndex run in parallel. Use ascending values (0, 1, 2, ...) to create sequential phases. +- orderId: The UUID of the order this step fulfils. Include this so the order is automatically marked done + when the step completes. Use the orderId shown in the order listing above. Submit steps using generation {generation}: makima directive add-step "Step Name" --description "..." --task-plan "..." --generation {generation} (Use --depends-on "uuid1,uuid2" for dependencies) Or batch: - makima directive batch-add-steps --json '[{{"name":"...","description":"...","taskPlan":"...","dependsOn":[],"orderIndex":0,"generation":{generation}}}]' + makima directive batch-add-steps --json '[{{"name":"...","description":"...","taskPlan":"...","dependsOn":[],"orderIndex":0,"generation":{generation},"orderId":""}}]' DEPENDENCY WORKTREE CONTINUATION: Each step runs in its own git worktree. How that worktree is initialised depends on dependsOn: diff --git a/makima/src/server/handlers/directives.rs b/makima/src/server/handlers/directives.rs index 960da94..fd7ae81 100644 --- a/makima/src/server/handlers/directives.rs +++ b/makima/src/server/handlers/directives.rs @@ -13,6 +13,7 @@ use crate::db::models::{ CreateDirectiveStepRequest, Directive, DirectiveListResponse, DirectiveStep, DirectiveWithSteps, PickUpOrdersResponse, UpdateDirectiveRequest, UpdateDirectiveStepRequest, UpdateGoalRequest, + UpdateOrderRequest, }; use crate::db::repository; use crate::orchestration::directive::build_order_pickup_prompt; @@ -1062,6 +1063,56 @@ pub async fn pick_up_orders( } }; + // Reconcile existing orders before picking up new ones + if let Ok(existing_orders) = repository::list_orders( + pool, + auth.owner_id, + Some("in_progress"), + None, + None, + Some(id), + None, + ) + .await + { + for order in &existing_orders { + if let Some(step_id) = order.directive_step_id { + if let Ok(Some(step)) = repository::get_directive_step(pool, step_id).await { + match step.status.as_str() { + "completed" => { + let order_update = UpdateOrderRequest { + status: Some("done".to_string()), + ..Default::default() + }; + let _ = repository::update_order( + pool, + auth.owner_id, + order.id, + order_update, + ) + .await; + } + "running" | "ready" | "pending" => { + let order_update = UpdateOrderRequest { + status: Some("under_review".to_string()), + ..Default::default() + }; + let _ = repository::update_order( + pool, + auth.owner_id, + order.id, + order_update, + ) + .await; + } + "failed" => { /* keep as in_progress for re-planning */ } + _ => {} + } + } + } + } + } + // Fetch available orders let orders = match repository::get_available_orders_for_pickup(pool, auth.owner_id).await { Ok(o) => o, -- cgit v1.2.3