Files
hq/00-META/process/07-feature-branches.md
T
mesh-toolsandClaude Opus 5.5 893d90fb41
mesh/merge-gate head 893d90fb417e merged into trunk 1e577168bf0c — pass: the change touches no module of the mesh's graph
mesh/repo-check head 893d90fb417e merged into trunk 1e577168bf0c — pass: its merge-check.sh passed
mesh/delivery delivered
Allow up to five Sonnet agents at once and keep the larger model to two, in playbook 07
The operator, 2026-10-15: the smaller model is as good as free against the weekly allowance, so the
limit that matters is how many agents the lead can settle and follow (three to five); subagents on the
larger model stay at two at most and as rare as possible.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 12:00:09 +00:00

161 lines
11 KiB
Markdown

# Playbook 07 — Feature branches across repos
**Trigger.** Work that changes code — in one code repo or in several at once (`mesh-sdk`,
`mesh-controller`, `mesh-catalog`, `mesh-host`, `mesh-lab`, and `hq` when a decision rides along).
**Who runs it.** Anyone who writes code, engineers and agents alike. Agents follow it exactly —
it is the guard against the failure it was written for.
## The failure it prevents
A feature was worked as a branch-and-MR per *unit of thought* — one per decision, one per
stacked increment — and each MR was treated as finished when it was *opened*, not when it was
*merged*. Across repos the same feature took a different branch name in each. The MRs piled up
unmerged: one session left **sixteen** stacked intermediate MRs that had to be consolidated and
closed by hand. An MR is a review checkpoint, not a scratchpad.
It failed the other way too. A large feature was worked as one pull request per issue into protected
integration branches, each waiting for the forge's merge check, again whenever another merge moved the
branch, and for a review. Most of a day went to waiting on checks while the code itself was ready.
## The rule
One feature is **one branch name**, **one worktree per repo**, **one MR per repo**, opened
**once, at the end**.
1. **Name the feature once.** `feat/<slug>`. The *same* branch name in every repo the feature
touches — never a different name per repo, never a fresh branch per increment within the
feature.
The feature branch is never protected and takes no pull request until step 4: a protected
branch forces a merge check on every merge into it, which is the waiting this playbook avoids.
2. **Isolate each repo.** One git worktree per touched repo under `.work/<slug>/<repo>`, branched
off `main`:
```
git worktree add .work/<slug>/<repo> -b feat/<slug> origin/main
```
Parallel features never collide, and no shared checkout is edited.
**Add the untouched siblings the lab reads.** The lab beds find the other repositories by
sibling path from the lab checkout (`../mesh-tools/module.json`, `../mesh-sdk`, …), the way
the main layout has them. A `.work/<slug>/` directory holding only the touched repos fails a
bed at once with `no manifest for mesh-tools at …/.work/<slug>/mesh-tools/module.json`, after
genesis has already passed. Give the directory those repos as **detached worktrees on `main`**
— never symlinks:
```
git worktree add --detach .work/<slug>/mesh-tools main
```
3. **Commit as you go — locally.** Increments land on the one branch. Nothing is pushed and no
MR is opened mid-feature.
The unit of work is an **issue**. Each commit message names the issue it fixes, such as
"Keep the assignments on the bus (issue 652)". No pull request, merge check, review or delivery
per issue.
4. **Finish, review, then publish.** When the whole feature is done — every repo, tests green —
each repository's change gets one independent review **of the local commits, before anything
is pushed**. A push starts the forge's checks (building the modules and running every test, up
to a quarter of an hour), so a review finding fixed after a push costs another full run. The
reviewer reads the feature worktree. Once the review approves, push every branch and open
**one MR per touched repo**, together; the checks run once, on those MRs, and the feature is
delivered as one delivery plan. MRs in two or more repositories that share the branch name are
one delivery group (ADR 0239 §2); a feature in one repository is one delivery. A small change
outside a feature (a playbook, a design) goes the same way: review the local commit, then push
once.
5. **Merge promptly, once approved.** Every merge into `main` is notified and approved
(ADR 0023); once it is, merge —
do not leave it sitting. The branch is deleted on merge.
6. **Leave nothing behind.** After the MRs merge, no `feat/<slug>` branch and no `.work/<slug>`
worktree survive.
## Working one feature with several agents
A large feature (a design's whole work) is fixed by several agents at once. The **lead** is the
session that runs the feature: it holds the feature branch, sends an agent per issue, and pushes,
reviews and delivers at the end. The agents never push.
- **One feature worktree per repository, held by the lead.** Each agent works on a local branch of
its own, in a worktree of its own, branched from the feature branch's current head.
- **Land by fast-forward, under a lock.** An agent brings its branch up to the feature branch,
runs the repository's tests and `merge-check.sh` in its own worktree, then fast-forwards the
feature branch under a lock (`flock <feature worktree>.lock git -C <feature worktree> merge
--ff-only <its branch>`). If another agent landed first, it merges again and repeats.
- **The lead decides, a fixing agent builds.** Before an issue goes to an agent, the lead reads it and
the code it touches, finds every open question and settles it: decided on the issue, through the
knowledge base, or by the operator. An agent that meets an undecided design question stops, and its
whole run is spent again when it restarts. The issue the agent gets then says, in its own text:
- the repository and the files it changes, one repository where it can be (split an issue along
repository lines rather than send one agent through three);
- the decisions it builds with, quoted, never "read notes 8 to 12";
- the test to write first, which fails before the change;
- its done-when, in one sentence;
- what it must not touch, and what is landing beside it.
An agent that finds the code differs from its issue stops and reports; it does not design around it.
- **Spend the operator's allowance on judgement.** Every agent the mesh runs draws on one weekly
allowance shared by every session, and the larger model (Opus) spends it many times faster than the
smaller one (Sonnet). Fixing agents and read-only diagnosis agents run on Sonnet. The larger model is
for the lead's own judgement, the independent review of each repository before the push, and
decision records, and as few of its subagents as possible.
- **Up to five Sonnet agents at once, at most two subagents on the larger model** (the lead not counted).
What sets the limit is how many agents the lead can settle and follow, and their token cost, not the
number of issues: an issue may take one agent or several, one after another. When one lands, the next ready
issue starts at once; an issue is ready when every issue its `depends-on` links name has landed on the
feature branch and the lead has settled its open questions. (Five agents on the larger model at once,
with restarts, spent a week's allowance in a night.)
- **An issue partly done goes back to `open`** with a note naming the commits that landed and what
is left, linked `depends-on` to what it waits for. An issue is never left in `fixing` while
nobody works on it. A finished issue's agent is gone, so the lead takes it over in `fixing` (its
`by` names the lead) and keeps working it: it is resolved, with `fixed-by` naming the feature's
MRs, once the feature's delivery is done.
- **Two issues in one file:** keep both sides' behaviour; where both added tests or list lines in
one place, keep all of them. Never resolve code with a blind `git merge-file --union`: it can
interleave two functions' braces. Build and test after every resolution.
- **Fetch by name.** In a shared clone a plain `git fetch` may update only `main`. Fetch the
feature branch with an explicit refspec before branching from it or comparing against it, or an
agent merges a stale copy.
- **Never land on a red check.** Run the checks, read the result, then land in a separate step; a
command that checks and lands anyway is how a red branch lands. A manifest claiming a new seat or
a new manifest word lands only with, or after, the controller change that understands it.
- **Review the tree that is pushed.** Before the local review, bring each feature branch up to the
current trunk (fetch by name, then merge or rebase), so the reviewed tree is the pushed tree.
Where an agent session pushes through the forge tool `gitea_commit` (it puts one commit on a branch
through the forge, as step 4's push), that commit is a new one on the forge's current base: the
reviewer confirms the forge commit's tree equals the reviewed one before posting its verdict on the
pull request.
- **Three to five review rounds.** Tell every reviewer which round it is in ("round N of at most 5"): a
reviewer that does not know keeps finding the next smaller thing. From round 3 it asks changes only for a
finding that makes the change wrong, and lists smaller points as follow-ups that do not block. If it
still asks changes at round 5, stop revising and bring the operator the findings with a recommendation:
block, or follow as an issue of its own. The same holds for a decision record's review (issue 784).
- **Report every ten minutes, and at once when something lands or needs the operator,** short, as a tree or a table: the issues done (of the total), each
issue being fixed with its active time (minutes since its agent was sent, from a start time
written down then, never guessed), the issues ready next, the ones waiting, and anything that
needs the operator, and the allowance used (`anthropic-licence-manager.usage`: the week's and the
session's share). Times are local and absolute.
- **When this playbook meets a problem,** the lesson is added here, so the next feature avoids it.
## What this is not
- **Not a licence to batch unbounded work.** A feature is a *bounded* unit; if it sprawls for
days, end-of-feature bloat merely replaces per-increment bloat. Split it into features, each
its own branch and MR.
- **Not a second trunk.** Every repo branches off `main`. There is no longer an `initialization`
trunk.
## How it is checked
The end state is visible, and its absence is the smell:
- After a feature merges, `git branch -r | grep feat/<slug>` and `git worktree list` return
nothing for it. A surviving branch or worktree means step 6 was skipped.
- 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.
- A feature's issues show their commits on the feature branch (`git log --grep 'issue <n>'`), and no
pull request into the feature branch exists. A pull request per issue, or an agent waiting on a
merge check mid-feature, is the second failure above.
- More than one open MR in a repo that share no feature name, or a stack of MRs none of which is
merged, is the failure this playbook exists to prevent — stop and consolidate before opening
more.