Agentic workflows: from runbook to command

Your delivery process is seven correct steps in an onboarding document, and everyone follows it roughly. Roughly is where the incidents come from, and an agent following it roughly is worse.

Your delivery process is a numbered list in an onboarding document. Seven steps, all correct, written by somebody thorough. Everybody follows it, roughly, and the roughly is where the last two incidents came from.

Ask three people to walk you through it and you get three walkthroughs. Nobody disagrees with the list. A list you read is a list you rebuild from memory the next time, and what gets dropped in the rebuild is always the step whose purpose is least obvious from its name. Hand the same list to an agent and it does the same thing, faster, on every ticket.

The same task run from memory and run by name Two groups of three runs through a five step task. In the runs done from memory, different steps are missing each time and the sequence differs between runs. In the runs done by name, all five steps are present in the same order in every run. run it from memory run it by name three runs, three sequences three runs, one sequence
Nobody skips a step on purpose. They skip it because the step before it looked fine, and because the sequence lives in a document rather than in something you can invoke. Illustrative — the shape of the argument, not measured data.

A document is read, a command is run

The fix is not a better-written list. A better list is still read once and reconstructed forever after, and the reconstruction is where the variance lives. What changes the outcome is making the sequence something you invoke instead of something you recall.

a document a command
tells you the order is the order
is followed roughly runs exactly
goes stale in silence breaks loudly when a step it names disappears
gets better when someone rewrites it gets better when someone fixes a step

The last row is the one that compounds. A process living in prose improves only when somebody sits down to improve the prose, which is nobody’s job. A process living in a file next to the code gets fixed by whoever just tripped over it.

The whole delivery loop, behind one word

Here is a real one, from deskspace. The entire prompt for a feature is an issue number:

/flow gh#42

Everything else is already in the repository. The issue carries the requirements and the test scenarios. CLAUDE.md carries the architecture and the conventions. The gates carry the definition of done. What the command file adds is the order, and the order is not negotiable:

1. Brief       issue_context.py validates and extracts the ticket
                 exit 1 -> not implementable, stop, do not repair the ticket
                 exit 2 -> tier 3, plan only, wait for a human
2. Plan        requirement -> scenario -> test table, blast radius, arch check
3. Implement   tech-lead subagent, test-first, enforced by a hook
4. Review      reviewer subagent, read-only, requirements against tests only
5. Cycle       bounded at 3 rounds, then escalate
6. Gate        ./mvnw verify, then ./mvnw -Pmutation verify
7. PR          pr.py create, because gh pr create is denied

Step one is the interesting one. Before a single token is spent on the work, a script decides whether the ticket can be worked at all: no context, no numbered requirements, fewer scenarios than requirements, no risk tier, and it exits non-zero. The agent is instructed not to fill the gaps by guessing, which is what it would otherwise do beautifully and invisibly.

What the file deliberately leaves out

Notice what is not in the sequence. Nothing about how to design the validator. Nothing about which package the change belongs in. Nothing about whether a requirement and its scenario actually describe the same thing, which is the one question in the whole loop that no script can answer and the one the plan step exists for.

That is the split, and it is the test of whether the line was drawn in the right place. The order is fixed because ordering is decidable. The semantics of a ticket are not, so they stay with a person or with the model, and the workflow says so out loud rather than pretending a checklist covers it.

Choosing a tool that can refuse removes one way to be wrong about the input. Naming a sequence does the same for the order. Both leave the interesting part untouched, and if encoding the sequence also encoded an opinion, the opinion did not belong in it.

What it replaces

Without the command, the same ticket starts with an agent reading the issue, inferring what “done” means, globbing the source tree to work out where Pet lives, and choosing its own order. It usually gets there. The variance is in which steps it skipped and whether anybody noticed.

# before: the sequence is in somebody's head, so it is rebuilt per ticket
read the issue, guess the missing requirement, find the package,
write the code, write a test afterwards if the loop ends early enough,
open a PR whose description omits two of the four scenarios

# after: the sequence is a file, and the file is the prompt
/work gh#42

The saving is not the typing. What changed is that the set of steps which definitely ran is the same set on every ticket, and the PR that comes out is refused by a tool if it omits a scenario, rather than merged because nobody cross-references four Gherkin blocks against a markdown table at six in the evening.

Only name a sequence you have already run

A command freezes an order, and freezing the wrong order is worse than having none: it is a guess that now looks official. The test is boredom. When you have run the same steps enough times that the order is dull, that dullness is the signal it is ready to be written down and stopped being thought about.

Until then it is still a decision, and decisions belong to people.


If your delivery process lives in an onboarding document that everybody follows roughly, that’s the work I do.


Working through this in your own team?

I help engineering teams adopt AI coding assistants without giving up engineering rigour, and design the architecture underneath. If that's on your plate, let's talk.