summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-02-11 00:33:36 +0000
committersoryu <soryu@soryu.co>2026-02-11 00:33:36 +0000
commit9bd6eacaa9ebe860842b5d5cfbf2b7d2d0293ab1 (patch)
tree2b5dceee69f2390ab5f2c29c6fe39e091d7ca318
parent15b6e5fba161a194fe5427d7d29b0c4286423260 (diff)
downloadsoryu-9bd6eacaa9ebe860842b5d5cfbf2b7d2d0293ab1.tar.gz
soryu-9bd6eacaa9ebe860842b5d5cfbf2b7d2d0293ab1.zip
Fix DAG ordering
-rw-r--r--makima/frontend/src/components/directives/DirectiveDAG.tsx40
-rw-r--r--makima/src/db/repository.rs6
-rw-r--r--makima/src/orchestration/directive.rs4
3 files changed, 20 insertions, 30 deletions
diff --git a/makima/frontend/src/components/directives/DirectiveDAG.tsx b/makima/frontend/src/components/directives/DirectiveDAG.tsx
index f288a0d..27a80ac 100644
--- a/makima/frontend/src/components/directives/DirectiveDAG.tsx
+++ b/makima/frontend/src/components/directives/DirectiveDAG.tsx
@@ -16,37 +16,19 @@ interface Layer {
function topoSort(steps: DirectiveStep[]): Layer[] {
if (steps.length === 0) return [];
- const stepMap = new Map(steps.map((s) => [s.id, s]));
- const assigned = new Set<string>();
- const layers: Layer[] = [];
-
- // Iteratively find steps whose dependencies are all assigned
- let remaining = [...steps];
- while (remaining.length > 0) {
- const layer: DirectiveStep[] = [];
- for (const step of remaining) {
- const depsResolved = step.dependsOn.every(
- (depId) => assigned.has(depId) || !stepMap.has(depId)
- );
- if (depsResolved) {
- layer.push(step);
- }
- }
-
- if (layer.length === 0) {
- // Cycle detected or orphaned — push all remaining
- layers.push({ steps: remaining });
- break;
- }
-
- for (const s of layer) {
- assigned.add(s.id);
- }
- layers.push({ steps: layer.sort((a, b) => a.orderIndex - b.orderIndex) });
- remaining = remaining.filter((s) => !assigned.has(s.id));
+ // Group steps by orderIndex — each unique orderIndex is one execution phase
+ const byOrder = new Map<number, DirectiveStep[]>();
+ for (const step of steps) {
+ const group = byOrder.get(step.orderIndex) ?? [];
+ group.push(step);
+ byOrder.set(step.orderIndex, group);
}
- return layers;
+ // Sort groups by ascending orderIndex
+ const sortedKeys = [...byOrder.keys()].sort((a, b) => a - b);
+ return sortedKeys.map((key) => ({
+ steps: byOrder.get(key)!.sort((a, b) => a.name.localeCompare(b.name)),
+ }));
}
export function DirectiveDAG({ steps, onComplete, onFail, onSkip }: DirectiveDAGProps) {
diff --git a/makima/src/db/repository.rs b/makima/src/db/repository.rs
index 358ab48..7afbeea 100644
--- a/makima/src/db/repository.rs
+++ b/makima/src/db/repository.rs
@@ -5371,6 +5371,12 @@ pub async fn advance_directive_ready_steps(
JOIN directive_steps ds ON ds.id = dep_id
WHERE ds.status NOT IN ('completed', 'skipped')
)
+ AND NOT EXISTS (
+ SELECT 1 FROM directive_steps prev
+ WHERE prev.directive_id = $1
+ AND prev.order_index < directive_steps.order_index
+ AND prev.status NOT IN ('completed', 'skipped', 'failed')
+ )
RETURNING *
"#,
)
diff --git a/makima/src/orchestration/directive.rs b/makima/src/orchestration/directive.rs
index 6121529..15cc7ed 100644
--- a/makima/src/orchestration/directive.rs
+++ b/makima/src/orchestration/directive.rs
@@ -641,7 +641,9 @@ For each step, define:
- description: What to do and acceptance criteria
- taskPlan: Full instructions for the Claude instance (include file paths, patterns to follow)
- dependsOn: UUIDs of steps this depends on (use IDs from previous add-step responses)
-- orderIndex: Execution order hint
+- 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.
+ Use dependsOn for fine-grained control within the same phase.
Submit steps:
makima directive add-step "Step Name" --description "..." --task-plan "..."