Task DAG & dependencies
How goals become a dependency graph of tasks, how it executes with bounded concurrency, and how delegation and rework drain in one cycle.
Decomposition turns a goal into a task DAG — subtasks that can depend on earlier ones. The runtime executes in dependency order: a task runs only once the work it builds on is done, and that upstream output is fed into the assignee’s prompt — so agents build on each other instead of working blind.
The task record
Every task is a persisted record. The fields that drive coordination:
dependsOn— a task runs only once its upstream is done, and receives that output as context.delegatedBy— records who handed a task down, so review routes back to the manager.depth— bounds the delegate→integrate chain (maxDelegationDepth).kind: 'integration'— marks the follow-up a manager runs to combine its reports’ work; an integration step never re-fans-out.qualityFlag/score— persist the review outcome so downstream tasks and the operator can tell accepted-clean from accepted-below-the-bar.
Decomposition
The coordinator asks the lead to propose 2–N subtasks as a JSON plan, seeded with the meeting’s agreed decision so tasks implement the debated direction rather than restate the goal title. Then:
- Each assignee is resolved — an id or role maps to an existing agent; an unknown role hires a specialist (see Hiring). The plan may pick the hire’s brain. If no usable assignee is named, the fallback picks the least-loaded role-matched agent (not raw array position), keeping the lead free to coordinate.
dependsOnstep numbers are resolved to real task ids, keeping only backward references — so the graph is acyclic by construction. A dropped forward/self reference is audited (tasks.dep.dropped) so a clipped plan is visible.
If the model gives no usable plan (for example, the fake brain), decomposition falls back to one task per agent.
Execution order & concurrency
Tasks execute in dependency order with bounded concurrency (TASK_CONCURRENCY,
default 4):
- A node runs only once every in-batch dependency has completed. Out-of-batch deps are assumed satisfied (finished in a prior cycle).
- A node that returns unfinished marks its dependents skipped this run — they don’t build on missing input.
- Failure isolation: a node whose work throws is recorded blocked, never rejecting the batch, so one flaky agent can’t discard every sibling’s completed work. The batch always resolves with a partial result map.
- Dispatch gate: once the cycle is aborted or over budget, new dispatches stop; in-flight nodes drain and the rest defer.
Independent tasks therefore run concurrently; dependent tasks wait and inherit their upstream’s output.
The intra-cycle drain
Delegation and rework create new board tasks during execution — which a
single execution pass can’t see (its set of tasks is fixed when it starts). So
the runtime wraps execution in a bounded drain loop: it repeatedly gathers
every task that’s still runnable and hasn’t run yet this cycle, executes that
batch, and repeats — up to maxDelegationDepth + 2 passes, stopping early once
a pass completes nothing new (only rework or blocked tasks remain).
This makes a delegate → execute → integrate chain resolve within one cycle
instead of costing one wall-clock tick per tree level. A task already run this
cycle is never re-run — rework correctly waits for the next cycle.
Rework holds its dependents
A task sent back for review rework holds its dependents until it lands — downstream work never builds on output that’s still being fixed. When the task is finally accepted, its dependents unblock and inherit the corrected result.
Upstream context
When a dependent task runs, its prompt includes the results of the tasks it
depends on. If a dependency was force-accepted below the quality bar, that
context is prefixed with [ACCEPTED BELOW QUALITY BAR, score N] so the dependent
can hedge rather than trust it blindly.