mesh/merge-gate head 464833d0ef3f merged into trunk 99a9da095010 — pass: the change touches no module of the mesh's graph
mesh/repo-check head 464833d0ef3f merged into trunk 99a9da095010 — pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
A feature waited a day for its last issue, each small issue paid for its context again, and waits nobody saw cost the most time. The operator set the flow on 2026-10-15: prepared context per repository, one to two hours of local development on unit tests, one review of at most three rounds back to the same agents, one push for the integration checks, then the delivery plan; steps for a larger feature; a separate playbook for small fixes; models per step; a wait list with bounds; a prepared clear at each phase end.
176 lines
12 KiB
Markdown
176 lines
12 KiB
Markdown
# 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](08-small-fixes.md) 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)
|
|
|
|
1. **Name the feature once:** `feat/<slug>`, the same branch name in every repository it touches. The
|
|
branch is never protected.
|
|
2. **Make the worktrees:** one per touched repository under `.work/<slug>/<repo>`, branched off `main`
|
|
(`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 on
|
|
`main` (`git worktree add --detach .work/<slug>/mesh-tools main`), never symlinks.
|
|
3. **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.
|
|
4. **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.
|
|
5. **Move the issues to `fixing`**, `by` naming 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 vet`
|
|
and the repository's unit tests stay green. The integration tests (`merge-check.sh` with the store and the
|
|
bus) wait for phase 3.
|
|
- **Nothing is pushed** and no pull request is opened in this phase.
|
|
- **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 blind `git merge-file --union`.
|
|
|
|
## Phase 2 — Review (one reviewer, larger model; at most 3 rounds, about 15 minutes)
|
|
|
|
1. **Bring every feature branch up to the current trunk** (fetch by name, then merge), so the reviewed tree is
|
|
the tree that is pushed.
|
|
2. **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").
|
|
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.
|
|
4. **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)
|
|
|
|
1. **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).
|
|
2. **The forge's merge checks run the integration tests once** (`mesh/merge-gate`, `mesh/repo-check`). No
|
|
second review: phase 2 reviewed this tree.
|
|
3. **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.
|
|
4. **Mark each pull request ready** (`mesh/ready` and a comment `ready 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 a
|
|
short table: the phase and its time used against its bound, each issue with its state, every wait past its
|
|
bound and what is being done about it, and the allowance used (`anthropic-licence-manager.usage`: the week's
|
|
and the session's). Times are local and absolute.
|
|
|
|
## 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>` and `git worktree list` return 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 `fixing` for more than a working day, is the smell: stop and bring it to the operator.
|
|
- A bed run from `.work/<slug>/mesh-lab` that 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.
|