Blog

Decomposition, the task graph and the plan gate

How to cut work for agents that run in parallel, what happens to the planning phase, and five questions to ask of a plan before a line is written.

Danh Hung, founder of Rindo · 2026-10-12 · 11 min read

Decomposition changed at the root, and the change is worth stating plainly: the criterion for cutting work is different. Work used to be cut by person. Front end for one, back end for another, the database for a third, each estimate carried by whoever would do it. The unit of work is now an agent running in parallel with other agents, so the governing criterion becomes: every unit must merge independently and verify independently. Everything below serves that one word, independent, because what kills throughput is not generation speed. It is large, entangled changes queueing for one reviewer.

A good unit has five properties

One intention: a pull request answers one why. Mixing a refactor into a feature is the fastest way to turn fifteen minutes of review into an hour. A done condition the machine can check: concrete criteria and tests, not “do the user part”. A fit to one span of context: the agent can load what it needs in one session, and a task that requires understanding half the codebase will drift. A bounded blast radius: you know in advance which files it touches, which connects straight to risk tiers, because a task that touches the core automatically takes the other review path. And a size measured in review cost, not coding cost. This is the inversion to remember. Code is cheap now, so “a day’s work” is the old ruler. The new one is “a diff one person can review carefully in about fifteen minutes”. The early studies of agent-authored pull requests on GitHub point the same way: the ones merged at once are the narrow, well-bounded ones, and the ones that stall are the ones that came back for rounds of refinement (Early-stage prediction of review effort in AI-generated pull requests, on the AIDev corpus).

Fix the joints, then slice vertically

Step one, from the spec and the design, pull out the contracts: schemas, interfaces, the API. Make those the tasks that are fixed first, usually by a person, as the design piece argued. Contracts are the joints that let everything after them run in parallel: one agent builds the calling side against the contract and a mock, another builds the implementing side, and neither waits.

Step two, cut the rest into vertical slices. Each task runs from the interface down to the database for one complete piece of behaviour, instead of cutting by horizontal layer. A back-end task on its own can be verified, through API, contract and integration tests, which is exactly what the contracts of step one make possible. Where a feature is accepted through a user interface, a back-end task on its own cannot be shown to the person accepting it, so the answer to “is this the right thing” waits for the layer above, and a vertical slice, which runs, can be shown and can be accepted, is the unit of progress. An API, an integration or a background job is accepted on its own terms and needs no slice. So the contract is the unit of parallelism, and the slice is the unit of progress wherever acceptance needs a screen.

Step three, for a new system, let one task go first as a walking skeleton: the thinnest path through the whole stack, carrying the CI, the deployment and the conventions, so that every later slice builds on that frame instead of each agent inventing a foundation.

File territory

Merge conflicts between agents are the largest hidden tax of parallel work. The principle: two tasks running at the same time may not edit the same file. You achieve it through architecture, not clever scheduling, which is where the design principle of hard boundaries and one directory per feature pays its direct dividend: good module boundaries make task territories fall apart naturally. The files everybody has to touch, the route registry, the central configuration, the migration index, are structural chokepoints. Either design them to be append-only or generated, or gather every edit to them into one integration task at the end of the chain. And always have that integration task, a person or an agent with a fresh context, that assembles the slices, runs the global checks and reads for consistency. Do not let “everything merged” mean “done”, because right in every piece and wrong as a whole is the coherence gap by its proper name.

In practice the roles reverse here as everywhere: have the model read the spec and the design and propose the task graph, tasks, dependencies, the files each will touch, the done condition for each, and let a person review the graph rather than draw the work breakdown by hand. The person looks hardest at the dependency edges, because a model tends to miss implicit dependencies through shared data, and at any task touching a risky zone. The graph then becomes the dashboard: which task runs, which waits for which contract.

Four things to avoid. Cutting by horizontal layer, the inheritance of cutting by person. A task called “the rest” that gathers the odds and ends, with no single intention and no done condition. Cutting so fine that the cost of coordination and review exceeds the work; below about fifty lines of diff, unless it is a hotfix, is usually too fine. And sending a second agent to help with a task in progress: one task, one agent, and if you want it faster, cut again rather than pile on.

Two kinds of plan

The output of decomposition is a plan, but the word carries two meanings and separating them makes everything click.

The task graph is the orchestration plan. It answers which units of work exist, how they depend on one another, which run in parallel, which wait for which contract, which touch which zone. It is a plan in the sense a project manager uses, close to a work breakdown structure, with two differences: the units are cut by the merge-and-verify criterion rather than by person-days, and it no longer carries the estimating and scheduling role, because coding time is no longer the main variable. Review capacity and dependency edges are.

The per-task plan is the implementation plan. For one task, it answers which files the agent intends to change, in what way, what tests will prove it, what the risks are. A task in the graph produces one when the agent picks it up. So the full chain is intent, spec, design, task graph (one per feature), per-task plan (one per task, generated just in time), code. Two layers, two reviewers, two kinds of error. Reviewing the graph catches structural errors: a missing dependency, two tasks on the same file, a task with no done condition. Reviewing a per-task plan catches errors of understanding: an agent about to do the wrong thing, or touch what it must not. Merging the two into one master plan is an anti-pattern. A document that both cuts the work and describes how each piece will be done is long, and stale by the time it is read, because the detailed how should be decided right before the doing, when the context is freshest. The graph is a living document; the first slice almost always reveals a dependency nobody saw, and editing the graph is normal and cheap. What needs to be stable is the contracts.

Intent
→
Spec
→
Design
→
Task graph
one per feature
→
Per-task plan
one per task, just in time
→
Code
The life of one node
take the task
plan
plan gate
code
→
↓
←
deterministic gate
diff review
merge
update the graph
↺ next node
Two plans, two reviewers, two kinds of error. The graph catches structure; the per-task plan catches understanding, at the cheapest point in the loop.

Where does decomposition sit, in design or in planning? On the seam. One half, what units the work can be cut into, is decided by the design in advance: the module boundaries, the contracts and the directory structure draw the possible cut lines, and at decomposition you follow them rather than drawing new ones. The other half, order, parallelism, routing by risk, attaching the acceptance condition, is planning, and changes with circumstances without touching the structure. The practical consequence matters more than the label: decomposition is the cheapest test of a design’s quality. If cutting the tasks is hard, every task crosses every other, no cut gives an independent slice, shared files sprout everywhere, the problem is not your cutting skill. The design is reporting coupling, and the fix is in the architecture.

Is there still a planning phase?

No standalone phase. The functions of planning survive, taken apart and attached elsewhere. Cutting the work became decomposition. Ordering and assignment live in the graph: the edges decide order, routing by risk tier decides which agent and which review path. Estimation shrinks the most: when code is cheap and parallel, completion time is no longer the sum of effort but the critical path through the contracts and the cadence of human review, so estimation moves from asking a developer how many days to measuring the system’s throughput, how many units clear the gates each week, and projecting it onto the graph. Tracking is the living graph: a node’s state, waiting for a contract, running, in review, merged, is more honest than any hand-painted percentage. And re-steering when reality diverges is the function that survives whole and gains value: deciding what to do when the first slice exposes a new dependency, what to prioritise when the review queue blocks, which node to cut when scope changes. It is not a phase. It is continuous, and it is a project manager’s real work.

So after decomposition there is no big planning meeting. There is a cadence: release tasks by the graph, pass each through its plan gate, merge, update the graph, reprioritise. If one ritual is kept, keep a short recurring look at the graph: which nodes are stuck, how long the review queue is, what remains on the contract critical path. Contracts and customers still ask for milestones and dates, and the honest way to give them is to project the living graph onto a timeline: group nodes into acceptance batches, divide the nodes remaining by the measured throughput, ten nodes at two a week is five weeks, add buffer at the risky ones. One source of data, two presentations. Each mergeable unit is also a unit the person accepting the work can see running, which is a better progress report than a percentage. Never let the presented timeline drive the internal work, because that is the shortest road back to waterfall.

The plan gate

Plan review connects at exactly one place: inside the life of each task, after the agent takes the node from the graph and before it changes a line. The rhythm of a node: take the task, read the spec, the design and the conventions, produce the plan, pass the plan gate, code, pass the deterministic gate, diff review, merge, update the graph. The gate is not a project phase. It repeats once per node, and it is the cheapest point of human intervention in the whole loop: fixing a line in a plan takes thirty seconds, and fixing a wrong direction after the code exists costs a regeneration and another review.

For the gate to be fast the plan has to be forced into a shape, which is the harness’s job, a template in the conventions, not the agent’s goodwill. Four sections of a few lines each. The reading of the task, which is the echo-back: a summary of the intent and the assumptions in use. The files it will touch, listed. How it will prove the result: which tests it writes, what it runs. The risks and the open questions. A plan longer than a page is a sign the task is too large; send it back to decomposition rather than reviewing it.

The reviewer reads with five questions, each catching a different error.

  1. Does the reading match the intent? This catches the intent gap, before a token of code has been paid for.
  2. Does the file list match the task’s territory? A file outside the graph’s expectation is scope drift or a dependency decomposition missed; the core touched outside the plan is the risk-tier alarm.
  3. Is the proof real? A plan that says “run the existing tests” for a new feature has no done condition. This is where fake tests and self-grading are stopped early.
  4. Does the approach follow the architecture? An agent inventing a fourth pattern is corrected here, far more cheaply than at diff review.
  5. Can I answer its questions now? Answer them into the plan. Each answer replaces a guess with a decision.

Depth is tiered like every other gate. An edge task at a low tier gets a one-minute look, or an automatic approval when the plan matches the graph and touches no protected zone. A core task, a migration, anything near money gets a careful read, sometimes two candidate plans to choose between. Do not read every plan at the same depth; a plan gate that becomes the new bottleneck has moved the jam, not cleared it.

The last point is the one most often missed. The output of the gate is not only approve or reject. It is data for improving the system. Each time a plan has to be corrected, ask why the agent misread: an ambiguous spec, fix the spec; a missing convention, add it; a graph that omitted a dependency, fix the graph; a repeated kind of error, add a rule to the harness. The same error at the plan gate for the third time, still being fixed by hand, means the flywheel is broken. Done right, the fraction of plans needing correction falls week by week, and that number is the health measure of the whole chain from intent to decomposition, because the plan is where every upstream ambiguity shows its face first.

In Rindo a task carries its dependencies, blocks and blocked by, with a loop refused, and can group subtasks under an epic; the tasks an agent files land in Triage for a person to sort, and a rewrite it proposes to a task’s description waits as a draft with a diff.