$ cat articles/ai-harness.md▊

The harness: running AI agents on a production Angular codebase

Six weeks, 858 agent runs, about 125 million tokens, 28 run reports with a per-agent ledger in each. This is how the Claude Code harness behind the Nx migration is built: rules, skills, eight agent roles, four hooks, one verification script, and the habit of writing down every ruling with what it costs if wrong. What it stands on, what it measures, what the audit found when it turned the measurements on itself, and what I would not build again.

Andrii Siuta · October 2026 · ~25 min read Claude Code superpowers Matt Pocock skills Angular Nx
8 rolessubagents, model and effort per role
858 runsagent dispatches in six weeks
≈ 125 Msubagent tokens, ledgered
28run reports with rulings and a ledger
4 hooksdeterministic guards, exit 2 blocks
11.5k → 3kwords loaded every turn, after the audit

01. What a harness is

A chat window with a good model in it will write you a component. It will not move 1,600 files across 42 libraries while four colleagues keep merging, and it will not tell you afterwards what that cost or which of its own suggestions were wrong. The difference is not the model. It is everything around the model that decides what it knows, what it may do, what has to be true before it is allowed to say "done", and what gets written down.

I call that a harness. Mine has seven layers, and the article walks through them in order:

  • Rules: instruction files loaded every turn, or when a path is touched. Invariants only, present tense, one home per fact.
  • Skills: knowledge loaded on demand by a trigger. Some I wrote, most I did not (section 2).
  • Agents: eight roles, each with its own model, effort, tool list, preloaded skills and a report contract.
  • Hooks: deterministic scripts on tool events. They block; they do not advise.
  • Tools: a verification script that runs every gate in one command, a ledger summariser, a dead-path checker.
  • Record: specs, plans, ADRs, run reports with token ledgers, an append-only progress file per run, a handover document when a session has to stop.
  • Memory: a small file-per-fact store for rulings, machine facts and diagnostic recipes. Never for ticket state.
Human Dispatcher session Subagents · 8 roles Knowledge Gates Record Third-party layer rulings · verification opens the PR brainstorm · dispatch rulings · run report investigator · spec-writer planner · 2 implementers 2 reviewers · pr-creator rules · skills · memory ADRs · specs · plans hooks on calls and stops verify: 8 steps, exit 1 run report + token ledger progress.md · handover superpowers · Matt Pocock skills · Angular team skill · Nx plugin · typescript-lsp · read-only MCP brief task brief report every call · every stop reports · ledger rows loaded into every session skills on demand · servers by name
The human never talks to a subagent; the dispatcher session does, with a brief it assembles from the plan. Gates sit on every tool call and every agent stop. The record is written as work happens, not afterwards.

02. The layer I did not write

Most of the process knowledge in this harness is other people's, installed as Claude Code plugins and switched on or off per skill. The repository-specific layer on top is thin on purpose: it says where this repo departs from the generic skill, and nothing the generic skill already says.

superpowers

Jesse Vincent's superpowers plugin is the spine of the workflow. brainstorming runs before any design work and refuses to start implementation until a design is approved. writing-plans is what the planner agent invokes, with two repo overrides (specs are written alongside, and tasks carry this repo's tags). subagent-driven-development is the dispatch loop, and its task-brief script is how a brief is assembled: the plan's header block plus one task's section, never a path to the plan. test-driven-development, systematic-debugging, verification-before-completion ("evidence before assertions") and using-git-worktrees are used as they ship. The rule that a skill is invoked before any response, even a clarifying question, is also theirs, and it is what makes the rest of the stack fire reliably.

Matt Pocock's skills

Matt Pocock's skill set covers the parts of engineering that happen before and around code, and four of them shaped how this harness keeps its record:

  • domain-modeling gives the repo its document layout: a single-context CONTEXT.md (here still carried by the rules file until it exists) and sequentially numbered ADRs under docs/adr/. The eleven ADRs in this harness follow its shape, and the rule "if a piece of work would contradict an ADR, say so and ask, never override silently" is its discipline.
  • research is the pattern behind the 68 research notes: a background agent investigates a question against primary sources, every claim carries its source, and the note lands where the repo already keeps such notes. The Nx improvements survey, the architecture review against Manfred Steyer's published material, and the AI-tooling survey were all written this way.
  • writing-for-agents is the reference I reach for whenever a rule file or an agent prompt is edited. Its vocabulary (context load versus cognitive load, a pointer per branch, progressive disclosure, "cut identity the body already carries") is the vocabulary the audit in section 8 used to cut the always-loaded layer from 11,500 words to under 3,000.
  • grilling (and grill-with-docs) is the design-tree interview: rounds of questions, each with a recommended answer, until nothing is silently assumed. It is how the bigger specs got their open-decisions list down to three or fewer before the spec-writer ran.

prototype, codebase-design (deep modules, where a seam goes), resolving-merge-conflicts, wizard and handoff are on and used when they fit. Three of his skills are deliberately switched off: tdd and diagnosing-bugs overlap superpowers' versions, and his code-review overlaps the official plugin's; two skills with the same trigger is one skill firing at random.

Framework and tooling skills

  • The Angular team's angular-developer skill (forty reference files: signals, resource, linkedSignal, signal forms) is preloaded into the implementer agent, so the generic Angular guidance costs nothing per task to look up.
  • The Nx plugin's nx-workspace and nx-generate skills replaced a hand-written library census: the list of projects by tag is one nx show projects away and is never restated in a document. Nx's own stance, "skills for knowledge, MCP for connectivity", is the rule here too.
  • Two vendored MIT skills for accessibility and performance (WCAG 2.2 AA, measure-first) are used for design-system work.
  • The official code-review plugin runs once per branch; typescript-lsp gives diagnostics after every edit and real go-to-definition across 43 projects instead of grep; Context7 answers library-version questions.
  • MCP servers, all read-only in effect: the Angular CLI server (docs search, the OnPush migration tool), Azure DevOps (work items, repositories, PR threads; its six write tools are denied so the shell's "no pipeline queueing" rule cannot be bypassed through a different door), Figma, SonarQube.

The repo layer says where this codebase departs from the generic skill. It never repeats what the generic skill already says.

03. Rules, skills and eight roles

Rules

Four files are loaded every turn: the global preferences (138 words), the tracked persona the team wrote (324), the local rules file (2,170 words, ten numbered rules and a table of what each unmerged branch adds), and a shell-rules file (319) that carries the git and process invariants. A fifth, the web rules (945), loads only when a file under apps/ or libs/ is read or edited. Every one of them is present tense and describes the checkout, not a branch. That sentence sounds obvious; section 8 is about how it stopped being true.

Repo skills

Five skills are repo-authored: web-conventions (where things live, the rules this repo adds, where it departs from the generic skills, a done-when list; 4,200 words plus eleven reference files), typescript-style (the recipe table: union states for loaded data, no loose null checks, as const satisfies for configs, pure sort and search helpers), ado-conventions (how to read and write the issue tracker, commit and PR style), writing-feature-spec (two templates and a 1,500-word cap) and syncing-docs (a diff-to-document mapping, run before every PR). Each has a trigger sentence that names the situation, because a skill that does not fire is a document nobody reads.

Eight roles

AgentEffortToolsPreloadedMust return
investigatorhighread, grep, glob, git reads, spawninvestigating-codebase, tracker conventionsa one-page facts note: files, flow, constraints; no code, no offer to implement
spec-writermediumread, write under docs/ onlywriting-feature-spec, diagrams, ADRsspec path, specs amended, open decisions (more than three means the brainstorm is not finished)
plannerhighread, write the plan file onlyweb-conventions, typescript-style, angular-architectureplan path and a task list with tags; at most 300 words per task, 12 tasks per plan
implementerinheritsallangular-developer, web-conventions, typescript-styleFiles: Checks: Deferred: Plan edits: or Blocked:, hook-enforced
mechanical implementerinheritsallweb-conventions, typescript-style (no generic Angular preload)the same contract; for tasks whose brief carries the complete code or an exact copy rule
conventions reviewer—read-onlyweb-conventions, typescript-stylea fixed 16-row checklist and a verdict; the mechanical rows are n/a because the verification script owns them
code reviewer—read-onlynone, deliberatelyan 11-row "second file" table: the translation twin, the flag's getter, the action's handler, the route's title key; verdict paired / one file missing / request changes
pr-creator—read, git, tracker CLItracker conventionsthe PR URL and title, after a five-step preflight; it runs only when told a human verified the branch

Three things in that table do most of the work. Model and effort live in the agent's frontmatter and nowhere else; after the audit the three document-writing roles moved off the most expensive model, and the spec-writer dropped to medium effort because, in its own prompt's words, "you write down decisions that are already made; you do not design". The code reviewer preloads nothing, because loading the conventions skill would pull it onto the other reviewer's checklist and the two would find the same things twice. Every report is a contract: the implementer's four headings are checked by a hook on the agent's stop event, and a report missing Deferred: is sent back. Manual checks used to be deferred silently; now the dispatcher sees the word every time.

an agent's frontmatter, the shape---
name: spec-writer
description: Turns a read ticket and an agreed approach into a spec under docs/, and reports the path plus the open decisions.
model: opus
effort: medium
tools: Read, Grep, Glob, Bash, Write, Edit
skills: writing-feature-spec, drawing-architecture-diagrams, writing-adr
---

04. The workflow: tiers and dispatch

challenge

For the first five weeks there was one pipeline for every size of change. A three-agent fix cost 246k tokens because it still went through investigator, spec, plan, two reviewers and a docs sync. A lint-config change cost 550k. The pipeline floor was the price of any ticket.

how we solved it

Three tiers, sized at the brainstorm and written into the rules:

  • S: at most three files, no new copy, no contract change. The main session or one implementer, the official code review, a human verifies, the PR is opened. No spec, no plan; the run is one line in a monthly file.
  • M: one screen or one contract, no new library. The ticket plus the brainstorm decisions in the brief stand in for the spec; the planner writes at most five tasks; one reviewer per new shape.
  • L: a new library, a cross-scope change, a wave of components. Investigator → brainstorm → spec-writer → planner → implementers → reviews → docs sync → a human verifies by hand → pr-creator. Every migration phase in the first article was an L.

Dispatch

The dispatcher is the main session. It never implements an L task itself; it assembles briefs, reads reports, makes rulings and writes them down. The rules it follows:

  • The brief is the plan's header block plus the task's section. The header carries the verification command, the commit recipe, the message rule and the report format once; a task never repeats them. An agent is never handed a path to the plan, because it would read all of it.
  • Same-shape tasks are one dispatch with a per-unit table and one commit per unit. An implementer run costs about 70k tokens before it touches a file, so ten five-minute renames as ten dispatches is 700k tokens of start-up.
  • Parallel only when nothing is shared. Two tasks run side by side only if they share no file and no measured number; the two translation files count as shared. A shared file no check guards is committed once by the dispatcher after the wave.
  • One working tree, pathspec commits. Implementers share a checkout and commit with git commit --only -- <paths>, never a bare commit, so two agents' half-edits never land in each other's commits.
  • An implementer that changes a shape a later task depends on edits that task in the plan, and says so under Plan edits:.
  • Manual checks batch into one click-through list at the end of the branch, done by a human.
  • Ask before adding a dependency, changing the lint config or the test runner, restructuring a domain folder, or queueing any pipeline. A queued build is outward-facing and is not the agent's call.
  • A PR opens only after a human has verified the change. The pr-creator's preflight checks that it is not on main, the tree is clean, commits exist, no local-only file is in the range and no PR is already open. It once refused a generalised "go on" because the verification step had not been named.

05. Gates, not prose

A rule an agent might forget is a rule a script should hold. The harness has two kinds of script: a verification command every implementer runs before it may report, and hooks the platform runs on tool events whether anyone remembers or not.

One command, eight steps

#StepCompared againstFails when
1Type-check the appzeroany error
2Type-check the specs, plus every library's own tsconfiga baseline file keyed on file, code and message (line numbers ignored)a new error; the 53 pre-existing ones are not printed
3strictNullChecks on the appa list of folders that are already cleanan error inside a listed folder; a folder at zero joins the list and never leaves it
4Jest, related tests only—a red test in anything that imports a touched file
5Module boundaries lintthe tag tablean upward or cross-layer import, a cycle
6ESLint parity per touched filethe base branch's version of the same file, linted through stdinany rule whose count grew; a new file must be clean. Lint is parity, not zero: main carries about 280 errors
7Conventions (70 ms, zero tokens)the base brancha translation key missing in one language, an added comment, an added eslint-disable, a new component without OnPush or the prefix, AI attribution in a commit message, a stale reference to a moved file, a local-only file in the commit range
8Production build (on demand)—only when a config, route or style entry file changed; otherwise once per branch

Two of those rows are ratchets, and ratchets are the most useful shape of gate I know. The spec type-check baseline means an agent sees only the errors it introduced, not the 104 lines of pre-existing noise that, before the baseline existed, every agent read in full on every run. The strict-null folder list means strictness only ever grows: 1,218 errors in 233 files on the day the ratchet started, eight folders clean today, none of them ever allowed to regress.

challenge

Row 7 used to be rows in a reviewer's checklist. Translation-key parity, added comments, a trailer in the commit message: an agent reading a diff for them is slow, costs tokens and misses some. The conventions reviewer spent 434k tokens on one run and changed nothing.

how we solved it

Every mechanical row moved into the verification script, where it costs 70 milliseconds and cannot be skipped. The reviewer's checklist now marks those rows n/a and keeps only the judgement rows: placement, the signals contract, TypeScript style.

Four hooks

hook wiring, the shape{
  "hooks": {
    "PreToolUse":  [{ "matcher": "Bash",                        "hooks": ["git-guard.mjs"] }],
    "PostToolUse": [{ "matcher": "Edit|Write",                  "hooks": ["prettier.sh"] }],
    "SubagentStop": [
      { "matcher": "web-implementer(-mechanical)?", "hooks": ["implementer-report.mjs"] },
      { "matcher": "spec-writer",                   "hooks": ["spec-cap.mjs"] }
    ]
  }
}
  • git-guard, before any shell command: if the command commits or pushes, resolve the directory it runs in (a leading cd or git -C counts), read the branch, and block a commit or push on main, a commit that names a local-only harness path, or a push whose range carries one. Exit 2, with the reason. This replaced four paragraphs of prose that four files repeated.
  • prettier, after every edit or write, skipping the docs and harness folders. Formatting is never a review finding.
  • implementer-report, when an implementer stops: the last message must carry the four headings, or the agent is sent back to finish.
  • spec-cap, when the spec-writer stops: today's spec files are word-counted, tables included, and 1,500 is the limit. A prose cap no hook checked is what produced a 16,800-word spec.

The deny list

Twenty-one denied commands, each with an incident behind it. pkill -f and pgrep -f, because a pattern like ng serve matches the shell running the command and kills the tool call; that happened 181 times in one week before the rule. git reset --hard, because one moved the wrong branch in a checkout shared with other sessions; every commit, push or reset now runs git branch --show-current in the same command. Stash pop, drop and clear, because the stashes hold uncommitted local tooling and the indices shift. rm -rf. Any command that queues a pipeline. And the six write tools of the issue-tracker MCP server, because a shell deny list does not cover MCP tool names, and a rule with a second door is not a rule.

06. Review economics

Every run report ends with a ledger, and the ledger has two columns most harnesses do not keep: findings that changed code and findings later refuted. The first report said why: "the ratio of code-changing findings per 100k tokens, per agent type, is the number to steer the review budget by." It took five weeks to act on it.

ReviewerRunsTokensCode-changing findings / 100k
Conventions reviewer (per task)12412.0 M1.29
Code reviewer (per task)12510.3 M0.89
Generic task reviewers (per task)232.4 M0.00 – 0.48
Official code review (once per branch)193.1 M3.4 – 3.9

The two custom reviewers cost 22.3 million tokens for 247 code-changing findings. The one review that reads the whole branch at the end, scoped to the files that carry logic, finds three to four times as much per token. In the migration's CI story the gap was 0.31 against 4.76.

A reviewer under 1.0 code-changing findings per 100k tokens over two consecutive runs leaves the per-task dispatch.

That is now a rule, and the tiering follows from it. A task that introduces a new shape gets the conventions reviewer (judgement rows only) and the code reviewer. Repeats of a shape get one batched pass per wave. The whole-branch review runs once. The mechanical rows went into the verification script (section 5). What the ledger also shows is where reviewers are worth it: the "second file" reviewer, which looks only for the file a diff forgot (the other language's translation key, the feature flag's getter, the action's handler), finds real omissions on new shapes and nothing on repeats, which is exactly what you would want it to do.

And reviewers are sometimes wrong with confidence. Two of them agreed with each other that two lazy-load sites of the same feature share one injector; a throwaway spec of under a thousand tokens refuted both. Eight findings of one whole-branch review on the shared rework were refuted on inspection. The ledger's second column exists so that a reviewer's finding is treated as a claim to verify, not a change to apply.

07. Run reports, ledgers, handovers

Every M or L run ends with a report under the research folder. By convention it carries:

  • Outcome: what merged, the commits, the acceptance criteria and how each was proven.
  • Rulings, in order, each with what it costs if wrong. "Tasks 1–3 run in parallel in the one shared tree; costs a transient verify failure on another task's half-edit, re-run." The cost clause is cheap to write and is what made two architecture rules easy to reverse later: the price of the original rule had been written down when it was made.
  • Departures from the spec, named, so the spec can be amended by appending rather than rewritten.
  • Deferred: the click-through list for a human.
  • Tool-shell facts for the next run: that the Nx daemon's graph goes stale after a rename, that lint-staged after a pathspec commit leaves the index dirty, that the coverage folder is root-owned when the container writes it.
  • The token ledger: one row per agent run with model, task, tokens, tool uses, wall time, findings that changed code, findings refuted. A 48-line script totals it by agent type and prints findings per 100k.

The dispatcher's own context is never in the ledger, and a resumed agent reports a cumulative total, so raw sums overstate. One run's raw 14.2 million tokens was 9.2 million after de-duplication; the 125 million in the headline is the raw figure, and the reports say so.

Resuming

challenge

A dispatching session hit its context limit in the middle of a multi-lane upgrade. Claude Code's automatic compaction at 94 % produced a summary, and the summary had lost the agent ids, the exact rulings and which commits already existed. The next session re-dispatched a task whose commit was already on the branch.

how we solved it

Two artefacts and one rule. Each run has an append-only progress.md: the plan, the spec, the branch and base commit, a pre-flight table of what each task produces and consumes, then one line per task (DONE, DONE_WITH_CONCERNS, BLOCKED, with commit hashes and verify counts) interleaved with rulings as they are made. A handover document, written when a session must stop, carries the git state per lane, the agents that were running, a numbered resume procedure and the line "never re-dispatch a task whose commit already exists". And the rule: stop at 80 % of context and start a new session from the ledger, never let compaction decide what to keep.

08. The audit: what six weeks of sediment looks like

With 28 run reports behind it, the harness was audited the way it audits code: the always-loaded files read in full, three subagents sweeping for staleness, duplication and contradiction, and the ledger script run over every report. The verdict was fair: unusually well instrumented, and three things had grown past the point where they cost more than they returned.

A. The rules described a branch, not the checkout

challenge

The harness lives in the main checkout, and the work lived on a stack of unmerged branches. The rules file had been kept current for the furthest branch. A session starting on main was briefed for a different codebase: 17 of 91 cited paths did not exist, and five version facts were wrong.

how we solved it

The always-loaded files describe main only. One table at the top carries the stack: branch, what it adds on top of the row above, where that is written down. When a branch merges, its row is deleted and the rules it introduced are written in the present tense. A dead-path checker runs over every backticked path in the harness and fails on a missing one.

B. Fifteen facts in six to twelve places, thirteen of them disagreeing

challenge

The library count appeared as 35, 37, 41, 42, 43, 44 and 46 in different files. There were three different commit syntaxes across three agent prompts. One agent prompt said "nothing in this repo enforces layer direction" a week after the boundaries gate went into CI. Every rule change had become a six-file edit that missed one, and an agent picked whichever copy it read last.

how we solved it

One home per fact. The rules file went from 5,362 words of changelog to 2,170 words of present-tense rules; the web rules from 2,912 to 945; the history moved to a dated research note. Counts and alias lists are environment facts, printed by a command and never restated. Agent prompts name a rule; they never repeat it.

C. Four agents re-read what they already had

Non-fork subagents receive the instruction hierarchy automatically. Four prompts nevertheless began with "read the rules file first". That was about 11k tokens per run, roughly 200 runs, about 2.2 million tokens for nothing. Four sentences deleted.

D and E. The document writers were the expensive stage

challenge

The spec-writer ran on the most expensive model at the highest effort, 104k to 614k tokens per spec, and five specs ran to 10,000 – 16,800 words against a 600-word prose cap nobody enforced. The planner cost 19.8 million tokens across 45 runs, 2,100 – 3,600 words per task, 99 % prose, each task restating the verification command, the commit recipe and the report format that the brief script already prepends. The three document roles were 22 % of all tokens.

how we solved it

Model and effort by role: the spec-writer to a cheaper model at medium effort, since it transcribes decisions already made; the planner to high effort with a 300-word cap per task and 12 tasks per plan, boilerplate once in the header. A second spec template for refactors (goal, inventory, rules the code must satisfy, the guard that fails if violated). A word cap a hook enforces. Specs are amended by appending a dated section, never by rewriting the body. The brief carries the investigator's note and the brainstorm decisions verbatim so the writer reads code only to confirm a name exists.

F to I, briefly

  • Review budget (section 6): the mechanical rows into the script, the 1.0 rule, one whole-branch review.
  • One pipeline for every size (section 4): the S/M/L tiers.
  • Memory had become a ticket tracker. 33 of 77 files described a PR that was completed or abandoned; the index was at 19.6 KB of a 25 KB cap, past which new memories silently stop loading. The rule now: PR state is never stored, it is one tracker command away; memory keeps rulings, machine facts and diagnostic recipes.
  • Hygiene. 46 registered worktrees, about 20 of them dead; 300 MB of ignored build copies; a permission for a build command that no longer existed; two agents told to use a Figma server they had no tools for. Thirty-nine worktrees removed, six unused global skills disabled, the rest a half-hour list.
MeasureBefore the auditAfter
Words loaded every turn≈ 11,500≈ 2,950 (+ 945 path-scoped)
Rules file5,362 words, 79 dates2,170 words, ten rules, one branch table
Harness paths missing on main17 of 910, checked by a script
Document-writing rolestop model, effort xhighcheaper model; medium / high effort; caps enforced by hooks
Agents re-reading the rules file40
Memory files77 (33 stale)34
Registered worktrees467
Pipeline tiers13

09. Keeping the harness out of git

The repository belongs to a team of eight, on a company tracker, and PRs carry product and pipeline changes only. The first time a spec and a plan reached a pull request they had to be rebased out, and the reviewers had rightly asked what they were. So the first ADR of the harness is that the harness is untracked: the agents, skills, rules, hooks, tools, the local rules file, the ADRs and every spec, plan and report. The mechanism is .git/info/exclude, the local ignore file, never the tracked .gitignore a well-meaning agent would otherwise "fix".

Two files are tracked, because the team wrote them: a short persona and the architecture blueprint. The docs-sync skill reports drift in those two instead of editing them; changing them is a team decision and its own PR. Before every push, a diff of the range against the local-only paths must print nothing, and since the audit that check is the git-guard hook, not a sentence.

The consequences are accepted and written down: no CI check guards the harness, so its verification is local; a worktree session still loads the main checkout's harness; and the whole thing is one git add away from being shared if the team ever wants it. Until then it is one person's tooling and carries no team commitment.

10. What it cost, what it bought

Agent typeRunsTokensAvg / run
implementer30848.5 M157k
planner4519.8 M439k
general-purpose (research, sweeps)8612.8 M149k
conventions reviewer12412.0 M97k
code reviewer12510.3 M83k
mechanical implementer879.4 M108k
spec-writer194.8 M253k
official code review193.1 M162k
investigator122.9 M241k
generic task reviewers232.4 M105k
pr-creator130.4 M33k
Total, 28 run reports858≈ 125 M raw246k – 19.2 M per branch

What that bought, in the same six weeks: the Nx migration (ten PRs, 42 libraries, feature work never paused), a framework major upgrade with a UI-library swap across 41 libraries, the first two waves of an RxJS-to-signals migration, and the foundation plus three component waves of a new design-system kit, each with a spec, a plan, a report and a ledger. The alternative I compare against is not "the same work by hand in six weeks". It is the two of those four that would have happened at all.

The honest caveats: the dispatcher's context is uncounted; resumed agents overstate; and the largest single cost, the 19.2-million-token component wave, is the run whose review stage the audit later judged least worth its tokens.

11. Failure modes, recorded

  • Confidently wrong reviewers. Two agreed on an injector-lifetime claim with no evidence; refuted by a throwaway test. Eight findings of one branch review refuted on inspection. A reviewer's finding is a claim.
  • A planner with no edit tool rewrote the whole plan on every revision: two full writes, 303k tokens, on the first run. Fixed by giving it one, and later by caps.
  • Output nobody asked for. 104 pre-existing type errors printed per run, read in full by every agent, until the baseline. Plans that were 99 % prose. A 16,800-word spec.
  • Blocked on the environment. A branch whose node_modules belonged to another branch's framework version: every first verification failed until the dispatcher ran a clean install. "Blocked (environment)" is now a report state.
  • Stale caches that look green. A lint cache replaying a stale pass; the Nx daemon dropping a renamed library from a run. The verification script disables the daemon.
  • Refusals, some right. A subagent's own harness refused to edit the lint config as a "CI bypass"; the rule became that only the main session edits it. The pr-creator refused a vague "go on". The platform's permission classifier refused a subagent's file deletion during the memory prune, and the deletion was handed to the human as one command. Each of those is the system working.
  • History surgery in a shared tree. One implementer rewrote history with low-level git commands to tidy a commit. The ruling: no implementer touches refs beyond a pathspec commit, ever; two untidy commits stay untidy because the repo squash-merges.
  • The dispatcher's own hands. A string replacement with an unescaped $` corrupted an ADR's addendum, caught by a review. The dispatcher is not exempt from the gates.
  • Context exhaustion, twice: once into a handover document, once into the 80 % rule. A weekly budget on the top model ran out mid-run and forced a model switch; the ledger row says so.
  • Ledger holes. An accidental interrupt and an API outage lost rows; resumed agents double-count. The totals are honest about being raw.

12. Principles, and what is next

  • One home per rule. A fact lives in one file; everything else points at it. The audit is what happens when you do not.
  • Hooks over prose. If a rule can be a script, it is a script. "A deterministic guard for the two git rules prose used to carry."
  • A structural rule names the guard that fails when it is broken, or says plainly that none exists.
  • Lint is parity, not zero. A touched file comes back no worse. A ratchet only moves one way.
  • Skills for knowledge, MCP for connectivity. And every MCP server read-only unless the write path is already a deliberate, human-gated command.
  • Model and effort per role, in frontmatter, nowhere else. The transcription job does not get the reasoning budget.
  • Rulings carry a price. "Cost if wrong" next to every decision makes reversing it a sentence instead of an argument.
  • Measure the reviewers. Findings that changed code, per 100k tokens, per agent type. Then act on it sooner than five weeks.
  • The human verifies; the agent never opens the PR first.
  • No AI attribution in commits or PR bodies. The work is the team's; the tooling is a detail of how it was done.

Next, in the order I would do them:

  • The Nx plugin's census commands fully replacing the last hand-written layout tables.
  • A Playwright CLI click-through subagent as a pre-check before the human one, on the one app that has no login wall.
  • Evals: a scorer over the run reports so a change to a prompt or a tier can be judged by findings per 100k and tokens per merged task, not by feel.
  • Memory kept under the cap by a hook, not by an audit.
  • Sharing the harness with the team, which is one git add and a conversation.

The model is the easy part. The harness is the part you keep.