Chapter 8: The Agent's Procedure¶
Orchestrating a Multi-Step Change¶
1. Why Sequences Break¶
The change touched five files: a schema migration, a deploy, a verification run, a cut-over and a cleanup. The agent planned all five in its head, ran them in one pass, and failed at the fourth. The error named a missing configuration key. It did not say whether the migration had applied, whether the deploy had taken the traffic, or whether the verification had ever run. There was one gate, at the end, and the end is where the information runs out.
That is the shape of the problem. A sequence that lives in prose is a sequence the agent re-derives every time, and re-derivation is where steps get skipped. Nothing in the repository knows which step the run is on, so the failure arrives with no step to blame and the resume starts at the top.
The three structures assume the work is repeatable. The workflow structure is the one this chapter makes concrete: it is where the prompt and the engineering are checked, in order, on the way to a change. A single edit needs no help. A change that touches five files and can fail in the middle needs a procedure: the ordered steps, each with a gate, written as a contract rather than a plan. A play is the file that states one procedure — the artefact, in the repository, reviewed like code.
Chapter 7 made one session's work resumable across tools. This chapter makes a multi-step procedure resumable inside that work: a procedure records which step it is on, so a resume starts at that step instead of at the top.
A procedure also carries approval points — steps that cannot proceed without a decision, by design. Those are not escalations. An approval point is planned and sits where the procedure put it; an escalation is a condition that stops a run wherever it appears. This chapter defines the first; the next defines the second.
💰 Cost Check: a procedure costs a review and a maintenance pass; the absence of one costs the re-derivation on every run, plus the failures that arrive at the end with no step to blame. Write procedures for work that repeats and fails in the middle — the two conditions where the arithmetic favours them — and delete the ones that ran once.
🎯 What You'll Learn¶
- The anatomy of a step — action, gate, approval point, and where a resumed run continues
- Why a procedure is a contract — the agent's, in the repository, not the Orchestrator's checklist
- Play, engine and board — which of the three belongs in your repository, and which you adopt
- When not to write one — the work that costs more with a procedure than without
The Five-File Change¶
| Without a procedure | With one |
|---|---|
| the agent plans five steps in its head | each step states its action and its gate |
| a failure at the end names one symptom | a failure names the step that failed |
| resumption starts from the top | resumption starts from the recorded step |
| two agents do it two ways | one way, reviewed |
flowchart LR
S["step"] --> A["action"]
A --> G{"gate"}
G -->|"passes"| N["next step"]
G -->|"fails"| F["stop — the gate names the fix"]
G -->|"needs a human"| P["approval point"]
What this shows: every step ends at a gate, and a gate has three outcomes — pass, fail, or escalate.
✅ What to Check¶
- Take your longest repeated change. Can an agent name the step it is on, right now, from the repository?
- Does the procedure have a gate per step, or one gate at the end?
2. Procedure Anatomy¶
Three words do most of the work in this chapter, and each means one thing:
| Word | What it means here | What it is not |
|---|---|---|
| sequence | ordered work, however it is written — including in someone's head | not a procedure until every step has a gate |
| procedure | a sequence written down, with a gate on every step | not the tool that runs it |
| play | the file that states one procedure | not the engine, and not the board |
A procedure is a list of steps, and a step has four parts:
| Part | What it states | Example |
|---|---|---|
| action | the one operation | apply the migration to staging |
| gate | what proves it worked | the schema validator passes |
| approval point | when a human decides | before production |
| resume | where to continue | step 4 if the gate passed |
Names are invariants, not tasks. A step named "check the migration" describes an activity; a step named "the migration applies cleanly to a copy of production" describes a state that is either true or false. The second can fail; the first can only be "done".
The Skeleton¶
# plays/<name>.yaml — the play, as the agent's contract
name: <what the procedure delivers>
trigger: <when this play is the right one>
steps:
- id: 1
invariant: <the state that must be true after this step>
action: <the one operation>
gate: <the check, with its command>
approval: none | human:<who>
on_fail: stop | run:<step-id>
stop: <the observation that aborts the run>
rollback: <how to return to the last known-good state>
A play is more than its steps. The three fields around them are what separate a play from a to-do list:
| Field | What it answers | Why it is not optional |
|---|---|---|
| trigger | when this play is the right one | a play applied to the wrong task is a sequence of plausible mistakes |
| stop | what observation aborts the run | without it, the run improvises at the moment improvisation is most expensive. This adds a stop for this play; the estate-wide conditions in the next chapter apply to every step and cannot be removed |
| rollback | how to return to the last known-good state | the point of gates is that the cheap failures stay cheap |
This is the same anatomy the Playbook states in prose — trigger, steps with gates and approvals, stop condition, rollback. The YAML is the machine-readable form of one play; the Playbook is where the plays are collected and reviewed.
The Five-File Change, as a Play¶
The scenario from §1, written as a procedure. Three rehearsal steps that can fail cheaply, one production step behind an approval:
# plays/migration-to-production.yaml — the five-file change from §1
name: the new schema is live and the old path is still draining
trigger: a migration that changes the schema under a running service
steps:
- id: 1
invariant: the migration applies cleanly to a copy of production
action: restore last night's snapshot into the rehearsal database, then apply the migration
gate: scripts/check-schema.sh --rehearsal
approval: none
on_fail: stop
- id: 2
invariant: the old and the new schema both accept writes
action: deploy the dual-write build to staging
gate: scripts/check-dual-write.sh --staging
approval: none
on_fail: stop
- id: 3
invariant: the rehearsal cut-over completes with no reads left on the old path
action: run the cut-over against the rehearsal database
gate: scripts/check-cutover.sh --rehearsal
approval: none
on_fail: stop
- id: 4
invariant: production serves the new schema with the old path still draining
action: apply the migration to production and enable dual-write
gate: scripts/check-production.sh --window=30m
approval: human:the-orchestrator
on_fail: stop
stop: the rehearsal database cannot be restored, or the cut-over leaves reads on the old path
rollback: keep dual-write on and re-point the service at the old schema; the migration has a tested down path
Read it top to bottom and the argument is visible: the three steps that can be ruined for free come first, the one that cannot waits behind an approval, and every step states the state it leaves behind. The failure in §1 named a configuration key; the same failure here names step 2, its gate, and the command that fixes it.
One run, recorded. The play above, run twice — the second time after a failed cut-over:
run 1 step 1 green schema applies to the rehearsal copy
step 2 green dual-write accepts on both schemas
step 3 red the cut-over leaves four reads on the old path
step 4 — not attempted; the approval was never requested
verdict: failed at step 3 — fix the leaking reader, resume at step 3
run 2 step 3 green no reads on the old path
step 4 human:the-orchestrator approved, window 30m
step 4 green production serves the new schema, old path draining
verdict: passed
The second run does not re-read the plan, re-run steps 1 and 2, or ask whether the migration applies. It starts at the first gate that is not green — which is the whole return on writing the play down.
Resumability comes from the id and the recorded gate status: a resumed run starts at the first step whose gate is not green. Idempotency comes from the action being repeatable — a step that cannot be run twice is a step that cannot be resumed.
✅ What to Check¶
- Is each step's name a state that can be false?
- Can each action run twice without harm?
3. When Not to Write a Procedure¶
A procedure is a maintenance liability: it must be kept true, and a stale one is worse than none because it is trusted.
Do not write one for:
- a single edit — one file, one gate, no sequence to lose;
- exploratory work — the destination is unknown, so the steps would be fiction;
- a one-off migration — the procedure outlives its purpose and becomes archaeology.
Do write one when: the change repeats, it touches more than a handful of files, a gate can fail mid-way, or a human must approve something before it continues.
✅ What to Check¶
- Take your procedures. Which ran once and never again? Delete them.
- Which repeated change still has none? That is the next procedure.
4. The Play, the Engine and the Board¶
- The play is the file that states the contract: the steps, the gates, the approvals. It lives in the repository, reviewed like code.
- The engine is a mechanism: it runs the steps and records the outcome. Adopt it; never rebuild it.
- The board is a view: what is in flight, what is blocked. Adopt it; never let it become the only place state exists.
A play that lives in the engine's configuration is a play that cannot be reviewed, diffed or tested — and disappears when the engine changes. This is the same rule as the Map: the rule lives where it is reviewed.
Gates that cannot pass are reported blocked. A gate that fails for a reason outside the run's control is not a failed step; it is a step that cannot proceed. Saying "blocked" with the exact command and the reason is the honest outcome. The state is this chapter's; the report and the decision are the next chapter's.
A Procedure Is Not a Pipeline¶
This is the question every engineer asks at this point, because a play looks like a CI workflow and is not one.
| A CI workflow — Actions, GitLab, Jenkins | A procedure | |
|---|---|---|
| who runs it | a runner, on a trigger, with no one watching | an agent or a person, watched or not |
| what its steps are | commands | states that must become true, each with a check |
| approval | not in the pipeline's vocabulary, or bolted on as a manual job | a first-class field, at the step where it belongs |
| memory | none — each run starts clean | the run record, so a resume starts where the failure was |
| rollback | a second workflow nobody wrote | a field, tested before it is needed |
| when it is wrong | the pipeline is red | the step says which invariant is false |
The two compose rather than compete. A procedure's gate is often a CI workflow: step 2's gate may be the integration workflow on the branch is green. The workflow is a mechanism inside a step; the procedure is the contract over the whole change, including the part no runner can do — deciding, approving, rolling back.
The paths make it obvious. The play lives in plays/, the pipeline in .github/workflows/ or .gitlab-ci.yml — if a play ever needs to sit in the pipeline's own directory to work, the engine has become the contract.
What would go wrong if you used the pipeline as the procedure? The run would begin at the first job and end at the last, with no line for the human decision that matters, no record of which step failed first, and no rollback that anyone rehearsed.
✅ What to Check¶
- Is the play in the repository, or in the tool?
- Can a blocked step be distinguished from a failed one?
5. Build the Play¶
Artefacts¶
| Artefact | What it is for | Where it lives |
|---|---|---|
| play file | the procedure, as the agent's contract | plays/<name>.yaml — never a CI workflow directory |
| gate annotations | the check per step, with its command | inside the play |
| resume rule | where a resumed run continues | the play, or the Playbook |
| run record | one procedure run, with its gates | the execution log |
Decisions¶
| Choice | Default | What changes if you choose otherwise |
|---|---|---|
| where the play lives | the repository | tool configuration: unreviewable, unportable |
| step naming | invariants | activity names that cannot fail |
| approval points | before anything irreversible | a human asked at the end, when the choice is gone |
| blocked vs failed | separate states | one word for two situations, and a decision nobody makes |
✅ Checks¶
- Every step has a gate with a command; a step with no gate is a narration.
- A resumed run starts at the first step whose gate is not green.
- A blocked step names the command and the reason, and is never reported as passing.
Sequence¶
- Write the play for one repeated change. Checkpoint: each step names an invariant.
- Add a gate per step. Checkpoint: every gate can fail and name its fix.
- Mark the approval points. Checkpoint: no irreversible action runs before one.
- Run it once and record the gates. Checkpoint: the run record matches the play's steps.
Done When¶
- One repeated change has a play whose steps are invariants.
- A resumed run continues at the right step without re-reading the whole plan.
- A blocked step is reported as blocked, with the command that would unblock it.
6. 🔑 Chapter Summary¶
A procedure is how a sequence becomes repeatable — and how a failure becomes legible.
- Sequences break when they live in prose: one gate at the end names the symptom, not the step.
- A step is action, gate, approval, resume — and its name is a state that can be false.
- Not every change needs a play — single edits, exploration and one-offs cost more than they save.
- The play is the file that states the contract; the engine is a mechanism and the board a view, and only the play belongs in your repository.
- Blocked is a state, not a failure — and never a pass.