# The planning loop

Tyler Youk · updated 2026-10-31 · https://tyleryouk.com/writing/planning-loop

This is the process that a human and the planning agent run together, and its vocabulary is the one the
rest of the fleet uses to talk about work. If you stopped it, every agent would keep executing whatever is
already filed and then run out of work, because nothing else in the organization turns an intention into a
board issue.

What the five steps *mean* is on [How work flows](/writing/how-work-flows). This page is about the one
structural property that makes them trustworthy.

## No step is reported. Every step is derived {#no-step-is-reported-every-step-is-derived}

```text
1 BRIEF · 2 LOOP ⟳  ‖  3 FILE · 4 DISPATCHED · 5 DONE
                      ↑ planning ends, execution begins
```

| # | Step       | Derived from                                                    |
| - | ---------- | --------------------------------------------------------------- |
| 1 | BRIEF      | a brief event, with its timestamp, on the plan's registry entry |
| 2 | LOOP ⟳     | at least one recorded planning-agent turn on that entry         |
| 3 | FILE       | an issue reference on that entry                                |
| 4 | DISPATCHED | at least one referenced issue still open                        |
| 5 | DONE       | every referenced issue closed                                   |

So a plan cannot report the wrong step, because it has no field where anyone records the step. The
command center reads steps 3 through 5 off the board every time it renders. Steps 1 and 2 come from events
that a hook writes as they happen, not from a summary written afterwards.

`step < 3` is the whole definition of planning. The command center splits its planning and execution
sections on that single number. So the line between "still thinking" and "being built" is one comparison,
not a judgement anyone makes.

## Step zero asks three ordered questions, and the order is load-bearing {#step-zero-asks-three-ordered-questions-and-the-order-is-load-bearing}

Before step 1 there is a step zero. It asks for the mode, then the portfolio, then the subprocess. The
order matters because each answer narrows the next. The mode decides which section of a catalogue exists
at all, and the portfolio decides which catalogue.

There are exactly two modes, and their names are fixed literals, not descriptions:

| Mode          | What it means                                                                                                                |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `dispatch`    | agents execute the work, and the planning agent stops being involved at dispatch unless the chosen subprocess says otherwise |
| `interactive` | the human and the planning agent work it in session, with the human in it                                                    |

Nobody is asked whether a dispatched chain is watched. The answer is read from a `monitor: yes|no` field on
the subprocess chosen at the third question. There used to be two dispatch modes, one monitored and one
not. We merged them because the monitored mode repeated a fact the subprocess already held, and the two
could contradict each other.

`interactive` is the whole name. It is not *needs-human*, not *blocked* and not *manual*, and above all it
does not mean blocked. It is a mode for work that is running with a person in it. Work that really cannot
run unattended is *filed* as interactive. It is never filed as unattended work that later reports itself
stuck.

That distinction sounds pedantic, but it is not. Once "interactive" and "blocked" mean the same thing, you
can no longer tell a queue of work that is *running fine with a human in it* from a queue of work that is
*stopped*, and no dashboard can separate them afterwards.

## Two things end the loop, and both are read rather than remembered {#two-things-end-the-loop-and-both-are-read-rather-than-remembered}

Every turn in step 2 ends with a question to the human. Its last two options are, in this order, *research
more* and *file it, move to step 3*. The loop ends when one of exactly two things happens:

1. The human clicks the second option.
2. The human types the sentence that means the same thing.

An approving remark does not end the loop, and neither does enthusiasm about the artifact.

The click is the more interesting half, because it is nearly invisible. A click comes back as a tool result, not as a
message, so a per-turn injector that reads what the human said cannot see it at all. A separate hook reads
the answers field of the tool response, and only that field. That hook observes the exit directly instead
of guessing it from the human's tone.

Neither exit advances the step, though. Filing does. The click ends a conversation, and the step changes
only when an issue exists.

## Filing is one command, and its shape is a real decision {#filing-is-one-command-and-its-shape-is-a-real-decision}

Filing either chains the work or groups it into waves, and the difference matters. A plan designed as
parallel groups but filed as a chain has exactly one dispatchable issue. The mechanics, the refusal rule
and the case that led to the rule are on [The board](/writing/the-board).

## The loop ends at dispatch {#the-loop-ends-at-dispatch}

Steps 1 and 2 of the next plan run at the same time as the last plan executes. The scheduler, the reaper
and the announcer handle the rest of the last plan, and the planner does not.

Watching an agent work looks exactly like not trusting it. If the only reason to stay is to see whether
the run succeeds, the run already announces that on the issue. Staying only means the planner works on one
plan at a time.

The step-4 mode is not the planning mode, even though they use the same words. The planning mode is the
step zero question above, and it decides which subprocesses exist. The step-4 mode is what the board shows
for work already dispatched. They are separate vocabularies that happen to share words. One table in
doctrine maps one to the other, so nobody has to work out the mapping at the keyboard.

## Where this loop is currently weakest {#where-this-loop-is-currently-weakest}

* A doctrine file states a subprocess count that the files on disk have outgrown. The file puts a count at
  the top of its own table, and a directory walk returns one more than that. The same section already has
  a note saying the count drifted *once before*, telling the reader to re-derive it instead of editing the
  cell. It drifted again anyway. A number in prose is a claim about the filesystem, and nothing was
  re-checking it.
* Nothing ties the two doctrine files to each other. One holds the full loop. The other is the shorter
  version that the executing agents read, and by design the same path resolves to a *different file*
  depending on the agent's class. Nothing checks a rule changed in one against the other. Continuous
  integration cannot see a contradiction between them. It shows up as an agent doing the planner's job, or
  the reverse.
* The repository that documents the loop cannot check the loop's own machinery. Everything that derives
  steps 1 and 2 lives in a separate package with its own version gate. So the repository's assertion
  checker can check the *doctrine* but never the *mechanism*. One script reads the real state across
  boxes, and someone has to run it by hand.

Publishing that third weakness matters more than it looks. When a process's enforcement ships on a
different release schedule from its documentation, the two drift apart, and neither side can see the
drift.
