feat(conventions): substantive work isolates into git worktrees, merge user-ratified

Introduce the worktree convention the corpus previously lacked: any
substantive work stream targeting main — in every mode, autonomous
(/boss) and interactive alike — runs in a git worktree on its own
branch (issue-N-<slug>), and the branch reaches main only through a
user-ratified merge performed by the orchestrator (fast-forward
preferred, push covered by the ratification, no pull request).
Background sessions were already harness-isolated; this extends the
same isolation to the sessions the harness does not guard. The
convention depends on worktree.baseRef=head (the default 'fresh'
bases branches on origin's routinely-stale tip under the
no-autonomous-push discipline), now pinned in conventions.md.

boss/SKILL.md gains the run-level mechanics (§ The run worktree):
one worktree per run named after the reference issue, entered before
the first dispatch; the merge is a user-reserved run-close act; the
done-state notify names the branch as the review handle; closes #N
fires at merge-and-push time. Iron Law, rollback-sandbox framing,
user-reserved-act enumerations, rationalisations, and red flags
updated to match.

Corollary reconciliations after adversarial review: implement's
'there is no branch' narrowed to 'the loop creates no branch of its
own'; specify's auto-signed-commits-on-main heading reworded to
ratified-history framing; the sandbox bullets in conventions.md and
the CLAUDE.md fragment note the anchor is the run branch's base.

The skills meta-repo itself stays exempt (its CLAUDE.md: install
symlinks resolve to the primary clone) and direct-on-main.
This commit is contained in:
2026-07-14 12:32:26 +02:00
parent bff2120f42
commit a1d6409163
5 changed files with 138 additions and 24 deletions
+86 -17
View File
@@ -60,19 +60,24 @@ BOUNCE-BACK ONLY ON THE FOUR NAMED TRIGGERS.
A NEW CYCLE'S DESIGN RUNS AUTONOMOUSLY VIA THE `brainstorm` SWARM — IT DECIDES
THE CONVERGENT (CITED-GROUND) FORKS AND BOUNCES ONLY A DIVERGENT DIRECTION
FORK OR A GREENFIELD DESIGN BELOW THE SWARM'S GROUNDING FLOOR.
AUTONOMOUS COMMITS LAND ON THE RUN'S WORKTREE BRANCH — `main` MOVES ONLY VIA
THE USER-RATIFIED MERGE (§ THE RUN WORKTREE).
```
Universal rules (only-orchestrator-commits, main-is-sacrosanct,
language, RED-first for bugs) come from the project's `CLAUDE.md`
worktree isolation for substantive work, language, RED-first for
bugs) come from the project's `CLAUDE.md`
— and from the user-level `~/.claude/CLAUDE.md` identity/security
layer above it, loaded in every session. Both bind regardless of
mode; autonomy does not lift a user-level constraint (e.g. an
external-service-consent rule that names `/boss` explicitly). This
skill does not restate them — with one deliberate local refinement:
`main`-is-sacrosanct holds **below the session anchor**, and the
rollback sandbox (§ Direction freedom) is the one narrow `/boss`
skill does not restate them — with two deliberate local refinements:
`main`-is-sacrosanct holds **below the session anchor**, with the
rollback sandbox (§ Direction freedom) as the one narrow `/boss`
exception, letting the orchestrator wind back its *own* unpushed
autonomous commits above that anchor.
autonomous commits above that anchor; and the worktree convention is
fixed at run granularity by § The run worktree (one worktree per run,
user-ratified merge).
## The Process
@@ -97,6 +102,50 @@ autonomously when the path is clear. If the top item is a real
fork (two substantive options with no clear default), bounce
back to the user.
### The run worktree (mandatory)
Before the first dispatch, the orchestrator enters a git worktree
for the run (harness `EnterWorktree`; name it `issue-N-<slug>` after
the reference issue, or the run's topic slug while no issue exists
yet — keep the slug short, the harness caps names at 64 chars, and
the name keys the worktree and its branch). A branch opened on the
topic slug keeps its name when the reference issue is created later
in the run — the issue's decision log names the branch; no rename
churn. Every commit the run makes lands on that branch;
the primary clone's `main` checkout is never touched. This is the
project-universal worktree convention
(`../docs/conventions.md` § Git discipline — which also pins
`worktree.baseRef: "head"`, so the branch bases on the local,
possibly-unpushed `main`) applied at run
granularity — one worktree per run, spanning its iterations. A run
that bounces at Step 2, before any dispatch, never opened a worktree
— there is then no branch to report.
- **Already isolated → don't nest.** A background session the
harness has already placed in a worktree needs no second one; a
resumed run whose worktree still exists re-enters it
(`EnterWorktree` with `path`, per the mid-flight rule in Step 3)
instead of opening a parallel tree.
- **The merge is user-reserved — a run-close act, not a queue
item.** At done-state or bounce-back after work has begun, the
branch stays where it is, unmerged; the notify names it as
awaiting review (§ Done-state notifications). Only after the
user's feedback does the orchestrator integrate: merge into
`main` from the primary clone — fast-forward preferred (rebase
the branch onto `main` first if `main` has moved) — push (the
ratification covers the push), then remove the worktree and
delete the branch (`git worktree remove` from outside the tree;
`ExitWorktree` does not remove a re-entered worktree). No pull
request is opened; the review is the user reading the branch,
human-in-the-loop. The merge joins push, formal milestone close,
and external-service consent in the user-reserved set — and,
being run-close integration, it never makes a queue item
non-doable mid-run.
- **`closes #N` fires at merge-and-push time.** Close-markers in
the run's commits reach the default branch only with the ratified
merge and its push — the reference issue stays open, correctly,
while the work awaits review.
### Step 3 — Dispatch the appropriate downstream skill
Pipeline:
@@ -238,7 +287,9 @@ floor).
If the working tree is mid-flight (uncommitted changes left over
from a previous session): inspect first, then resume the right
step. Do not restart from scratch.
step. Do not restart from scratch. If a previous run's worktree
still exists, resume *inside it* (`EnterWorktree` with its path,
§ The run worktree) rather than starting a parallel tree.
### Step 4 — Loop, deciding on each agent output
@@ -268,8 +319,9 @@ For each dispatched skill's result, decide:
When everything still open genuinely needs the user without
blocking on a decision — an `idea`-labelled item (droppable
by definition; its triage is the user's prioritisation call)
or a user-reserved act (push, formal milestone close,
external-service consent) — the run is done; name those
or a user-reserved act (push, the ratified merge of the run's
branch into `main`, formal milestone close, external-service
consent) — the run is done; name those
leftovers in the notify. A remaining **design fork** is the
one user-reserved residue that is *not* done-state: it
matches bounce-back triggers 1/4 and takes the problem-state
@@ -317,7 +369,8 @@ The same test is the **done-state discriminator**. An open item is
**autonomously-doable** when it has a clear path with no genuine design
fork (a bug with an established fix pattern, a mechanical/type edit, a
test-specifiable feature) and requires no user-reserved act (no push, no
formal milestone close, no external-service consent). Follow-ups the
ratified merge into `main`, no formal milestone close, no
external-service consent). Follow-ups the
run's own closing gate discovered — audit drift items, fieldtest
findings, routed inline or filed onto the tracker — re-enter the queue
and take the same test; being discovered *after* the primary task does
@@ -391,12 +444,14 @@ decide.
`main` stays sacrosanct **below the session anchor**. At the start of an
autonomous run the orchestrator captures the HEAD commit it starts from
— the last user-ratified state — as the **session anchor**. The anchor is
that captured revision (a fixed sha), not the live `main` ref: in the
primary clone on `main` the two coincide; in a git worktree the run sits
on its own branch and the anchor is that branch's base, so a `reset` to
the anchor only ever moves the session branch and cannot reach `main` at
all. Everything the orchestrator then commits autonomously, this run, and
has **not pushed**, is its own sandbox above that anchor.
that captured revision (a fixed sha), not the live `main` ref: in a git
worktree — the normal seat of a `/boss` run (§ The run worktree) — the
run sits on its own branch and the anchor is that branch's base, so a
`reset` to the anchor only ever moves the session branch and cannot
reach `main` at all; in the primary clone on `main` (the exception —
e.g. a repo that forbids worktrees) the two coincide. Everything the
orchestrator then commits autonomously, this run, and has **not
pushed**, is its own sandbox above that anchor.
Inside the sandbox, and only there, the forward-only rule is relaxed:
when the orchestrator runs itself into a dead end — `implement` stays
@@ -411,8 +466,9 @@ hard:
autonomous commits.
- **Never a pushed commit.** Pushing hands work to the outside; a pushed
commit leaves the sandbox and reverts to forward-only (`git revert`),
never `reset`. In practice the orchestrator does not push autonomously,
so the sandbox is local.
never `reset`. In practice the orchestrator neither pushes autonomously
nor merges the run's branch into `main` (§ The run worktree), so the
sandbox is local.
- **Hard-drop, no parking.** A discarded attempt is gone — no
`attempt-N` branch or tag is kept. The reference-issue thread is its
trace; the transcript holds the rest. (The user's standing preference:
@@ -543,6 +599,13 @@ watch the orchestrator work**:
identifiers, type names, agent names, or iteration codes
(`ct.1.7`, `18a`, etc.). Describe the *change in the project*
— what is now possible, fixed, or visible — not the mechanics.
- **Name the review handle.** When the run worked in its worktree
(§ The run worktree), its work sits unmerged on the branch; the
notify says so and names the branch. The no-internals rule bans
identifiers the user cannot act on; the branch is the one
identifier that exists *for* the user's action (review, then the
ratified merge), so it is carried. A run that bounced before
entering a worktree has no branch and names none.
- **No assumption of context.** The user is on their phone and
has not seen this session. Lead with the result; the *why*
is fine if it fits in one short clause.
@@ -740,6 +803,7 @@ the user wants it — there is no autonomous filing to govern.
| "Iteration just landed cleanly, let me notify so user can see" | Mid-flow notifications burn user attention without giving them a decision to make. Iteration-clean is internal progress, not a notify event. |
| "Sub-goal of the cycle is done, that counts as done-state" | Done-state is queue-empty-of-autonomously-doable, not sub-goal-complete. If the next iteration is obvious and substantive, keep dispatching. |
| "Primary task done, the audit/fieldtest findings are filed as issues — that's a wrap, notify done-state" | Filing the findings *repopulated the queue* — and findings routed inline instead (a `bug` → `debug`, a drift item → tidy iteration) are queue work all the same. Each follow-up takes the direction-freedom test like any other item: a clear, fork-free bug or mechanical edit continues the loop — being discovered after the primary task is not a stopping reason. Only a residue of `idea` items and user-reserved acts is a legitimate done-state; a remaining fork bounces instead. |
| "The picked item is tiny — skip the worktree, work on `main` directly" | The worktree is per-run, not per-item, and it is entered before the first dispatch — its cost is one `EnterWorktree`, once. Item size never decides where autonomous commits land; `main` moves only via the user-ratified merge (§ The run worktree). |
| "Top of the backlog has two equally good options, I'll just pick one" | If both are substantive and the choice is non-obvious, that's a real fork — bounce back. The cost of a wrong pick (working-tree discard) is higher than the cost of one short ping to the user. |
| "User said 'until X then notify', X just completed, notify now" | Check whether X was actually the last actionable thing. If hitting X leaves an open cycle with obvious follow-up, the user's intent was "wake me when there's nothing else to do" — continue. |
| "Notify can name the iteration code — user will figure out what it means" | No. The notify text is for the user-as-reader who did not watch the orchestrator work. Iteration codes, crate names, function identifiers, type names — all internal, all banned. |
@@ -766,6 +830,8 @@ the user wants it — there is no autonomous filing to govern.
- About to send a notify on an internal-progress event (test suite landed, audit clean before close-fixes, iter N done with N+1 in scope).
- About to declare done-state while the queue holds a follow-up that passes the direction-freedom test — including one this run's own closing gate just surfaced. Primary-task-complete is a sub-goal; "discovered after the primary task" and "outside the originally discussed tickets" are not stopping reasons. (The inverse holds too: a queue of only `idea` items and user-reserved acts IS done-state — do not grind through user-reserved work to avoid the notify; a remaining design fork bounces as problem-state instead.)
- About to dispatch the next skill without checking that the previous output was actually good.
- About to dispatch substantive work while sitting on the primary clone's `main` checkout — the run's work belongs in its worktree (§ The run worktree); enter it before the first dispatch.
- About to merge the run's worktree branch into `main` — or push it — without the user's review; the merge is human-in-the-loop, never autonomous, and no pull request is opened.
- About to autonomously dispatch in a session where the user did NOT type `/boss`.
- About to send a notify that names a crate, an iteration code, or an agent.
- About to bounce back at every iteration boundary "just to be safe" — that's reactive deference, not direction freedom.
@@ -801,6 +867,9 @@ the user wants it — there is no autonomous filing to govern.
- **Issue-writing conventions:** `../issue/SKILL.md` — used both for the
run's reference issue on the *project* tracker and for skill-system
feedback filed on the *plugin's own* tracker (§ Skill-system feedback).
- **Worktree convention:** `../docs/conventions.md` § Git discipline —
substantive work in a git worktree, user-ratified merge, no pull
request; § The run worktree fixes the run-level mechanics.
- **Glossary write-rule:** `../docs/glossary-convention.md` —
record-reality discipline for the only autonomous glossary writer.
- **Spec auto-sign gate:** owned by `../specify` Step 6 — under `/boss`
+30 -2
View File
@@ -139,12 +139,40 @@ too.
recovery is forward-editing or `BLOCKED` (`implement/SKILL.md` Iron
Law, issue #23).
**One narrow `/boss` exception — the rollback sandbox:** inside an
autonomous run the orchestrator anchors on the `main` HEAD it started
from (the last user-ratified state) and MAY `git reset --hard` its
autonomous run the orchestrator anchors on the HEAD it started
from (the last user-ratified state; under the worktree bullet below
that is the run branch's base, so a reset moves only the branch and
can never reach `main`) and MAY `git reset --hard` its
*own* commits *above* that anchor — autonomous, **unpushed** work only
— back toward (never below) the anchor when it has run into a dead end.
A pushed commit has left the sandbox and is forward-only (`git revert`).
See `../boss/SKILL.md` § Direction freedom.
- **Substantive work runs in a git worktree — merge is human-in-the-loop.**
Any substantive work stream targeting `main` — a feature, a bug fix, a
refactor; anything beyond a trivial mechanical edit — is isolated into
a git worktree on its own branch before work starts, in every mode:
autonomous (`/boss`, see `../boss/SKILL.md` § The run worktree) and
interactive alike. (Background sessions are worktree-isolated by the
harness already; this convention extends the same isolation to the
sessions the harness does not guard.) The worktree branches from the
current **local** HEAD — pin `worktree.baseRef: "head"` in settings;
the harness default `fresh` bases the branch on origin's tip, which
is routinely stale under this convention's no-autonomous-push
discipline. Name the branch after the
tracker issue where one exists (`issue-N-<slug>`); a live worktree
branch is then also the visible being-worked-on signal in
`git worktree list`. The branch reaches `main` only through a
**user-ratified merge**: the user reviews the result, then the
orchestrator merges — fast-forward preferred (rebase the branch onto
`main` first if `main` has moved) — pushes (the ratification covers
the push), and removes the worktree and
deletes the branch. No pull request is opened, and the merge is never
autonomous. `closes #N` markers consequently fire at merge-and-push
time, when the commits reach the default branch.
**One exception:** the skills meta-repo itself forbids worktree
development outright (its `CLAUDE.md`: the install symlinks resolve to
the primary clone, so worktree edits are silently ineffective) and
stays direct-on-`main`.
These also appear in the universal-discipline fragment
(`../templates/CLAUDE.md.fragment`) that each project's CLAUDE.md
+3 -1
View File
@@ -219,7 +219,9 @@ Workflow({ name: "implement-loop", args: {
The carrier fields are defined authoritatively at the top of
`workflows/implement-loop.js`. The load-bearing `iter_id` rule is there
too: it names the scratch dir and the stats filename, NOT a branch
(there is no branch).
(the loop creates no branch of its own; it runs in whatever checkout
the session sits in — under the worktree convention, the run's
worktree branch).
### Step 2 — Read the end-report
+2 -2
View File
@@ -443,8 +443,8 @@ leaning. This is a *tool it reaches for*, not a gate it must clear: the
lens returns a finding, the orchestrator decides. Carrier and lenses are
defined in `agents/spec-skeptic.md`.
**Auto-signed CODE commits on `main` are forward-only — with one `/boss`
exception.** The spec itself is never a commit (it is a git-ignored
**Auto-signed CODE commits are forward-only once ratified — with one
`/boss` exception.** The spec itself is never a commit (it is a git-ignored
working file), so a later user veto is a forward correction: shell-`rm`
(or edit) the git-ignored spec, and `git revert` / `git checkout` only
the downstream **code** it produced. The `/boss` rollback sandbox is the
+17 -2
View File
@@ -44,7 +44,7 @@ discipline the agents are designed to enforce.
## Commit discipline and main-branch sanctity
Two rules govern who touches git history and how:
Three rules govern who touches git history and how:
- **Only the orchestrator commits.** No skill agent runs
`git commit`. Every agent writes its output into the working
@@ -63,7 +63,22 @@ Two rules govern who touches git history and how:
autonomous commits — those above the HEAD the run started from
(the session anchor) — back toward, never below, that anchor,
when I have run into a dead end. A pushed commit has left the
sandbox and is forward-only (`git revert`).
sandbox and is forward-only (`git revert`). (Under the worktree
rule below, the anchor is the run branch's base — a reset moves
the branch, never `main`.)
- **Substantive work runs in a git worktree.** Before starting any
substantive work stream — a feature, a bug fix, a refactor;
anything beyond a trivial mechanical edit — I enter a git
worktree on its own branch (harness `EnterWorktree`, or
`git worktree add`), based on the current local HEAD
(`worktree.baseRef: "head"`) and named after the tracker issue
where one exists (`issue-N-<slug>`). The branch reaches `main` only after
the user has reviewed the result: they ratify, I merge —
fast-forward preferred, no pull request — then remove the
worktree and delete the branch. Background sessions are
worktree-isolated by the harness already; this extends the same
isolation to interactive and autonomous sessions (see
`~/dev/skills/docs/conventions.md` § Git discipline).
## Design rationale ≠ implementation effort