Everything so far has been a piece: a check the agent can run, rules the repo carries, hooks that enforce them, isolation so several agents can work at once, and a reviewer that did not write the code. Assembled, they stop being tips and start being a system.
Addy Osmani calls this loop engineering, and the phrase is right: the leverage moves from the quality of any single prompt to the shape of the loop around it. What follows is that idea with the two Claude Code commands that make it concrete, plus the piece that is easiest to skip and hurts most when you do.
The finish line: /goal
The most common failure in a long session is not a wrong answer, it is stopping too early. “I have made good progress, let me know if you would like me to continue” is not a bug in the model, it is what happens when nothing in the session defines done.
/goal takes a plain English condition and installs it as a stop gate for the session. Every time the agent tries to end its turn, that attempt is checked against the condition. If the work does not satisfy it, stopping is refused and the agent is told why, so it goes back. When the condition holds, the goal clears itself.
- 01 Work happens Claude finishes a piece of it.
- 02 It tries to stop Every attempt to end the turn passes through the gate.
- 03 The condition is checked Met? The goal clears itself and the turn ends.
- 04 Not met Stopping is refused, it is told why, and it goes back to work.
- ↑ back to the start
If you read the previous post, you already know what this is: a Stop hook with a condition attached. That is worth knowing, because it tells you exactly what kind of condition works. A gate can only be as good as what it can check.
/goal every test in packages/api passes and pnpm lint reports zero errors
/goal the /pricing page renders the three new tiers, has no console
errors at 375px and 1440px, and tsc is clean - Prefer an exit code. “
pnpm testexits zero” beats “the tests look fine”, for the same reason the first post in this series exists. - One outcome, not six. An omnibus goal becomes a long session where the first item was done an hour ago and quietly forgotten. Sequence small goals instead.
- Make satisfying it leave evidence. Output, a diff, a measurement. Something you can read afterwards.
- Know it can be unreachable. A condition depending on a flaky external service will never clear.
/goal cleardrops it; you will not need that on success, because a met goal clears itself.
The heartbeat: /loop
A goal says what done means. It says nothing about when to look again, and a lot of real work is exactly that: a deploy that takes nine minutes, a CI matrix, a queue draining, a nightly job you want to catch.
/loop re-runs a prompt or a slash command on a schedule. Give it an interval and it uses that. Leave the interval out and the agent paces itself, choosing each delay and having to state a reason for it.
- A fixed heartbeat: every five minutes, regardless.
- Right for something that changes on a schedule of its own.
- A five minute poll on an eight minute job spends most of its wakeups learning that it is still running.
- The agent picks each delay, and has to say why.
- Right when the correct wait depends on what the last check found.
- A build it just watched start earns an eight minute wait on purpose.
The self paced form is the more interesting one, because a fixed interval encodes an assumption about the world that is usually wrong. An agent that just watched a build start can decide to wait eight minutes on purpose, and being made to justify the number keeps the pacing honest.
| Interval | Wakeups in 8h | What it is for |
|---|---|---|
| 1m | 480 | almost nothing. this is a bug |
| 5m | 96 | a deploy you are watching right now |
| 20m | 24 | a long CI matrix, a slow queue |
| 1h | 8 | a background sweep |
The failure to avoid: do not poll something that would have told you anyway. If a background command already wakes the session when it exits, a one minute loop layered on top is pure waste. Loops are for state the session has no other way to hear about.
State, because context is not memory
This is the piece that separates a loop that runs overnight from one that runs for twenty minutes and then starts repeating itself. A session’s context does not survive forever. It gets compacted, cleared, or simply ends. Anything the loop needs to know next time has to be written down somewhere that is not the conversation.
A plain markdown file is enough, and being plain is a feature: you can read it, edit it, and commit it.
# loop state, updated at the end of every pass
## done
- 2026-08-27 flaky `order.spec.ts`: retried timer, PR #412, merged
## in progress
- 2026-08-28 `checkout/total.ts` rounding: worktree `wt/fix-rounding`
tests still red: 2 of 14. Do not open a PR yet.
## blocked, needs a human
- staging DB credentials expired; the integration suite cannot run Tell the loop to read it first and write it last, and the whole thing becomes resumable. It also becomes auditable, which matters more than it sounds: when you come back in the morning, the file is the log of what a machine decided while you were asleep.
One loop, assembled
- 01 State A file on disk, read first. It survives every context reset, which the conversation does not.
- 02 Discover What broke since yesterday? CI, issues, the commits that landed.
- 03 Isolate One worktree per item, so nothing collides.
- 04 Make The maker agent changes it.
- 05 Check The checker, plus the commands from the first post in this series.
- 06 Land A pull request, for a human. Then the state file is written back.
- ↑ back to the start
Read it top to bottom, then round again: state tells it where things stood, discovery finds what is broken, each item gets its own worktree, a maker changes it, a checker and the project’s own commands decide whether it is real, and what survives becomes a pull request with a human at the end of it. Then the state file is written back, so the next pass can be picked up by a session that remembers none of this.
Guardrails, and they are not optional now
- Branch or worktree. Never your main checkout. Unattended work on the tree you are reading is a bad trade for any amount of convenience.
- Machine checkable conditions only. A goal that can only be judged by reading prose will be judged generously, at three in the morning, by something that wants to finish.
- Keep the permission leash on. Unattended is the worst possible moment to turn prompts off. If a run genuinely needs none, put it somewhere disposable.
- Cap the spend before you start. Turns are not free, and a tight interval is the most common way to discover that.
- A human lands it. The loop can open the pull request. It should not be the thing that merges it.
What the loop still will not do
Two costs are worth naming, because the tooling will not surface them for you.
The first is that verification stays yours. The checks make the loop possible, but a green suite means the tests passed, not that the change was right, and it is your name on the deploy. Automating the work does not automate the accountability.
The second is quieter and compounds: comprehension debt. Code you did not write and did not read still becomes code you own. Shipping faster than you understand feels like leverage for a few weeks and then feels like maintaining a codebase somebody else left you. The loop is worth building precisely so you spend your attention on the parts that need it. That only works if you actually spend it there.
Build the loop. Keep reading the diffs.