summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--makima/frontend/src/components/orders/OrderDetail.tsx3
-rw-r--r--makima/frontend/src/components/orders/OrderList.tsx5
-rw-r--r--makima/frontend/src/lib/api.ts2
-rw-r--r--makima/migrations/20260217000000_add_order_under_review_status.sql4
-rw-r--r--makima/src/db/models.rs1
-rw-r--r--makima/src/db/repository.rs30
-rw-r--r--makima/src/orchestration/directive.rs6
-rw-r--r--makima/src/server/handlers/directives.rs51
8 files changed, 94 insertions, 8 deletions
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<OrderStatus, { color: string; label: string }> = {
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<OrderStatus, { color: string; label: string }> = {
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()}
</button>
))}
</div>
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<PickUpOrdersRes
// =============================================================================
export type OrderPriority = "critical" | "high" | "medium" | "low" | "none";
-export type OrderStatus = "open" | "in_progress" | "done" | "archived";
+export type OrderStatus = "open" | "in_progress" | "under_review" | "done" | "archived";
export type OrderType = "feature" | "bug" | "spike" | "chore" | "improvement";
export interface Order {
diff --git a/makima/migrations/20260217000000_add_order_under_review_status.sql b/makima/migrations/20260217000000_add_order_under_review_status.sql
new file mode 100644
index 0000000..166be32
--- /dev/null
+++ b/makima/migrations/20260217000000_add_order_under_review_status.sql
@@ -0,0 +1,4 @@
+-- Add under_review status to orders for orders whose steps exist but are not yet merged
+ALTER TABLE orders DROP CONSTRAINT IF EXISTS orders_status_check;
+ALTER TABLE orders ADD CONSTRAINT orders_status_check
+ CHECK (status IN ('open', 'in_progress', 'under_review', 'done', 'archived'));
diff --git a/makima/src/db/models.rs b/makima/src/db/models.rs
index 2951159..e53dcce 100644
--- a/makima/src/db/models.rs
+++ b/makima/src/db/models.rs
@@ -2860,6 +2860,7 @@ pub struct CreateDirectiveStepRequest {
#[serde(default)]
pub order_index: i32,
pub generation: Option<i32>,
+ pub order_id: Option<Uuid>,
}
/// 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<Option<DirectiveStep>, 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<DirectiveStep, sqlx::Error> {
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":"<uuid-of-order>"}}]'
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,