Merge pull request 'Playbook 07 as five bounded phases with reused context; playbook 08 for small fixes' (#364) from playbooks/07-08-fast-flow into main

This commit was merged in pull request #364.
This commit is contained in:
2026-10-15 17:54:22 +00:00
3 changed files with 240 additions and 213 deletions
+2 -1
View File
@@ -56,7 +56,8 @@ the expensive half.
| [04](04-build-handoff.md) | Build handoff | A design is ready to be built |
| [05](05-constitution-sync.md) | Constitution sync | `how-we-build.md` changed a rule the mesh enforces |
| [06](06-writing-a-module.md) | Writing a module | Something that runs today must run on the mesh |
| [07](07-feature-branches.md) | Feature branches across repos | Work that changes code, in one repo or several at once |
| [07](07-feature-branches.md) | Features | Work that changes code across issues or repositories: prepared context, local development, one review, one check, one delivery |
| [08](08-small-fixes.md) | Small fixes | One issue in one repository, about an hour of work: one review, one push, one delivery |
## The cycle is checked
+181 -212
View File
@@ -1,244 +1,213 @@
# Playbook 07 — Feature branches across repos
# Playbook 07 — Features
**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).
**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 —
it is the guard against the failure it was written for.
**Who runs it.** Anyone who writes code, engineers and agents alike. Agents follow it exactly.
## The failure it prevents
## The failures 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.
- **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.
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 shape
## The rule
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:
One feature is **one branch name**, **one worktree per repo**, **one MR per repo**, opened
**once, at the end**.
| 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 |
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.
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.
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.
**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.
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.
## Phase 0 — Prepare (the lead, larger model)
**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.
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.
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.
## Phase 1 — Develop (one fixing agent per repository, smaller model)
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.
- **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. 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 blind `git merge-file --union`.
**One exception: a catalogue change that uses a new manifest word.** The catalogue's merge check
reads manifests with the controller the mesh runs — the delivered one, not `main` and not the
group's own branch — so such a feature is delivered in two steps, controller first: merge and
deliver the controller's pull request, then ask `mesh-delivery.recheck` for the catalogue's, and
merge it on the new verdict.
## Phase 2 — Review (one reviewer, larger model; at most 3 rounds, about 15 minutes)
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.
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.
## Working one feature with several agents
## Phase 3 — Check (no review)
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.
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).
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.
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 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 and one change, sized so an agent finishes in
five to ten minutes. The repository's own check is a floor under that: mesh-controller's, with its
store, took 6 minutes 51 seconds on 2026-10-15 (the merge check of mesh-controller #342). A change that needs readers in one repository before a writer in another is two issues,
the second depending on the first, not one agent through three repositories: each extra repository
means merging the feature head and running every check again while other agents land;
- 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: for an agent that finished without committing, the lead
commits, merges the feature head, runs the checks and lands it; for an agent that stopped on its
stop rule, the lead settles the question first — then sends the issue again or lands it itself —
or takes the question out of the feature as its own issue, saying on the issue which of the two it
did. 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 after the controller change that understands it is delivered.
- **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.
**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.
## The ready note
## Phase 4 — Deliver
The form of what "The lead decides, a fixing agent builds" (above) asks the issue to say: a note on the issue
headed exactly "Ready to build". If any line cannot be filled in, the issue is not ready.
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:
```
Ready to build (<lead>, <time>).
Repository: <one repository; an issue that needs another is split, the second depending on the first>.
Files: <path:function or path, each>.
Decisions it builds with: <quoted, one line each, with where each was decided>.
First test: <name and what it asserts; it fails today because …>.
Done when: <one sentence>.
Do not touch: <files, behaviour, other issues' areas>; landing beside it: <issues>.
Stop if: <the assumption the note rests on>.
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>
```
When the reading is large, a read-only agent on the smaller model reads the code and answers the lead's
exact questions with file:line; the lead writes the note from its answers. The agent's prompt is then one
paragraph: "Fix issue N of feature <name>. Follow playbook 07's agent brief; build exactly what the
issue's ready note says." plus the feature's few local facts (below).
**The short layout** (on a channel such as Telegram, or when nothing changed but time):
## The brief a fixing agent follows
```
<slug> step <n>/<m> · <phase> <used>/<bound> min · <landed>/<total> landed · past bound: <none | what> · needs you: <nothing | what> · week <n> %
```
**Name your issue everywhere.** Begin every step, to-do item and message with the issue: "issue 805:
landing in mesh-catalog", never "landing". The operator follows several agents at once.
A report says what changed since the last one first; an unchanged table is not repeated, the short layout
says it instead.
**Stop rule, the most important** ("The lead decides, a fixing agent builds", above): a decision the note
does not give includes a new field, a new rule or a choice between two ways. Stop before writing code and
report what and where (file:line). A stop costs minutes; building the wrong thing costs a review and a
rebuild.
## The context of the lead's own session
**Read the issue first,** its ready note first of all; read other notes, a design or a decision record only
where the note points to them.
**Hard rules.** Reach the mesh only through the mesh MCP server; never ssh, never run `nox`. Never push,
never open, merge or close a pull request: the lead does. Never write to a store's database by hand; a schema change
is a numbered migration and the consolidated schema. Go only, unless the note says otherwise. Never move another issue; never edit files
the mesh manages on a machine. Use the glossary's words; the unit of work is an issue.
**Steps.**
1. Move the issue to `fixing` with `by` `<feature>-<issue>@<node>`. If it is refused, stop and report.
2. Make your own worktree on a local branch `<feature>-<issue>` from the feature branch's head.
3. Write the note's first test and see it fail. Make the change and see it pass; keep every other test
passing.
4. Commit: concise, imperative, about why, naming the issue.
5. Merge the feature branch's head into yours. In a conflict keep both sides' behaviour and every test,
then build again.
6. Run the repository's checks in your worktree (`gofmt -l`, `go vet ./...`, `go test ./...`,
`merge-check.sh`; where a test needs the store, a throwaway database server under the feature's
scratchpad, stopped when done). Read the result. If anything is red, do not land.
After every merge of the feature head, check again before landing.
7. In a separate step, land by fast-forward under the feature worktree's lock. If it is not a
fast-forward, go back to step 5.
8. Note the issue: the commits and how its done-when is proven. Leave it in `fixing`.
**Fifteen minutes.** An agent still working after fifteen minutes tells the lead, in a few lines and
without stopping, what has landed, what is left and whether part of it could be split off; the lead decides
whether it goes on or is split.
**Report back,** in under 150 words: commits per repository, the tests added and that they failed first, the check
results, and anything you stopped on or found that a later issue must know.
**The feature's local facts** (the lead gives them in the prompt or a short local file): the scratchpad and
its worktrees, the feature branch's name, the lock files, and anything particular to the feature's
repositories (a check that needs a gate built from another worktree, a test that is red at certain times).
**A shared machine's temporary directory can be filled by other sessions.** A test then fails with
"disk quota exceeded", which is the machine, not the code: mesh-controller's `internal/builder` test
`TestAGoProgramsBuildSourceIsItsImportClosure` fails when its temporary directory lies inside a git
checkout such as the home. The local facts name a temporary directory for tests outside the home, and
every check runs with `TMPDIR` set to 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.
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
The end state is visible, and its absence is the smell:
- 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.
- 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.
When this playbook meets a problem, the lesson is added here, so the next feature avoids it.
+57
View File
@@ -0,0 +1,57 @@
# Playbook 08 — Small fixes
**Trigger.** One issue in one repository, small enough to build in about an hour: a bug with a known cause,
a missing check, a wording, a playbook or design edit. Anything larger, or anything that needs a change in a
second repository first, is a feature and follows [playbook 07](07-feature-branches.md).
**Who runs it.** Anyone who writes code, engineers and agents alike.
## The aim
A small fix spends its time on the fix, and runs the long part once: **one review, one push, one check run,
one delivery**. Everything before the push is local and fast.
## Steps
1. **Read the issue and the code it names** (the session's own model, or one read-only agent on the smaller
model answering exact questions with file:line). If a design question is open, it is settled before any
code is written: on the issue, through the knowledge base, or by the operator. A fix that turns out to
need a decision or a second repository stops here and becomes a feature.
2. **Move the issue to `fixing`** with `by` as who@node.
3. **Build on a local branch** `fix/<issue>-<slug>`, in a worktree of its own branched from `main`. Write the
test that fails today first; make the change; keep `gofmt`, `go vet` and the repository's unit tests green.
Commit, naming the issue. For a playbook or design edit, run the repository's own checks (in hq,
`sh merge-check.sh`).
4. **One review of the local commit, before anything is pushed** (a reviewer that did not write it, larger
model; at most 2 rounds). The reviewer's findings go back to the same agent that wrote the fix, continued by
its id, so the fix keeps its context. A playbook or design edit of a few lines may skip the reviewer: the
operator's word on the pull request is its review.
5. **Push once and open the pull request.** The forge's merge checks run the integration tests. A failed check
goes back to the same agent with the failure, and the reviewer reads only the change it makes.
6. **Mark it ready** (`mesh/ready` and `ready at <full sha>`) once the operator's word is given; mesh-delivery
merges it when its checks pass.
7. **Follow its delivery plan to done** (`mesh-controller.plans` with `wait` and `until: done`), prove the fix on
the mesh (the symptom gone, measured), and resolve the issue with `located-in` and `fixed-by` the same hour.
## Bounds
| Waiting on | Bound | Past the bound |
|---|---|---|
| building the fix | 1 hour | it is a feature: stop and plan it under playbook 07 |
| the review | 15 minutes | ask the reviewer where it stands |
| the merge check | 15 minutes | read `mesh-controller.queue` and the check's log |
| the merge after ready | 5 minutes after the checks pass | read `mesh-delivery.show` for the delivery |
| the delivery plan | its class's budget (`mesh-delivery.times`), at most 30 minutes | read `mesh-controller.plans`, then `mesh-delivery.stalled` |
A wait past its bound is said to the operator at once, with what is being done about it.
## Models
The fixing agent runs on the smaller model; the reviewer on the larger. A session fixing alone does the
reading itself and uses one subagent only for the review.
## How it is checked
- A small fix has one pull request with one or few commits naming its issue, opened after its review: a pull
request with many heads is a fix reviewed after its push.
- Its issue moves to `resolved` within the hour its delivery plan ends.