Feature 57 (config and provisioning): the record of its designs, playbooks and glossary words, synced with main
mesh/merge-gate head 6be8f6eada67 merged into trunk b7e22be7f55f — pass: [release feat/57-config-and-provisioning, judged by its controller cf897e89fe31] t…
mesh/repo-check head 6be8f6eada67 merged into trunk b7e22be7f55f — pass: [release feat/57-config-and-provisioning, judged by its controller cf897e89fe31] i…
mesh/delivery delivered

This commit is contained in:
2026-10-16 07:22:11 +00:00
parent b7e22be7f5
commit 6be8f6eada
90 changed files with 1524 additions and 696 deletions
+5 -4
View File
@@ -50,13 +50,14 @@ the expensive half.
| # | Playbook | Trigger |
|---|---|---|
| [01](01-research.md) | Research | An idea worth investigating before committing to design |
| [02](02-graduation.md) | Graduation & design change | Research concludes, or a design must change |
| [01](01-research.md) | Research | An idea from the operator, or one worth investigating before committing to design: setup questions, findings, a direction the operator confirms |
| [02](02-graduation.md) | Graduation & design change | Research concludes (its direction confirmed), or a design must change: decision records, the to-be design |
| [03](03-issues.md) | Issues | Something is wrong — often with the owner unknown |
| [04](04-build-handoff.md) | Build handoff | A design is ready to be built |
| [04](04-build-handoff.md) | Work breakdown and handoff | A design is ready to be built: milestones usable on their own, issues per repository, each milestone one feature step |
| [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
+105 -39
View File
@@ -1,66 +1,132 @@
# Playbook 01 — Research
**Trigger.** An idea, technology or approach worth investigating before it is committed to
design. Also: an as-is document that raises a question nobody can answer.
**Trigger.** An idea the operator expresses, or an idea, technology or approach worth investigating before
it is committed to design. Also: an as-is document that raises a question nobody can answer. A symptom (a
bug, something broken) is not research: it follows playbook [03](03-issues.md).
**Who runs it.** Anyone.
**Who runs it.** Anyone; in an agent session, the session that heard the idea leads it.
**Where it lives.** A research effort lives whole in the knowledge base, the seat `knowledge-base`, reached
through the mesh MCP server from any node and any folder (ADR 0290). This repository holds none: an effort
is written with the knowledge base's verbs, never as a folder here.
through the mesh MCP server from any node and any folder (ADR 0290). This repository holds none.
## The cycle it starts
```
idea ─► research (01) ─► decisions and design (02) ─► work breakdown in milestones (04)
─► each milestone built and delivered as one feature step (07) ─► what is reported afterwards (03, 08)
```
An idea arrives in one of two forms, and the first step differs:
- **A new idea**, where only the mesh's own context matters: read what the knowledge base holds on it.
- **An idea for an existing application**: read that application's to-be and as-is designs, its decision
records and its open issues first; the effort `touches` them.
## Steps
1. **Read what is known first.** `knowledge-base.ask` with the question in plain words returns the decision
records and research efforts that bear on it; `knowledge-base.get` with `kind: research` and a number
reads one effort, and `reasoning: true` (or `section` with a document's name) reads its documents.
2. **Take a number.** `knowledge-base.reserve` with `kind: research` and `for`: the effort's title. The
number comes from the issue tracker's counter and is spent whether or not the effort is ever proposed;
one never proposed is withdrawn after a week. Never pick a number by hand.
3. **Propose it.** `knowledge-base.propose` with `kind: research`, the `number` reserve gave, a one-line
`title`, `status: active`, `date` (the day it began), and `sections`: its documents in order, each a
`name` and its markdown `text`. The first is `00-overview.md`: **what** is being investigated, **why**,
and **what it touches**. Name what it touches as `links` of kind `touches` (`"0276"`, `"research 045"`,
`"issue 365"`, or a design's path), not only in prose, and what it names for another reason as `cites`.
4. **Do the research** in further documents of the same effort: notes, option analyses, evidence, draft
designs. Each change is `knowledge-base.propose` again with the effort whole and `why`, one line saying
why this version. Every version is kept; `knowledge-base.history` lists them and gives the difference
between two.
1. **Read what is known.** `knowledge-base.ask` with the idea in plain words returns the decision records and
research efforts that bear on it; `knowledge-base.get` reads one (`reasoning: true`, or `section`, for its
documents). Read the issue tracker for open issues in the same area (`issue-tracker.list`).
2. **Ask the operator to set it up.** A few questions, one at a time, each answerable with a choice and
carrying a recommendation: what comes first, what is out of scope, which options must be tried, what
done looks like. Three to five questions is usual; the answers are the effort's first findings.
- **Check every option against the mesh's own rules before offering it.** An option that breaks one
(the core values in [`mission.md`](../mission.md), among them *the mesh owns the truth* and
*sovereignty: external dependencies are deliberate and few*; the rules in
[`how-we-build.md`](../how-we-build.md); and everything between modules over the bus, research 036) is
dropped, or offered only with the rule it breaks named. A recommendation the operator has to argue back
costs a version.
- **Explain each question with a concrete picture** in the operator's words: what they would see, do or
lose under each option. No internal names (a record number, a verb, a word the glossary lacks) without
what it is beside it.
3. **Take a number.** `knowledge-base.reserve` with `kind: research` and `for`: the effort's title. The number
is spent whether or not the effort is ever proposed; one never proposed is withdrawn after a week. Never
pick one by hand.
4. **Propose it.** `knowledge-base.propose` with `kind: research`, the `number`, a one-line `title`,
`status: active`, `date` (the day it began), and `sections`, each a `name` and its markdown `text`:
- `00-overview.md`: the idea in the operator's words, what exists, the operator's answers (dated), and the
words the effort uses that the glossary lacks, each defined in one line;
- `01-questions.md`: the questions to answer, the first ones first;
- `02-method.md`: what will be tried and measured, and on what.
`propose` refuses an effort that breaks a rule that judges one record (an unknown status, a link to
nothing, a retired word of the glossary, a daemon's line), naming every reason at once and writing nothing.
The knowledge base then reviews the proposal itself, through a judgement that sees only the record, the
records it links and the rules; on `changes` the effort stays proposed with the findings, and its author
proposes it again. An effort that needs the operator's word is proposed with `needs-operator: true` and
waits for the operator's Approve on a channel that proves who answered.
Name what it touches as `links` of kind `touches` (`"0276"`, `"research 045"`, `"issue 365"`, or a
design's path), and what it names for another reason as `cites`.
5. **Do the research.** Try, measure and read, smallest question first. Each finding goes into
`04-findings.md`: what was tried, what it showed (with numbers), what it means. Each change of the effort
is `knowledge-base.propose` again, whole, with `why`; every version is kept, and `knowledge-base.history`
lists them and gives the difference between two. Until issue 844 is delivered, the knowledge base
takes a new version of an accepted, active effort only as a progressive insight: give `insight`, saying
what the effort held before the new finding, and mark the finding in the text with its date.
- **Propose a version per settled set, not per answer.** While the operator answers a run of questions,
or while experiments run, keep the answers and findings in the session's save point and propose once
when the set is settled. Each version is reviewed and resent whole, so a version per answer multiplies
the reviews.
- **Pre-check before proposing**, against what the review sends back most: every record, issue or design
the text names is in `links`; work put off names the issue that tracks it (an acceptance test is not
put-off work: it is what the build proves, step 6); each figure is given as measured (a range, not a
rounded claim); no word on a glossary *Not:* line.
- **Try at the cheapest level that answers the question.** A program's own behaviour (a library, a
server, a protocol) is tried in process, on the machine's loopback, in minutes. Lab runs (about 12 to
16 minutes fresh) are for the mesh's own code; before that code exists, a lab run tests nothing an
in-process test does not, and its lab scenario is written as the build's acceptance test instead.
6. **Settle the direction with the operator.** When the questions are answered, write `05-direction.md`:
the proposed decisions in a few lines each, the options rejected and why, and the build's acceptance
tests: what the build must prove before it is done, carried by its milestones (playbook
[04](04-build-handoff.md)). Nothing else is left open: each question closes by a test, by a decision the
effort records with its reason (a small design limit is the lead's), or by the operator. Ask the
operator to confirm or change the direction, one point at a time, then propose it once. Then the effort
graduates through playbook [02](02-graduation.md).
## Bounds and models
| Step | Bound | Model |
|---|---|---|
| reading and the setup questions (1, 2) | 30 minutes | the session's own, larger |
| proposing (3, 4) | 15 minutes | larger |
| each experiment or reading task (5) | 30 minutes | smaller, one agent at a time unless the operator allows more |
| the direction (6) | 30 minutes | larger |
A step past its bound is said to the operator at once, with what is being done about it. The lead reports
as playbook 07 does: every ten minutes and at once when something is found, stops or needs the operator,
with the allowance used.
## What makes research worth reading
State evidence, not assertion. *"Zero of 124 modules declare `brain` as a dependency"*
outranks *"the dependency rule is not followed"*. An effort that measured nothing has not
finished.
State evidence, not assertion. *"Zero of 124 modules declare `brain` as a dependency"* outranks *"the
dependency rule is not followed"*. An effort that measured nothing has not finished.
Research describes real observations but never identifies the mesh it observed. The shape of a
finding survives anonymisation intact — *a node publicly named but behind a household NAT*
carries the whole lesson without naming anything.
Research describes real observations but never identifies the mesh it observed. The shape of a finding
survives anonymisation intact — *a node publicly named but behind a household NAT* carries the whole
lesson without naming anything.
`propose` refuses an effort that breaks a rule that judges one record (an unknown status, a link to
nothing, a retired word, a daemon's line), naming every reason at once and writing nothing. The knowledge
base then reviews the proposal itself; on `changes` the effort stays proposed with the findings and its
author proposes it again, at most three times before the findings go to the operator with a
recommendation. An effort that needs the operator's word is proposed with `needs-operator: true` and waits
for the operator's Approve on a channel that proves who answered.
## Do not
- Do not put status in prose. It is the effort's `status`, and the prose must not restate it.
- Do not give a `became` link while the effort is open — playbook 02 gives it at closure.
- Do not write into `03-DESIGN` from an open effort.
- Do not write an effort, or a copy of one, into this repository: the knowledge base is the only place one
is read, and `records.py` refuses a new path under `01-RESEARCH/`.
- Do not write an effort, or a copy of one, into this repository: `records.py` refuses a new path under
`01-RESEARCH/`.
## Closing
An effort never just stops. It closes through playbook [02](02-graduation.md) as `graduated`
or `abandoned`, always with a `became` link to what it turned into. Nothing is deleted —
what was rejected, and why, is the more expensive half to rediscover.
An effort never just stops. It closes through playbook [02](02-graduation.md) as `graduated` or
`abandoned`, always with a `became` link to what it turned into. Nothing is deleted — what was rejected,
and why, is the more expensive half to rediscover.
## How it is checked
The rules on an effort are the knowledge base's: `propose` refuses one that breaks a rule that judges one
record (the module's table test holds one case per rule), and the rules that judge the whole record set run
after every acceptance and raise a condition when one fails. That this repository holds no effort is
`records.py`'s `no-records` check, on every pull request.
after every acceptance and raise a condition when one fails. The habits of steps 2, 5 and 6 (options checked
against the rules, one version per settled set, the cheapest level, nothing left open) are the lead's, read by
the reviewer; of the pre-check, `propose` refuses a link to nothing and a retired word, and the review returns
a record named but not linked, an untracked deferral and a figure not as measured. That this repository holds
no effort is `records.py`'s `no-records` check, on every pull request. An active effort with no new version
for seven days is the smell of research left behind: its lead closes or continues it.
+26 -4
View File
@@ -13,11 +13,26 @@ clauses in force for a topic or a record).
## Graduating research
The effort's `05-direction.md`, confirmed by the operator (playbook [01](01-research.md), step 6), is what
graduates. The lead writes the records and the design on the larger model; it is one sitting, bounded at
about an hour, and said to the operator if it runs past.
1. **Check it against GENESIS.** An effort graduates only if its conclusion is traceable to
[`mission.md`](../mission.md), [`context.md`](../context.md) and
[`effect.md`](../effect.md). If it conflicts, either the effort is wrong or GENESIS is —
say which, in writing, before proceeding.
2. **Record the decision** (below, *Writing a decision record*).
2. **Record the decisions** (below, *Writing a decision record*): one record per choice that could be
reversed on its own, not one record for the whole effort.
- **Write them together, not one after another.** Reserve every number first, then propose each record
as soon as it is written, without waiting for the review of the one before; the reviews run side by
side, and the operator's asks for the records that need them arrive together. A record that
`rests-on` or `extends` a sibling is proposed once that sibling is accepted.
- **Take the clauses from the confirmed direction** and cite the effort (`cites`, `"research NNN"`) for
the evidence instead of retyping its findings: a record's Context holds the few facts its decision
rests on, with the effort named for the rest.
- **Pre-check each record before proposing it**, as for research (playbook [01](01-research.md), step
5): every record, issue or design it names is in `links`; each deferral names its issue; each figure
is given as measured; no word on a glossary *Not:* line.
3. **Write the design.** Create the document under `03-DESIGN/01-to-be/` with frontmatter:
```yaml
@@ -31,7 +46,11 @@ clauses in force for a topic or a record).
---
```
4. **Close the effort.** `knowledge-base.propose` the effort again, whole, with `status: graduated` and a
The design lists the direction's acceptance tests: what the build proves before it is done.
4. **Hand it to the work breakdown.** The design goes on through playbook [04](04-build-handoff.md):
milestones, issues, and each milestone built as one feature step (playbook [07](07-feature-branches.md)).
5. **Close the effort.** `knowledge-base.propose` the effort again, whole, with `status: graduated` and a
`became` link to the decision record (`"NNNN"`) and to the design document (its path). An effort that
stops without a decision is proposed with `status: abandoned` and a `became` link to what replaced it.
@@ -66,7 +85,8 @@ clauses in force for a topic or a record).
superseded record cited alone, an unknown topic, a retired word of the glossary, a daemon's line, a
deferral no issue tracks. The knowledge base then reviews the proposal itself, through a judgement that
sees only the record, the records it links and the rules, and answers `approve` or `changes`. On
`changes` the record stays proposed with the findings: revise it by proposing it again, whole, with `why`.
`changes` the record stays proposed with the findings: revise it by proposing it again, whole, with `why`;
at most three rounds, then the findings go to the operator with a recommendation.
On `approve` it is accepted, or, when it says `needs-operator`, it waits for the operator's Approve on a
channel that proves who answered. Nothing proposed is in force. `knowledge-base.get` shows a record's
reviews and the operator's ask when it waits for one.
@@ -133,4 +153,6 @@ record (its module's table test holds one case per rule), and the four rules tha
changes, an accepted record extending a superseded one) run after every acceptance and raise a condition
when one fails. In this repository, `records.py` checks that a design's `decisions:` entries and every
"ADR NNNN" in the text are well formed, and that no record is written here; `index.py` checks the reading
order's six topics, which the knowledge base reads.
order's six topics, which the knowledge base reads. The three habits of graduating step 2 (side by side, clauses
from the direction, the pre-check) are the lead's, read by the reviewer; `propose` refuses a link to nothing,
a deferral no issue tracks and a retired word, and the review returns a record named but not linked.
+56 -25
View File
@@ -1,35 +1,66 @@
# Playbook 04 — Build handoff
# Playbook 04 — Work breakdown and handoff
**Trigger.** A to-be design is settled and work is about to start in a code repository.
**Trigger.** A to-be design is settled (playbook [02](02-graduation.md)) and its building is about to start.
**Who runs it.** Whoever starts the build.
**Who runs it.** The lead who will run the feature, on the larger model. It is one sitting, bounded at about
an hour, and said to the operator if it runs past.
## What it produces
A design becomes **milestones**, and each milestone becomes **issues**:
- **A milestone is usable on its own.** Once its delivery plan is done, the operator or the mesh can do
something new, said in one sentence ("a Claude Code session is mirrored into a thread on the bus").
A milestone that changes nothing anyone can use is not a milestone; fold it into the next.
- **A milestone is one feature step** of playbook [07](07-feature-branches.md): prepared, developed in one to
two hours, reviewed, checked and delivered as one. A milestone larger than that is split.
- **An issue is one repository's part of a milestone,** sized so a fixing agent finishes it inside the
milestone's development time. Issues that must land in order are linked `depends-on`.
## Steps
1. **Confirm the design is settled.** Its frontmatter reads `status: designed`, and every
claim in it traces to a decision record in the knowledge base (`knowledge-base.get` for each entry of
its `decisions:`). An open question in the text is a
reason to run playbook [01](01-research.md), not to start building around it.
2. **Name the owner.** Set `code:` in the design's frontmatter to the repositories from
[`repos.md`](../repos.md). If the repository does not exist yet, add it to `repos.md` in
the same change.
3. **Check the as-is.** Read the matching `03-DESIGN/00-as-is/` document. What is being
replaced is stated there; if it is not, write it before changing it. Building against an
undocumented as-is is how a shipped behaviour gets lost.
4. **Flip the status.** `status: in-progress`, `updated:` today.
5. **Build in the code repository.** HQ is not a code repository and never carries
implementation.
6. **On completion**, run the "when something ships" section of playbook
1. **Confirm the design is settled.** Its frontmatter reads `status: designed`, and every claim in it traces
to a decision record in the knowledge base (`knowledge-base.get` for each entry of its `decisions:`). An
open question in the text is a reason to go back to playbook [01](01-research.md), not to build around it.
2. **Name the owners.** Set `code:` in the design's frontmatter to the repositories from
[`repos.md`](../repos.md). A repository that does not exist yet is added to `repos.md` in the same change.
3. **Check the as-is.** Read the matching `03-DESIGN/00-as-is/` document. What is being replaced is stated
there; if it is not, write it before changing it. Building against an undocumented as-is is how a shipped
behaviour gets lost.
4. **Break it into milestones.** Write them into the design, in a section *Milestones*: for each, its number,
its one-sentence outcome, the acceptance tests of the design it proves, the repositories it touches, and
what it depends on. Order them so each is usable as soon as it is delivered, the smallest useful one
first.
5. **Ask the operator to confirm the milestones**, in a short table: number, outcome, the acceptance tests it
proves, repositories, the parts each repository builds. Change what they change.
6. **Open the issues.** For each confirmed milestone, `issue-tracker.open` one issue per repository part, its
title the symptom or capability in plain words, its report naming the design, the milestone and the
decisions it builds with; link the order with `depends-on`. Write the issue numbers into the milestone's
entry in the design.
7. **Flip the status.** `status: in-progress`, `updated:` today; the design change goes through playbook
[08](08-small-fixes.md) (one hq pull request).
8. **Hand each milestone to playbook 07, one at a time.** The milestone is the feature step; its issues are the
step's issues; the next milestone starts when the previous one's delivery plan is done.
9. **On completion of the last milestone**, run the "when something ships" section of playbook
[02](02-graduation.md).
## Rules
- Every merge is a human checkpoint, without exception.
- What the handoff leaves for later is an issue with an owner, named in the design or the decision that
leaves it — never a sentence saying "later". The design's `code:` names who owns what is built; the issue
names who owns what is not. the knowledge base's rule `deferral` checks the decision record when it is proposed
- What the handoff leaves for later is an issue with an owner, named in the design or the decision that leaves
it — never a sentence saying "later". The design's `code:` names who owns what is built; the issue names who
owns what is not. The knowledge base's rule `deferral` checks the decision record when it is proposed
([issue 354](../../04-ISSUES/354-a-decided-rename-was-never-scheduled-and-the-operator-believed-it-live/00-report.md)).
- Never open a pull request unprompted. A permissions list saying it is allowed is not a
request.
- Work in an isolated worktree, never a shared checkout. A failed `cd` in a shared checkout
commits to the wrong branch, and the error scrolls past.
- HQ is not a code repository and never carries implementation.
- How the building is done — worktrees, agents, review, checks, the ready mark and the delivery — is playbook
07's, not this one's.
## How it is checked
Read by the lead and the reviewer today; a check in hq's merge-check is tracked by issue 845.
- A design in `status: in-progress` has a *Milestones* section, each milestone with its outcome and its
issues; an in-progress design without one is the smell of building without a breakdown.
- Every issue a milestone names exists in the tracker (`issue-tracker.get`), and an issue in `fixing` names
its milestone in its report.
- A milestone whose issues are all `resolved` has had its delivery plan done; its outcome sentence and
acceptance tests are proven on the mesh before the next milestone starts.
+199 -55
View File
@@ -1,69 +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.
## The rule
## The shape
One feature is **one branch name**, **one worktree per repo**, **one MR per repo**, opened
**once, at the end**.
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:
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.
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 | 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 |
**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.
4. **Finish, then publish.** When the whole feature is done — every repo, tests green — push
every branch and open **one MR per touched repo**, together.
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.
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.
## What this 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.
- **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.
## 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. 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`.
## 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).
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 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
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.
- 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.
+64
View File
@@ -0,0 +1,64 @@
# 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).
**Related issues in one fix.** A small fix may cover two or three related issues when they are in the same
repository, mostly in the same module, and together still fit in about an hour: they touch the same files, or
one would be wrong or unsafe without the other (switching a clipboard's history on before its history tool stops
handing out what was copied). Each issue is named in the commit and the pull request and moved to `fixing`,
and each is resolved on its own once the fix is delivered. More issues, a second repository, or more than an
hour is a feature.
**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 3 rounds, as playbook 07; from round 2 only what is wrong). 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.