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:
+86
-17
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user