# Worktree-Invocation Contract

The shared, universal preamble for any skill that may run from a
mux/linked git worktree. `/execute` and `/execute-group` both **read
this file and follow it** at their Step 0 rather than each spelling the
mechanism — so the detect/sync/artifact rules change in one place.

Running a skill from a linked worktree is the **normal case**, not an
error — never refuse. Establish these three facts first; everything
caller-specific (how a particular skill merges, whether it spawns
worktree agents) lives in the caller's own Step 0, AFTER it reads this.

## 1. Detect where you are

```bash
COMMON="$(git rev-parse --path-format=absolute --git-common-dir 2>/dev/null)"
TOP="$(git rev-parse --show-toplevel 2>/dev/null)"
# In a linked worktree the common .git lives under the MAIN checkout, so
# dirname(COMMON) != TOP. On the main checkout they are equal.
if [ -n "$COMMON" ] && [ "$(dirname "$COMMON")" != "$TOP" ]; then
  : # linked worktree — sync + artifact rules below apply
fi
```

`--path-format=absolute` is load-bearing: from the main checkout
`--git-common-dir` is a bare relative `.git`, and a string compare /
`dirname` on the relative form misclassifies. Force absolute on the
read. (Same trap the pib-db path resolver and the completion-gate hook
both handle.)

## 2. Sync with main before anything else (worktree only)

A worktree starts behind — plan text, code, and the staleness/CP guards
all read state that may have moved on main since the worktree was cut.
If the branch is behind main:

```bash
git merge main --no-edit
```

**On conflict, HALT the whole skill — do not auto-resolve and do not
proceed to implement against a half-synced tree.** This is an
interactive skill; resolution is the operator's call. Leave the worktree
exactly as the conflicted merge left it (conflict markers, an in-progress
merge) and surface, in plain English:

> Sync with main hit a conflict in `<files>`. The worktree is mid-merge.
> Resolve the conflicts and `git commit` the merge — **or** `git merge
> --abort` to back out — then re-run the skill. I stopped before
> implementing so nothing lands on a half-synced tree.

## 3. Completion artifacts land in the MAIN checkout

A worktree's `.claude/verification/` is gitignored and disposable —
anything written there is lost on worktree cleanup (the lost-breadcrumb
failure: `lesson_workflow_report_worktree_cwd`). Resolve the MAIN
checkout once and write every verification artifact under it:

```bash
MAIN="$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")"
# breadcrumbs, reports → "$MAIN/.claude/verification/<fid>.json"  (never cwd-relative)
mkdir -p "$MAIN/.claude/verification"
```

On the main checkout `$MAIN` resolves to the cwd, so the same line is
correct in both places — no on-main special case. The completion-gate
hook already READS breadcrumbs from `$MAIN/.claude/verification/`; this
makes the WRITE side agree.

**Sibling copies of this one git-truth — keep them in step:** the
`MAIN_RESOLVE` string constant in
`templates/workflows/execute-group-implement.js`, and the shell spelling
in `templates/hooks/action-completion-gate.sh` (a hook can't read this
doc). Three copies, one resolution; change one, change all three.

## 4. Main-only files (cabinet briefings) are read through `$MAIN` too

The same worktree-invisibility that governs *writes* (§3) also governs
*reads* of files that live only in the main checkout. `.claude/cabinet/`
briefing and context files (`_briefing*.md`, member briefings) are
typically gitignored — generated per-project by `/onboard`, never
committed — so §2's `git merge main` never brings them into a worktree
(gitignored content is in no commit), and a cwd-relative read from a
worktree fails or reads a stale copy. Skills that assemble member
briefings (the `/execute` and `/execute-group` checkpoint protocol) hit
this at CP time.

Read cabinet/briefing files through the same `$MAIN` from §3:

```bash
# member briefings, cabinet context → "$MAIN/.claude/cabinet/…"  (never cwd-relative)
cat "$MAIN/.claude/cabinet/_briefing.md"
```

On the main checkout `$MAIN` resolves to the cwd, so the same line is
correct in both places — no on-main special case. One rule, both
directions: worktree-invisible `.claude/` state is always addressed
through `$MAIN`, whether you are writing it (§3) or reading it (here).

## 5. Worktree environment lifecycle (mux projects)

Mux owns the worktree environment end to end; sessions should not
hand-repair it. Three verbs, all single-sourced in
`~/.config/mux/worktree-session-health.sh`:

- **Provision** — a project that declares gitignored env files in
  `.mux-worktree-provision` (project root; one path per line) gets them
  copied from main at worktree creation and freshness-checked on later
  health runs. A locally-modified worktree copy is never overwritten.
- **Preflight** — `mux worktree preflight [project]` verifies main's
  declared env files exist and every declared `check:` command passes.
  Run it BEFORE spawning parallel worktree lanes; non-zero exit means
  do not spawn yet. Probes should verify identity/freshness, not bare
  liveness — a leftover stack answers a bare port probe. Trust note:
  `check:` lines execute as shell from the project root (same trust
  class as npm scripts and CC hooks) — don't run preflight inside a
  clone you don't trust.
- **Reap** — every worktree removal path stops the lane's docker
  containers (exact compose-project-label match on the worktree dir
  name; containers only, volumes kept). Assumes the lane's compose
  stack was started from the worktree ROOT (compose's default project
  name = directory basename); a stack started with `-p`/
  `COMPOSE_PROJECT_NAME` or from a subdirectory is not reaped. Nothing
  for a session to do; noted so cleanup surprises are recognized as
  reaping, not data loss.

One symptom worth recognizing from a worktree: if `git merge main`
aborts citing "local changes to .mcp.json" while `git status` shows
clean, that is the mux identity symlink held under skip-worktree (main
changed the file since the worktree was cut — pre-existing behavior
either way). Recovery: `git update-index --no-skip-worktree .mcp.json
&& git checkout -- .mcp.json && git merge main`; the next health run
re-symlinks and re-hides.

## Who merges (universal), and what stays caller-specific

The universal rule every caller shares (act:3d1ac2b7): **finished worktree
work is MERGED by its producer** — a close-out or handoff describes work
already on main, and `merge_state: "merge-pending"` exists only for a
merge explicitly gated on something external, named in
`evidence.merge_gate`.

The merge ORCHESTRATION is caller-specific and stays inline in each
caller's own skill, never here:

- **`/execute-group`** keeps inline: that its `isolation: worktree`
  agents branch from and merge into MAIN regardless of the invoking cwd
  (sequential, baseline-gated merges owned by the workflow), and its
  inline-build escape hatch.
- **`/execute`** keeps inline: its merge-first close-out — Step 8
  delegates the merge to `/qa-handoff`'s merge-now recipe, then files a
  `merged` handoff that dispatches; every merge failure HALTs, never
  silently downgraded to merge-pending.
