14 KiB
Playbook 07 — Features
Trigger. A feature: work that changes code in one repository or several (mesh-sdk, mesh-controller,
mesh-catalog, mesh-host, mesh-lab, and hq when a design rides along), too large to be one small fix.
A small fix (one repository, one issue, about an hour of work) follows
playbook 08 instead.
Who runs it. Anyone who writes code, engineers and agents alike. Agents follow it exactly.
The failures it prevents
- Pull requests as scratchpads. A feature worked as one pull request per unit of thought left sixteen stacked pull requests unmerged, consolidated by hand afterwards. A pull request is the end of a feature, not a step in it.
- Waiting on checks mid-feature. A feature worked as one pull request per issue into protected branches spent most of a day waiting for merge checks while the code was ready.
- Context bought again and again. A fresh agent per small issue spends most of its run reading the playbook, the issue and the code before it writes a line. On 2026-10-14 an issue sized for five to ten minutes took forty-one.
- Finished work that never ships. A feature that kept taking new issues held thirteen landed issues undelivered for a day: an issue done at 21:09 was still not on the machines at 15:00 the next day.
- Work that waits and nobody notices. Most lost time was not work at all, but a check, a delivery plan or an agent waiting on something that had stopped, unseen.
The shape
One feature is one branch name, one worktree per repository, one pull request per repository, opened once, after the review. It runs in five phases, and its time is bounded:
| Phase | What happens | Bound |
|---|---|---|
| 0. Prepare | the lead settles the open questions and writes one context file per repository | 15 minutes |
| 1. Develop | one fixing agent per repository builds every issue locally, unit tests only | 1 to 2 hours |
| 2. Review | one reviewer reads all of it; reports go both ways; at most 3 rounds | 15 minutes |
| 3. Check | push; the forge's merge checks run the integration tests; no further review | one check run |
| 4. Deliver | merge; the delivery plan builds, publishes and sends the whole feature | its delivery plan |
A feature that cannot be developed in about two hours is split into steps. Each step runs all five phases on its own; the next step starts only when the previous step's delivery plan is done. A step is the unit that ships, so nothing finished waits for work that is not.
Scope is fixed at phase 0. An issue found during a feature goes to the next step or the next feature, never into the running one, unless it blocks the running one.
Phase 0 — Prepare (the lead, larger model)
- Name the feature once:
feat/<slug>, the same branch name in every repository it touches. The branch is never protected. - Make the worktrees: one per touched repository under
.work/<slug>/<repo>, branched offmain(git worktree add .work/<slug>/<repo> -b feat/<slug> origin/main). The lab beds find the other repositories by sibling path, so add the untouched siblings the lab reads as detached worktrees onmain(git worktree add --detach .work/<slug>/mesh-tools main), never symlinks. - Settle every open question in the feature's issues before any agent starts: on the issue, through the knowledge base, or by asking the operator. A fixing agent that meets an undecided design question stops, and its run is spent again.
- Write one context file per repository,
.work/<slug>/<repo>.context.md. It is what a fixing agent reads instead of rediscovering the code, and it is reused by every agent and every round:- the issues it builds, in order, each with: the files and functions it changes, the decisions it builds with (quoted, with where each was decided), the test to write first, done-when in one sentence;
- the repository's map for this feature: the packages and files involved, with one line each on what they hold, and the patterns to copy (file:line);
- what not to touch, and what lands beside it in other repositories;
- the local facts: the worktree, the branch, the check commands, a temporary directory for tests outside
the home (
TMPDIR), a throwaway database server if the store tests need one. When the reading is large, a read-only agent on the smaller model answers the lead's exact questions with file:line, and the lead writes the file from its answers.
- Move the issues to
fixing,bynaming the feature's agent for that repository.
Phase 1 — Develop (one fixing agent per repository, smaller model)
- One agent per repository, kept for the whole feature. It reads its context file once and builds the issues one after another, committing each locally with a message naming the issue ("Keep the assignments on the bus (issue 652)"). It is not replaced per issue: the lead continues the same agent (by its id) with the next instruction, so its context is paid for once.
- Unit tests only. Each issue's first test fails before its change and passes after;
gofmt,go vetand the repository's unit tests stay green. The integration tests (merge-check.shwith the store and the bus) wait for phase 3. - Nothing is pushed and no pull request is opened in this phase. Never land on a red check: run the checks, read the result, then land in a separate step.
- The fixing agent's brief (the lead gives it with the context file): name your issue in every step and message ("issue 805: landing in mesh-catalog"); reach the mesh only through the mesh MCP server, never ssh; never push, open, merge or close a pull request; never write to a store's database by hand, a schema change is a numbered migration and the consolidated schema; Go only unless the context file says otherwise; never move another issue. Report back in under 150 words: commits, tests added and that they failed first, the check results, anything you stopped on.
- An agent that finds the code differs from its context file stops and reports (file:line); the lead settles it, updates the context file, and continues the agent.
- Two repositories that depend on each other keep their order in the context files: the reader before the writer, the controller's new manifest word before the catalogue manifest that uses it.
- Several agents in one repository (only for a large repository with independent areas): each works on a
local branch of its own from the feature branch's head and lands by fast-forward under a lock
(
flock <feature worktree>.lock git -C <feature worktree> merge --ff-only <its branch>), after its unit tests pass; if another landed first, it merges again and repeats. In a shared clone, fetch the feature branch by name before branching from it. Never resolve a conflict with a blindgit merge-file --union.
Phase 2 — Review (one reviewer, larger model; at most 3 rounds, about 15 minutes)
- Bring every feature branch up to the current trunk (fetch by name, then merge), so the reviewed tree is the tree that is pushed.
- One reviewer reads every repository's local commits together, against the context files and the decisions. Its report is per repository: each finding as file:line, the failure it causes, and the fix. It says which round it is in ("round 1 of at most 3").
- The report goes to the same fixing agents that wrote the code, continued by their ids, so they fix with the context they already hold. Each answers the reviewer: what it changed, per finding, or why not.
- The reviewer reads the answers and the new commits, and approves or reports again. From round 2 it asks changes only for a finding that makes the feature wrong; smaller points become issues of their own. After round 3 the lead stops and brings the operator what is left, with a recommendation.
Phase 3 — Check (no review)
- Push every feature branch and open one pull request per repository, together. Pull requests in two or
more repositories that share the branch name are one delivery group (ADR 0239 §2).
Where a session pushes through the forge tool
gitea_commit(one commit on the forge's current base), the reviewer first confirms that commit's tree equals the reviewed one. - The forge's merge checks run the integration tests once (
mesh/merge-gate,mesh/repo-check). No second review: phase 2 reviewed this tree. - A check that fails goes back to the reviewer with the failure, for one more cycle of phase 2 with the same fixing agents; then push once again.
- Mark each pull request ready (
mesh/readyand a commentready at <full sha>) once the operator's word for the feature is given; mesh-delivery merges it when its checks pass (ADR 0364).
One exception: a catalogue change that uses a new manifest word. The catalogue's merge check reads
manifests with the controller the mesh runs, so the controller's pull request is merged and delivered first;
then mesh-delivery.recheck the catalogue's and merge it on the new verdict. Plan such a feature as two steps.
Phase 4 — Deliver
The merge starts the delivery plan: build, publish, send. The lead follows it to done
(mesh-controller.plans with wait and until: done), then resolves the feature's issues with fixed-by
naming its pull requests, and removes what is left: no feat/<slug> branch and no .work/<slug> worktree
survive.
Models
| Who | Model | Why |
|---|---|---|
| The lead (phase 0 decisions, the context files, following and reporting) | larger | judgement |
| Read-only questions about the code for the context files | smaller | reading, answered with file:line |
| Fixing agents (phase 1, and the fixing in phases 2 and 3) | smaller | building to a written context |
| The reviewer (phase 2, and the extra cycle in phase 3) | larger | judgement, once per round |
| Decision records | larger | judgement |
Every agent the mesh runs draws on one weekly allowance shared by every session, and the larger model spends it many times faster. Up to five agents on the smaller model at once, at most two subagents on the larger one (the lead not counted).
Following and reporting
Most lost time is waiting nobody saw. The lead therefore keeps a wait list: everything the feature waits on, with when it started and its bound.
| Waiting on | Bound | Past the bound |
|---|---|---|
| a fixing agent's issue | 30 minutes | ask it where it stands; split or take over |
| the review | 15 minutes a round | ask the reviewer; stop at round 3 |
| a merge check | 15 minutes | read the build queue (mesh-controller.queue) and the check's log |
| a merge after ready | 5 minutes after the checks pass | read mesh-delivery.show for the delivery |
| a delivery plan | its class's budget (mesh-delivery.times), at most 30 minutes |
read mesh-controller.plans, then mesh-delivery.stalled |
Every ten minutes, and at once when something lands, stops or needs the operator, the lead reports, in one of the two layouts below. Times are local and absolute; every number has its unit and its bound or baseline; an issue is named by its number and a few words, never by its number alone.
The full layout (in the session), the issues as a tree: grouped by repository, each issue under the one it waits for, marked ✓ landed, ▶ fixing, ⏸ waiting, ✗ stopped:
Feature <slug> · step <n> of <m> · phase <name>, <minutes used> of <bound> min
Issues: <landed> of <total> landed · <fixing> fixing · <waiting> waiting
mesh-controller (agent: fixing, 2 of 3 landed)
├── ✓ 652 assignments on the bus landed 14:02
├── ▶ 736 placement words leave the layers fixing since 13:51, 23 of 30 min
│ └── ⏸ 751 config keys named (mesh-catalog) waits for 736
└── ✓ 654 a file names its template landed 13:20
mesh-catalog (agent: waiting)
└── ⏸ 751 (above, under 736)
Past its bound: <the wait, how long, what is being done> — or "none"
Needs you: <one line per question, with my recommendation> — or "nothing"
Allowance: week <n> % (<+d> since the last report), session <n> %
Next: <the next action, one line>
The short layout (on a channel such as Telegram, or when nothing changed but time):
<slug> step <n>/<m> · <phase> <used>/<bound> min · <landed>/<total> landed · past bound: <none | what> · needs you: <nothing | what> · week <n> %
A report says what changed since the last one first; an unchanged table is not repeated, the short layout says it instead.
The context of the lead's own session
At the end of each phase, and whenever the session's context grows large or spread over unrelated work, the
lead prepares a /clear as the mesh's instruction "prepare before clear" says: a save point with the
feature's phase, its branches and heads, the context files' paths, the agents' ids, the wait list and the
next action; then the prompt the operator pastes. The context files make the feature resumable after a clear.
How it is checked
- A feature's issues show their commits on the feature branch (
git log --grep 'issue <n>'), and no pull request into a feature branch exists. A pull request per issue, or an agent waiting on a merge check in phase 1, is the failure this playbook prevents. - A context file exists per touched repository under
.work/<slug>/while the feature runs. - After a feature's delivery,
git branch -r | grep feat/<slug>andgit worktree listreturn nothing for it. - More than one open pull request in a repository that share no feature name, or a feature step whose issues
stay in
fixingfor more than a working day, is the smell: stop and bring it to the operator. - A bed run from
.work/<slug>/mesh-labthat fails naming a.work/<slug>/<repo>/…path it cannot find is a missing sibling worktree, not a mesh fault.
When this playbook meets a problem, the lesson is added here, so the next feature avoids it.