A bed run from .work/<slug>/mesh-lab derives mesh-tools and mesh-sdk by sibling path and fails at once when the directory holds only the touched repos. Detached worktrees on main, never symlinks.
70 lines
3.5 KiB
Markdown
70 lines
3.5 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.
|
|
|
|
## 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.
|
|
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.
|
|
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](../../02-DECISIONS/0023-approval-is-the-checkpoint.md)); 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.
|
|
|
|
## 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.
|
|
- 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.
|