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>
161 lines
11 KiB
Markdown
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.
|