A one-session feature is easy now: you write a dispatch, the agent works, the gate passes, you commit. A three-week project is a different animal, and it fails in ways a single session never shows you. Session four boots with no memory of session three's decision and relitigates it. Dispatch nine quietly contradicts dispatch two because nothing recorded what two settled. By week two there are four documents that half-agree about the current state, and the thing you are least sure of is what is actually done. None of these are model failures. They are orchestration failures, and the answer is not a smarter agent or a longer context. It is a harness: a small set of durable artifacts and a fixed rhythm that turns a long project into a sequence of clean, single-session dispatches, each booting from state the last one wrote down.
This is the personal harness deliverable of the course. Everything before it in Part 3 was a component: the dispatch (3.1), the gate (3.2), the state file (3.1), git isolation (3.6). This lesson assembles them into a system you install once and run for a career. This course's own repository is a live instance of it, parallel worktree phases and all, which is why its STATUS.md reads the way it does.
Why long projects collapse, and what the harness answers
Three specific failure modes, each with a specific artifact as its answer:
- Context loss between sessions.
/clearis total; the next session knows only what is written in the repo. Answer: durable state (STATUS.md for the ledger, handoff.md for the baton). - Drift across dispatches. Twenty dispatches each locally reasonable can walk a project somewhere nobody chose. Answer: a goal tree that every dispatch traces back to, and a verification registry that pins what "correct" means per area so it cannot be quietly redefined.
- No single source of truth. When state lives in your head and three stale docs, every session starts by guessing. Answer: one canonical state, updated inside the dispatch-close ritual, never as an afterthought.
The figure is the whole system on one page: durable state on the left survives every session; the operating rhythm on the right is a loop that reads that state, produces a dispatch, gates it, writes the state back, and clears. The property that makes it work is the one Part 1 was building toward: the conversation is disposable because the state is not.
The artifacts, complete and copyable
Seven files. Each is shown filled in for a worked mini-project (a repo-health tool for the practice repo, built in three dispatches: a stats collector, a staleness checker, and a report formatter) so you copy an inhabited template, not a skeleton with holes.
1. The goal tree
A goal tree decomposes one project into a tree of agent-sized dispatch leaves. A leaf is right-sized when it fits one session, states its outcome as a checkable result, and names its own verification. The tree is what every dispatch traces back to, so drift has something to be measured against.
# GOAL TREE: repo-health tool
## Root goal
A `repo-health` CLI that prints per-directory stats, flags stale files,
and renders a markdown report. Done = `repo-health --report` produces a
report a stranger can read, with tests green and CI passing.
## Milestone 1: data collection
- [x] LEAF 1.1 stats collector
outcome: `collectStats(dir)` returns file+line counts per subdir
verify: unit tests on a fixture tree; typecheck; lint
depends on: nothing
- [ ] LEAF 1.2 staleness checker
outcome: `findStale(dir, days)` lists files untouched > N days by git
verify: unit test with a fixture repo of known commit dates; typecheck
depends on: 1.1 (shares the directory walk)
## Milestone 2: presentation
- [ ] LEAF 2.1 report formatter
outcome: `--report` renders stats + staleness as markdown to stdout
verify: snapshot test of the rendered report; manual read; CI green
depends on: 1.1, 1.2
## Out of tree (log, do not build)
- Web UI for the report (not this project)
- Multi-repo aggregation (elective, later)The checkbox is the truth of what is done. The depends on lines are the ordering: a leaf is dispatchable only when its dependencies are checked off, which is how the dispatcher picks the next one without you re-deriving the plan every session.
2. STATUS.md, the durable ledger
STATUS.md is the project's long memory: what is happening, what shipped, what was decided and why, what is open, what is broken, what is off limits. It changes at every dispatch close and it is the first thing every session reads.
# STATUS: repo-health
## Active work
LEAF 1.2 staleness checker. No dispatch in flight.
## Just shipped
- a1b2c3d feat(stats): collectStats returns per-dir file+line counts (LEAF 1.1)
- e4f5g6h test(stats): fixture-tree unit tests, green
## Decisions
- 2026-07-05 Staleness measured by git last-commit date, not filesystem
mtime, because checkouts reset mtimes. (Affects LEAF 1.2.)
- 2026-07-05 In-memory only, no DB; this is a CLI over one repo.
## Open questions
- Should --report default to stdout or write a file? Leaning stdout;
operator to confirm before LEAF 2.1.
## Known issues
- collectStats follows symlinks; could loop on a self-referential link.
Logged, not fixed; out of scope until it bites.
## Don't touch
- test fixtures in tests/fixtures/** (golden inputs; changing them
invalidates the tests)3. handoff.md, the baton
STATUS.md is the ledger; handoff.md is the sticky note on top of it. It answers one question for the next session: you are here, do this next. It is short, it is overwritten every session, and it carries the tactical detail STATUS.md is too durable to hold.
# HANDOFF -> next session
## You are here
LEAF 1.1 shipped and committed. LEAF 1.2 is specced in the goal tree
and unblocked (1.1 is done).
## Do this next
Dispatch LEAF 1.2. Run `/dispatch-next 1.2` to generate the dispatch,
review its scope against the goal tree, then execute in a fresh session.
## Gotchas discovered
- The fixture repo for staleness tests needs commits with backdated
dates: use `git commit --date=`. A plain fixture has all-now commit dates
and cannot test the > N days branch.
- `collectStats` already exports the directory walk; reuse it in
`findStale`, do not write a second walk.
## State of the tree
Branch: main is clean, everything committed. No worktree open.
Verified: typecheck, lint, unit all green as of a1b2c3d.The split matters: if you kept only one file, either it stays tactical and loses the project's decision history, or it stays durable and buries "do this next" under six months of ledger. Two files, two jobs.
4. verification.md, the registry
The verification registry maps areas of the codebase to the checks that must pass when that area changes. It is the machine half of every dispatch's VERIFICATION section, written once so the dispatcher can look it up instead of you retyping it, and so "correct" for an area cannot be silently redefined dispatch to dispatch.
# VERIFICATION REGISTRY
# area glob -> commands that must pass when that area changes.
# The dispatcher reads this to build each dispatch's VERIFICATION block.
src/** -> npm run typecheck && npm run lint && npm test
tests/** -> npm test (never weaken; test diffs reviewed first)
tests/fixtures/** -> DON'T TOUCH (golden inputs; changing them fails review)
*.md, docs/** -> npm run build (docs must not break the build)
package.json -> npm ci && npm run build (lockfile integrity)
## Always, every dispatch
- git diff --stat reviewed against the dispatch's IN SCOPE before commit
- STATUS.md and handoff.md updated at dispatch close5. The dispatcher, shipped as a skill
The dispatcher is a procedure that reads the state and constructs the next dispatch in the 3.1 five-section shape. Shipping it as a skill (2.4) means it is a repeatable command, /dispatch-next <leaf>, that any session can run, with the current state injected automatically. Skills live at .claude/skills/<name>/SKILL.md; the frontmatter's description tells Claude when it applies, disable-model-invocation: true makes it manual-only (you decide when to dispatch, not the model), and allowed-tools pre-approves the read-only commands it needs.
---
name: dispatch-next
description: Construct the next dispatch for this project from the harness
state. Use when the operator runs /dispatch-next with a goal-tree leaf id.
disable-model-invocation: true
allowed-tools: Read, Bash(cat *), Bash(git status *), Bash(git log *)
---
## Current harness state
Goal tree:
!`cat goal-tree.md`
Project ledger:
!`cat STATUS.md`
Baton:
!`cat handoff.md`
Verification registry:
!`cat verification.md`
## Your task
The operator wants a dispatch for goal-tree leaf: $ARGUMENTS
Do NOT write any implementation code. Construct a dispatch and stop.
1. Find leaf $ARGUMENTS in the goal tree. If its `depends on` leaves are
not all checked off, STOP and say which dependency is unmet.
2. Read its `outcome` and `verify` lines. These become GOAL and the
machine half of VERIFICATION.
3. Look up every file area the leaf will touch in the verification
registry, and include those exact commands in VERIFICATION.
4. Cross-check STATUS.md Decisions and Don't touch: fold relevant
decisions into the dispatch so the agent does not relitigate them,
and copy any relevant Don't-touch paths into DON'T TOUCH.
5. Emit the dispatch in exactly this shape, nothing else:
# DISPATCH: <leaf id and name>
## GOAL (the leaf outcome, as an observable result)
## IN SCOPE (named paths the leaf may create or change)
## OUT OF SCOPE (adjacent work declined; what an eager intern would add)
## DON'T TOUCH (from the registry + STATUS.md; back high-cost ones with a hook)
## VERIFICATION (registry commands + one operator check)
6. End with: "Review this dispatch against the goal tree before executing
in a fresh session."That skill is the "reads state and constructs the next dispatch" box in the figure, made real. It never implements; it composes state into a bounded instruction, which you review and then hand to a fresh session. Re-verified against the current skills docs: ! runs a command and injects its output at load time, $ARGUMENTS substitutes what you typed after the command, and allowed-tools grants those tools without per-use prompts while the skill is active.
6. Specialist roles
Not every dispatch wants the same agent. Subagents (2.5) let you define roles with scoped tools and a charter, each in .claude/agents/. Four earn their place on most projects:
---
name: implementer
description: Executes a bounded dispatch: writes code to satisfy GOAL
within IN SCOPE, runs the VERIFICATION gate, stops at green.
tools: Read, Edit, Write, Bash
model: inherit
---
You implement one dispatch. Stay inside IN SCOPE; anything you would
add beyond it goes in a note for the operator, not in the diff. Run the
VERIFICATION commands yourself and do not report done until they pass.
If the spec is wrong or underspecified, stop and say so; do not guess.---
name: reviewer
description: First-pass review of a dispatch's diff against its spec.
Finds lies, scope creep, and weakened tests. Never the final say.
tools: Read, Bash(git diff *), Bash(git log *)
model: inherit
---
Review the diff, not the files. Check it against the dispatch: does it
match GOAL, stay inside IN SCOPE, and leave DON'T TOUCH untouched? Apply
the 3.4 hierarchy: matches spec, then lies (mocks, stubs, weakened
tests), then soundness. Report findings; you cannot approve or merge.---
name: debugger
description: Drives the 3.5 debugging loop on a reproduced failure:
symptom, hypothesis, cheapest discriminating test, repeat.
tools: Read, Edit, Bash
model: inherit
---
Reproduce first; if you cannot reproduce it, say so and stop. State a
hypothesis and the cheapest test that would discriminate it before
changing code. Do not "fix" what you cannot explain. Report the root
cause, not just the symptom that went away.---
name: researcher
description: Read-only investigation and doc fetching. Returns a findings
summary, never edits. Use before specifying unfamiliar work.
tools: Read, Grep, Glob, WebFetch
model: inherit
---
Investigate and report. You never modify files. Fetch and cite current
docs for any external-tool claim (versions, flags, APIs change). Return
a summary an operator can turn into an IN SCOPE list, plus the risks and
unknowns you found.The reviewer's read-only tool list and the researcher's no-edit charter are not stylistic: they are the structural enforcement of "the thing that checks is not the thing that did", which is the same independence 3.1, 3.4, and 3.7 all rely on.
7. The operating rhythm
The artifacts are inert without the ritual that drives them. The rhythm is five steps per dispatch, and its discipline is the whole game.
# OPERATING RHYTHM (one pass per dispatch)
1. BOOT. Fresh session. It reads (CLAUDE.md points it here):
STATUS.md, handoff.md, goal-tree.md. Now it knows the state.
2. DISPATCH. Run /dispatch-next <leaf>. Review the generated dispatch
against the goal tree and tighten scope. Optionally hand it to the
implementer subagent, or run it in this session.
3. GATE. Run the VERIFICATION commands. Not green is not done. Review
the diff against IN SCOPE (git diff --stat first).
4. UPDATE STATE. Commit at the gate (3.6). Update STATUS.md (just
shipped, new decisions, open questions, known issues), check off the
goal-tree leaf, and overwrite handoff.md with the next "do this next".
5. CLEAR. End the session. State is durable, so this costs nothing.
Boot rule for CLAUDE.md:
"Read STATUS.md, handoff.md, and goal-tree.md before any task."The one rule that keeps the whole thing honest is step 4 happening before step 5, every time. A dispatch that ships code but not a state update leaves the next boot standing on a stale ledger, which is 3.1's context-poisoning failure with your own name on the poison. Update the state as part of finishing the work, never as a favor to your future self that a deadline can cancel.
Where it breaks
The harness becomes the work. A two-file weekend script does not need a goal tree and a verification registry; that is 3.10's calibration lesson arriving early. The harness earns its overhead on multi-session, multi-dispatch projects. Install it when the project is long enough that state loss between sessions is a real cost, not before.
Stale state, the recurring villain. Every artifact here is a liability the moment it stops being true. A goal tree with leaves checked off that were never built, a STATUS.md that says the migration is pending when it shipped, a handoff.md pointing at a "next step" that is already done: each sends the next session confidently wrong. The rhythm's step 4 is the only defense, and it only works if it is non-negotiable.
Dispatcher drift. The dispatcher skill is only as good as the state it reads. If the goal tree is vague ("build the reporting stuff"), the dispatch it constructs is vague, and you are back to "add rate limiting to the API". Precise leaves in, precise dispatches out. The skill composes state; it cannot invent rigor the state lacks.
Over-decomposition. A goal tree with forty leaves for a small project is its own kind of theater. Leaves should be session-sized, not line-sized. If a leaf's verification is "the variable is renamed", it is too small to be a leaf; fold it into a neighbor.
Lablab-3-9Install the harness and run a three-dispatch project
Goal: Install all seven harness artifacts on your practice repo, then run a real three-dispatch mini-project through the full boot-dispatch-gate-update-clear rhythm without losing state between sessions.
Prereqs: the agentic-practice repo with the STATUS.md from 3.1, the hooks from 2.6, and a working verification gate.
- Install the artifacts. Create
goal-tree.md,handoff.md,verification.md, andrunbook.mdin the repo, and the.claude/skills/dispatch-next/SKILL.mdand four.claude/agents/*.mdfiles, from the templates above. Adapt the goal tree to a real three-leaf project (therepo-healthtool is a fine choice; so is anything with three ordered, session-sized pieces). Add the boot rule to CLAUDE.md. - Verify the dispatcher works before you rely on it: run
/dispatch-next 1.1and read what it produces. It must emit a five-section dispatch whose VERIFICATION matches your registry, and it must refuse a leaf whose dependencies are unmet (try/dispatch-next 2.1first and confirm it stops on the unmet dependency). - Run leaf 1 through the full rhythm: boot, dispatch, execute, gate, and at the gate update all state (commit, check off the leaf, rewrite handoff.md), then clear.
- Actually clear the session, then boot a fresh one and confirm it reconstructs the project state from the files alone: ask it "what is the state of this project and what is next?" and check its answer against reality. This is the test that the harness works, not the individual dispatch.
- Run leaves 2 and 3 the same way. Between each, the only thing carrying you forward is the written state; if a fresh session ever boots confused, the previous session's step 4 was incomplete, and that is the finding.
- At the end, confirm the three-dispatch project is done, all leaves checked, STATUS.md and handoff.md honest, and the git history a clean sequence of gate-boundary commits.
Verify
- All seven artifacts exist and are filled with your project's real content, no template placeholders left.
/dispatch-nextemits a correctly-shaped dispatch and refuses a leaf with unmet dependencies.- A genuinely fresh session (after a real clear) answers "what is done and what is next" correctly from the files alone, at least twice during the project.
- The finished project has three gate-boundary commits, a fully-checked goal tree, and a STATUS.md whose "just shipped" matches git log.
>Troubleshooting
/dispatch-nextdoes not appear: the skill is not discovered. Confirm the path is exactly.claude/skills/dispatch-next/SKILL.md, that the frontmatter is valid YAML, and run/doctorif the description looks truncated. A malformed frontmatter still loads the body under/dispatch-nextbut with no description.- The
!commands in the skill show nothing: they run at load time from the session's working directory. Run the skill from the repo root whereSTATUS.mdand friends live, or thecatfinds nothing. - A fresh session boots confused about state: the previous session skipped or half-did step 4. This is the failure the whole harness exists to prevent; treat it as a process bug, and tighten the dispatch-close ritual, not the prose.
- The goal tree and STATUS.md disagree about what is done: pick git as the tiebreaker (what is actually committed and passing), fix both docs to match it, and note that the two drifted, which means an update was skipped.
Knowledge check
Knowledge check
Sources
- Skills (SKILL.md frontmatter fields
name/description/disable-model-invocation/allowed-tools; dynamic context injection with!bash execution at load time;$ARGUMENTSsubstitution; skill locations.claude/skills/<name>/; running skills in a subagent; tool restriction): https://code.claude.com/docs/en/skills (fetched July 2026) - Best practices for Claude Code (long-horizon spec discipline, CLAUDE.md as boot instructions, subagents for delegation and context isolation): https://code.claude.com/docs/en/best-practices (fetched July 2026)
- Sub-agents (agent definition frontmatter
name/description/tools/model, per-role tool scoping,.claude/agents/location): https://code.claude.com/docs/en/sub-agents (fetched July 2026) - This course's own repository as a live instance of the harness (parallel worktree phases, STATUS.md gate reports, phase-file dispatches), verified against its STATUS.md and git history July 2026.