NanookNanook
  • Docs
  • API
  • Blog
  • Articles
  • GitHub

›Recent Posts

Recent Posts

  • 2026-11 Rules Need Exit Codes
  • 2026-11 The Agent That Pleases
  • 2026-11 Working Through 1,200 Plans
  • 2026-10 Tests First, Then Implement
  • 2026-10 A Table Cannot Stay Silent
  • 2026-10 What the Next Session Knows
  • 2026-10 One Root, Many Repos
  • 2026-09 Fewer Skills, Shorter Rules
  • 2026-09 Agentic Development
  • 2026-08 E-Invoices: 40 Dialects
  • 2026-08 One Login, Eight Cases
  • 2026-08 Testing a SaaS
  • 2026-03 AI-Assisted Tables
  • 2026-03 Manual vs. Automated
  • 2026-03 Equivalence Class Testing
  • 2026-02 Nanook Is Back
  • 2019-06 Introducing Nanook

Working Through 1,200 Plans: A Pipeline, a Ledger, and Many Repositories

November 3, 2026

Torsten Link

Part 7 of 9 in the series on agentic software development. Part 1, The Agent Writes the Code has the two settings, the numbers, and the ten sentences the series works through.

The SaaS platform in this series spans 74 repositories and, at the last count, about 1,240 completed plans. At that volume, working through plans one at a time with a person in the loop for every step stops being a process and becomes the bottleneck. So an autonomous pipeline works through them on its own: a Claude Code skill called plan-pipeline, and a loop command that keeps it going. Most of what makes that work is bookkeeping, and most of the bookkeeping exists because a run died at the wrong moment at least once.

Audit, implement, release, move

For a given REQUIREMENTS/<PRODUCT> folder, the pipeline does four things in order.

  1. Audit every open plan against the code. What is already there, and what is really still open? Plans routinely turn out to be partially done, or claim to be done and are not. The audit is the step that turns a folder of intentions into a list of work; skipping it means implementing what already exists.
  2. Implement the open increments, one sub-agent per step: implement, build, test, commit.
  3. Release: push, wait for CI, cascade versions into the dependent repositories.
  4. Move finished plans to done/, and extract the blocked remainders so that a plan that is nine tenths done does not sit in the open folder looking untouched.

Nothing in that list is unusual. What lets it run unattended is what the pipeline remembers between runs.

The ledger: the pipeline’s memory

The heart of the pipeline is a ledger file: a Markdown table with one row per plan and a fixed status vocabulary: open, verified-open, in-work, blocked, extern (another session owns it; do not touch), and done. The vocabulary is small on purpose. A status that needs a sentence to explain is a status the next run will misread. extern is the one that makes parallel sessions possible at all: a plan another session owns is not audited, not implemented and not moved by this one. It is the ledger’s one concession to not being the only writer in the tree.

Every status change is written to disk immediately. A run that dies, whether from a server error, a killed process or an exhausted context, loses at most the current increment. The next run reads the ledger and resumes where the last one stopped. Immediate writes sound obvious and are not what an agent does by default; left to itself, it keeps the state in its context and writes a summary at the end, which is precisely the moment a dying run never reaches. Two refinements came from real crashes.

  • Write-ahead. A ledger that is written after the sub-agent reports is empty exactly where it hurts: the row for the increment that was in progress when the run died. So before every agent spawn and before every push, a row goes into an IN FLIGHT table at the top of the file. A non-empty IN FLIGHT table at startup always means a turn died here. The next run does not trust the row; it checks Git, which is the truth, and reconciles the ledger against it.
  • Size measured in bytes. Resume reads only the head of the ledger, head -c 40000, about 10k tokens. When the file passes 150 KB, the old part is archived. A single row may not exceed 800 characters; detailed findings belong in the agent report, not in the ledger. The limits are in bytes rather than rows because bytes are what a resume costs: a ledger that grows without a bound eventually eats the context it was meant to protect.

Which model does what, and when not to delegate

Not every job needs the most expensive model, and not every job tolerates a cheap one. The assignment in the pipeline:

RoleModelWhy
Orchestratorstrong modeljudgement: audits, blocked/done decisions, what to believe
Implementation, browser QA, code reviewstrong modelreal implementation scope, hundreds of tool calls
Dependency cascades, scaffolding, drift checksfaster modeldiligent, well-specified routine work

The orchestrator is on the strong model not because it does the most work but because it does the deciding: which audit finding to believe, whether a plan is blocked or done, what the sub-agent’s summary leaves out. The routine jobs go to the faster model because they are fully specified; a version bump has no judgement in it. Browser QA sits on the strong model for the same reason as implementation: hundreds of tool calls, and a page that does not do what the plan says needs someone who notices.

And a delegation threshold: up to about five Git commands, a build that takes seconds, or a one-line fix, the orchestrator does the work itself. Spawning an agent has overhead of its own kind. The sub-agent knows only what is in its brief, and the orchestrator sees only its summary; for small work, that loss of context on both sides costs more than the tokens it saves.

A decision is the go

One simple rule removed a surprising amount of friction: when a decision has been made, that is also the go for implementation. Before the rule, the pipeline had parked eleven plans as “decision made, go missing”. Each of them had been discussed and settled; none was moving. From the outside it looked as if nothing was happening. From the pipeline’s side everything was in order: it was waiting for a step that nobody knew was a step. A process that asks for the same thing twice under two names will get it once.

One change, many repositories

The e-invoicing API, the trades software and the PDF API share the same plugins and backends. A change to a shared backend package or a frontend plugin therefore has to be checked against all apps, and the same bug in several apps is fixed in all of them at the same time. That rule stands in the instruction file, marked critical, because an agent that fixes a bug in the app it happens to be looking at considers the job done. It is not wrong about the app; it is wrong about the scope.

Then the change has to travel: the shared package is released, the next layer bumps and releases, the apps bump and release. With 74 repositories the order matters, and it is a topological one. A command, /update-cascade, computes that order and drives the bumps, with up to nine sub-agents in parallel on a fast model and a pin file that holds versions back where a bump is deliberately not wanted yet.

Between commit and release stands the push gate.

  • Before every push, the full test chain runs in the affected repository: format, lint, type check, dead code, build, tests with coverage. A global pre-push hook enforces it, not a sentence in the instruction file; the hook is global, so no repository opts out by lacking one.
  • After the push, the CI run counts, not the push. The pipeline watches the run until it reports completed/success, and sweeps all touched repositories at the end of a run, because a push that nobody watched is a push whose result nobody knows.
  • A green CI run without a release publishes nothing. semantic-release silently skips the release when the branch is behind the remote, for example because the next commit was pushed while the CI job was still running. Green, and nothing shipped, and no error to say so. So backend waves are bundled and pushed once.

Why so strict? Because a rule in prose was not enough. The hooks that were bypassed and the red CI runs nobody noticed are the subject of part 9.

Parallel sessions in one checkout

Running several agent sessions in parallel is where the throughput comes from. It is also where the nastiest incidents came from, because Git and everything built on it assume one person per working tree. Five of them:

  • A push that would have published someone else’s unfinished work. While one session worked through plans in two repositories, a second session worked on a tax-authority integration in the same repositories. At the pre-check both were clean. Twenty minutes later there were five unpushed foreign commits and twelve modified files. A push by the first session’s implementer would have published those commits and triggered a semantic-release of unfinished work, and a published npm package cannot be rolled back.
  • git add -A grabs everything. There is no such thing as “my lane’s local commits” in a shared checkout: one working tree, one history, one branch. A commit contained only its own six files only because the other session had committed its work 18 minutes earlier. A push took the other session’s finished commit along without its release. And another session saw an identical commit message in the log, took the commit for its own, and derived a coordination violation that never happened.
  • git stash pop restored a stash from a different session. The stash belongs to the working tree, not to whoever made it.
  • Two test suites on the same repository, two pushes and two pre-push hooks, competed for resources and produced shifting red results that were neither session’s fault.
  • Agents that go silent. A sub-agent on a fast model starts the test suite in the background, ends its turn with “I’ll wait for the background task”, and never wakes up. The work lies finished in the working tree; no error, no report. An explicit prohibition in the brief did not help once the step took longer than the tool timeout. The prohibition was in the brief, the timeout was in the tool, and the tool won.

What helped, in six rules:

  1. Implementation agents in shared repositories get a push ban. Committing is fine, with explicit paths only: never git add -A, never git add <directory>. The orchestrator coordinates the push.
  2. Re-check the working tree before every increment, not once at the start of the run. Twenty minutes is enough for the state to change.
  3. Long-running or delicate work goes into its own git worktree. Two sessions in two trees have nothing to grab from each other.
  4. Foreign red tests are not your regression. Do not fix them; separate them in the report.
  5. Liveness is measured on the process, not on the last message. If no test runner and no push is running and the tree contains the work, the agent is dead, and the orchestrator finishes inline.
  6. At most three sub-agents at a time across all types, for cost and for contention.

The rules this part comes down to: the ledger is written before the work, not after it, and when ledger and Git disagree, Git is the truth. A decision is the go. A change to something shared is a change to every app that uses it, and along the way the CI run counts rather than the push, and the release counts rather than the CI run. And in a shared checkout, the implementing agent commits by explicit path and never pushes; whether it is still alive is a question for the process list, not for its last message.

Previous: Part 6, Tests First, Then Implement All Plans. Next: Part 8, The Agent That Pleases.

Recent Posts
Nanook
Docs Tutorials Guide
More About Articles Imprint Privacy Policy GitHub Manage Cookies
© 2018-2026 nanook.xhub.io — An Open Source Project by BeeBack UG.
Cookie preferences

We use cookies to analyze site usage and improve your experience. You can choose which cookies to allow below. See our Privacy Policy for details.

EssentialAlways active
Analytics