# Incubate

> Clone or create repos for active development — the right hand of /learn. Use when user says "incubate [repo]", "work on [repo]", "clone for dev", or wants to set up a dev workflow. Use --wt SLUG to give each agent its own git worktree body when several agents develop the same repo in parallel. Do NOT trigger for study/exploration (use /learn), finding projects (use /trace), or session mining (use /dig).

- Skill: `soul-brews-studio/incubate` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add soul-brews-studio/incubate`
- Raw SKILL.md: https://api.skillmd.com/api/skills/soul-brews-studio/incubate/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: soul-brews-studio (https://skillmd.com/u/soul-brews-studio)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/soul-brews-studio/incubate

---


# /incubate — Active Development Workflow

Clone or create repos for active development → set up branches, make changes, push PRs.

> "/learn reads the book. /incubate writes the next chapter."

## Usage

```
/incubate [url]                          # Clone via ghq, symlink, ready for dev
/incubate [slug]                         # Use slug from ψ/memory/slugs.yaml
/incubate [repo-name]                    # Finds in ghq or creates with default org
/incubate [url] --flash "fix desc"       # Issue → branch → fix → PR → offload
/incubate [url] --contribute             # Fork if needed → branch per feature → PRs
/incubate --status                       # List all active ψ/incubate/ with git status
/incubate --status --include-offloaded   # Also list offloaded entries from .origins (#280)
/incubate --offload [slug]               # Remove symlink, keep ghq clone
/incubate --offload [slug] --purge       # Also drop entry from .origins manifest (#280)
/incubate --init                         # Restore all origins after git clone

# Parallel bodies — one git worktree per agent, same mother clone (#487)
/incubate [slug] --wt <name>             # Claim a body on branch incubate/<name>
/incubate [slug] --wt <name> --task "…"  # Same, recording who + why (worktree lock reason)
/incubate [slug] --wt <name> --from <ref># Branch from <ref> (default: origin/HEAD)
/incubate --offload [slug] --wt <name>   # Retire ONE body (branch survives)
/incubate --offload [slug] --all-wt      # Retire every body on this machine
```

---

## Workflow Modes

| Flag | Scope | Duration | Cleanup |
|------|-------|----------|---------|
| (default) | Long-term dev | Weeks/months | Manual offload |
| `--wt <slug>` | One agent's parallel body | Hours/days | `--offload --wt <slug>` (branch survives) |
| `--flash` | Single fix | Minutes | Issue → PR → auto-offload + purge |
| `--contribute` | Multi-feature | Days/weeks | Offload when all PRs done |
| `--status` | Query | — | Read-only listing |
| `--offload` | Cleanup | — | Remove symlink (keep ghq) |

```
incubate        → Long-term dev (manual cleanup)
    ↓
--wt <slug>     → N agents, N worktrees, 1 mother clone (parallel bodies)
    ↓
--contribute    → Push → offload (keep ghq)
    ↓
--flash         → Issue → Branch → PR → offload → purge (complete cycle)
```

**Mother and bodies (#487).** One `ghq` clone per repo per machine = one working
tree = one checked-out branch, so N agents on the same repo stomp each other.
`--wt` gives each agent a **body**: a `git worktree` on its own branch
`incubate/<slug>`, sharing the mother's object store. The mother clone is
untouched — on the `--wt` path it is **fetch-only**, never pulled, never stashed.

The claim is atomic for free: `git worktree add -b` creates the branch ref in a
single ref transaction, so of N agents racing for the same slug **exactly one
wins** and the losers touch nothing. No lockfiles, no `flock` (unreliable on NFS,
meaningless across machines), no claim directory.

---

## Directory Structure

```
ψ/incubate/
├── .origins                          # Manifest of incubated MOTHERS (committed)
└── OWNER/
    └── REPO/
        ├── origin                    # Symlink to mother ghq clone (gitignored)
        ├── REPO.md                   # Hub file — tracks incubation sessions (committed)
        └── wt/                       # Parallel bodies (#487)
            └── <slug>/
                └── origin            # Symlink to the worktree (gitignored)
```

Machine-local side (never in ψ, never committed):

```
$(ghq root)/github.com/OWNER/REPO   # mother clone — one per repo per machine
$WT_ROOT/OWNER/REPO/<slug>/         # body — branch incubate/<slug>

WT_ROOT="${INCUBATE_WT_ROOT:-${XDG_STATE_HOME:-$HOME/.local/state}/incubate/worktrees}"
```

**ψ is the committed soul; worktrees are machine-local scratch.** Bodies live
outside ψ **and** outside `$(ghq root)`. ψ gets one gitignored symlink per body
and nothing else.

**Bodies must not live under `$(ghq root)` (#487).** ghq decides "this is a
repository" by `stat`-ing `<dir>/.git`, and a linked worktree's `.git` is a
regular file that passes that test. Measured on ghq 1.10.1, with bodies under
`$(ghq root)/.worktrees/`:

```
$ ghq list
.worktrees/acme/api/fix-auth      ← phantom
github.com/acme/api
```

Every consumer of `ghq list` then sees phantom repos, and `.worktrees` sorts
*before* `github.com`, so any "first match wins" resolver returns the body
instead of the repo. That is a regression in **other** skills caused by this
one, so the body root lives in the XDG state directory. `$INCUBATE_WT_ROOT`
relocates it; any path outside `$(ghq root)` works, including a different
filesystem (E17).

**The body link filename is literally `origin` — this is load-bearing.** It means
the existing `.gitignore` rule `ψ/incubate/**/origin` already covers bodies (no
new rule), `wt/<slug>/` contains nothing git tracks (no `.gitkeep`), and the
`--status`/`--offload` finders keep working unmodified.

**There is no per-body manifest file.** The registry is
`git -C <mother> worktree list --porcelain -z`, which git maintains atomically
and which cannot drift from reality. A committed `.origins.d/<slug>.yaml` was
rejected (#487): committed files mean N agents each `git add`/commit/push into
ψ, converting a benign file race into a git non-fast-forward race one layer up.

Per-body metadata (who, when, why) rides in the **worktree lock reason** —
machine-local at `<mother>/.git/worktrees/<slug>/locked`, surviving `kill -9`,
never committed:

```
incubate|<owner-id>|<iso8601>|<task text>
```

**Read the registry with `--porcelain -z | tr '\0' '\n'`, never plain
`--porcelain` (#487).** git C-quotes the *entire* `locked` line the moment the
reason holds a non-ASCII byte or a `"`. Measured on git 2.50.1 with
`--task "แก้บั๊ก auth"`:

```
locked "incubate|nat@m5|2026-07-27T…|\340\271\201\340\270\201…"   ← --porcelain
locked incubate|nat@m5|2026-07-27T…|แก้บั๊ก auth                   ← --porcelain -z
```

Under the quoted form `awk -F'|'` sees field 1 as `"incubate`, so every
ownership guard silently misses, the body is misreported as another tool's, and
`--offload` refuses it forever. `-z` never quotes (it terminates records with
NUL, so there is nothing to escape) and `tr '\0' '\n'` restores the exact shape
every existing `awk` expects. Requires git ≥ 2.36.

Three reason classes, and only three:

| field 1 | means | do |
|---|---|---|
| `incubate` | ours | parse `who \| when \| task` |
| `initializing` | **git's own** lock, written during `worktree add` and left by a `kill -9` mid-checkout (E11) | report as crashed-mid-create, offer the heal, allow offload once unlocked |
| anything else | another tool's | report as `(locked by another tool)`, never parse, never break |

`initializing` is deliberately **not** in the "another tool" bucket — treating
it as foreign wedges the body, the slug, and the mother's offload permanently.
The full set of registry queries is in
[`references/worktree-recovery.md`](references/worktree-recovery.md).

**Offload source, keep hub:**
```bash
unlink ψ/incubate/OWNER/REPO/origin   # Remove symlink
# ghq clone preserved for future use
# Hub file (REPO.md) remains in ψ/incubate/OWNER/REPO/
```

---

## /incubate --init

Restore all origins after cloning (like `git submodule init`):

```bash
ROOT="$(pwd)"
GHQ_ROOT=$(ghq root)                     # hoisted — ~24ms per call (#487)
WT_ROOT="${INCUBATE_WT_ROOT:-${XDG_STATE_HOME:-$HOME/.local/state}/incubate/worktrees}"

# Read .origins through a de-duplicated private copy, NEVER the shared file
# directly (#487). The guarded append at Step 0 can leave a duplicate line
# (measured: 15/30 trials with 4 concurrent runs) and every reader must absorb
# it. Redirected, not piped — a pipe would run the loop in a subshell.
ORIGINS_SNAP=$(mktemp "${TMPDIR:-/tmp}/incubate-origins.XXXXXX")
sort -u "$ROOT/ψ/incubate/.origins" > "$ORIGINS_SNAP"

while IFS= read -r repo; do
  [ -z "$repo" ] && continue
  OWNER=$(dirname "$repo")
  REPO=$(basename "$repo")
  ghq get -u "https://github.com/$repo"
  MOTHER="$GHQ_ROOT/github.com/$repo"
  mkdir -p "$ROOT/ψ/incubate/$OWNER/$REPO"
  # ln -sfn, NEVER ln -sf: onto an EXISTING symlink-to-directory, `ln -sf`
  # follows the link and creates a stray link INSIDE the old target, leaving
  # the ψ link still pointing at the old path. Measured (#487).
  ln -sfn "$MOTHER" "$ROOT/ψ/incubate/$OWNER/$REPO/origin"
  echo "✓ Restored: $repo"

  # Body relink pass (#487). Re-points ψ at any worktrees this machine already
  # has. No-ops on a vault with zero bodies — `worktree list` reports only the
  # mother — so old vaults behave exactly as before.
  #   -z: mandatory, see "Directory Structure" — plain --porcelain C-quotes.
  #   sub(/^worktree /,"") + $0: `print $2` truncates at the first space, and
  #   `$(ghq root)` on a macOS home directory with a space in its name is
  #   ordinary. Measured: relinking `/…/my ghq root/…` produced `/…/my` (#487).
  git -C "$MOTHER" worktree list --porcelain -z 2>/dev/null | tr '\0' '\n' \
    | awk '/^worktree /{sub(/^worktree /,""); print $0}' | while IFS= read -r wt; do
      [ "$wt" = "$MOTHER" ] && continue
      s=$(basename "$wt")
      # Registered but gone from disk (E9) — relinking would manufacture a
      # DANGLING ψ symlink and report it as a restored body. Report instead.
      [ -d "$wt" ] || { echo "  ⋯ body wt/$s registered but gone from disk — /incubate --status"; continue; }
      mkdir -p "$ROOT/ψ/incubate/$OWNER/$REPO/wt/$s"
      ln -sfn "$wt" "$ROOT/ψ/incubate/$OWNER/$REPO/wt/$s/origin"
      echo "  ↳ relinked body wt/$s"
    done

  # Bodies that exist only on ANOTHER machine — report, never auto-materialize.
  # A vault with 20 recorded bodies would otherwise detonate 20 `worktree add`
  # calls on a fresh clone. The human opts in per body (#487).
  git -C "$MOTHER" ls-remote --heads origin 'incubate/*' 2>/dev/null \
    | sed 's|.*refs/heads/incubate/||' | while IFS= read -r s; do
        [ -z "$s" ] && continue
        [ -d "$WT_ROOT/$OWNER/$REPO/$s" ] \
          || echo "  ⋯ body '$s' lives on another machine — /incubate $OWNER/$REPO --wt $s"
      done
done < "$ORIGINS_SNAP"

rm -f "$ORIGINS_SNAP"
```

`.origins` keeps its exact format — flat, one `OWNER/REPO` per line, committed.
**Bodies are never recorded in it**, because `.origins` answers "which mothers
must `--init` re-clone" and a body's mother is already listed. No migration, no
dual-read, no version marker.

---

## Step 0: Detect Input Type + Resolve Path

**CRITICAL: Capture ABSOLUTE paths first:**
```bash
date "+🕐 %H:%M %Z (%A %d %B %Y)" && ROOT="$(pwd)"
echo "Incubating from: $ROOT"
```

### If URL (http* or owner/repo format)

Clone or create, symlink origin, update manifest:

```bash
# Replace [URL] with actual URL
URL="[URL]"
ROOT="$(pwd)"
OWNER=$(echo "$URL" | sed -E 's|.*github.com/([^/]+)/.*|\1|')
REPO=$(echo "$URL" | sed -E 's|.*/([^/]+)(\.git)?$|\1|')
SLUG="$OWNER/$REPO"

# Auto-stash unstaged changes in source clone before pulling (#279).
# `ghq get -u` runs `git pull` under the hood and aborts on dirty trees,
# stranding the ritual. Detect + stash with a clear log + restore hint.
GHQ_ROOT_PRECHECK=$(ghq root 2>/dev/null)
SOURCE_PRECHECK="$GHQ_ROOT_PRECHECK/github.com/$SLUG"
if [ -d "$SOURCE_PRECHECK/.git" ]; then
  if [ -n "$(git -C "$SOURCE_PRECHECK" status --porcelain 2>/dev/null)" ]; then
    STASH_NAME="pre-incubate-$(date +%Y-%m-%d)"
    echo "⚠️  Source clone has uncommitted changes — auto-stashing as '$STASH_NAME'"
    git -C "$SOURCE_PRECHECK" stash push -u -m "$STASH_NAME"
    echo "    (run \`git -C $SOURCE_PRECHECK stash pop\` to restore)"
  fi
fi

# Check if repo exists on GitHub
if gh repo view "$SLUG" --json name &>/dev/null; then
  ghq get -u "https://github.com/$SLUG"
else
  echo "Repo not found — creating private repo..."
  NEW_REPO_CREATED=1   # our own fresh repo — commit-ignore the breadcrumb (Step 0.5)
  gh repo create "$SLUG" --private --clone=false
  ghq get "https://github.com/$SLUG"
  GHQ_ROOT=$(ghq root)
  LOCAL="$GHQ_ROOT/github.com/$SLUG"
  # Seed a .gitignore that ignores the incubation breadcrumb from the very first
  # commit, so the rule travels with every clone (a local .git/info/exclude does
  # not). Safe here — WE just created this private repo; the #447 "don't edit a
  # foreign repo's committed .gitignore" rule applies only to CLONES.
  grep -qxF '.claude/INCUBATED_BY' "$LOCAL/.gitignore" 2>/dev/null \
    || echo '.claude/INCUBATED_BY' >> "$LOCAL/.gitignore"
  [ -f "$LOCAL/README.md" ] || echo "# $REPO" > "$LOCAL/README.md"
  git -C "$LOCAL" add README.md .gitignore
  git -C "$LOCAL" diff --cached --quiet || git -C "$LOCAL" commit -m "Initial commit"
  git -C "$LOCAL" push origin main 2>/dev/null || git -C "$LOCAL" push origin master
fi

GHQ_ROOT=$(ghq root)
mkdir -p "$ROOT/ψ/incubate/$OWNER/$REPO"
# ln -sfn, NEVER ln -sf (#487) — see the comment in --init above.
ln -sfn "$GHQ_ROOT/github.com/$OWNER/$REPO" "$ROOT/ψ/incubate/$OWNER/$REPO/origin"

# Auto-add gitignore pattern if missing (#250)
GITIGNORE="$ROOT/.gitignore"
if [ -f "$GITIGNORE" ]; then
  if ! grep -q 'ψ/incubate/\*\*/origin' "$GITIGNORE" 2>/dev/null; then
    echo 'ψ/incubate/**/origin' >> "$GITIGNORE"
    echo "✓ Added ψ/incubate/**/origin to .gitignore"
  fi
else
  # Also check ψ/.gitignore as fallback
  PSI_GITIGNORE="$ROOT/ψ/.gitignore"
  if [ -f "$PSI_GITIGNORE" ] && ! grep -q 'incubate/\*\*/origin' "$PSI_GITIGNORE" 2>/dev/null; then
    echo 'incubate/**/origin' >> "$PSI_GITIGNORE"
    echo "✓ Added incubate/**/origin to ψ/.gitignore"
  fi
fi

# Update manifest — guarded O_APPEND, never read-modify-write (#487).
# The `sort -u -o F F` that lived here re-read and rewrote the whole file:
# measured 8 concurrent writers x 10 trials -> 8/10 trials LOST entries (worst
# kept 5 of 8), and 5 trials emitted `sort: No such file or directory` because
# the file transiently does not exist — so a concurrent --init or --status read
# an EMPTY manifest. A guarded append measured 0/10 lost. Residual TOCTOU can
# only ever produce a DUPLICATE line, never a missing one, and every reader
# absorbs duplicates via `sort -u`. .origins is no longer kept sorted; nothing
# ever consumed its sortedness.
ORIGINS="$ROOT/ψ/incubate/.origins"
mkdir -p "$ROOT/ψ/incubate"
grep -qxF "$OWNER/$REPO" "$ORIGINS" 2>/dev/null || printf '%s\n' "$OWNER/$REPO" >> "$ORIGINS"

echo "✓ Ready: $ROOT/ψ/incubate/$OWNER/$REPO/origin → source"
```

### Step 0.5: Drop INCUBATED_BY Breadcrumb (#226, #228)

After clone/symlink, write `.claude/INCUBATED_BY` in the **target repo** (not the oracle repo):

```bash
TARGET_REPO="$GHQ_ROOT/github.com/$OWNER/$REPO"
mkdir -p "$TARGET_REPO/.claude"

# Check if this repo was previously /learn'd
LEARNED_FROM=""
if [ -d "$ROOT/ψ/learn/$OWNER/$REPO" ]; then
  LEARNED_FROM="learned-from: ψ/learn/$OWNER/$REPO/"
fi

cat > "$TARGET_REPO/.claude/INCUBATED_BY" << BREADCRUMB
oracle: $(basename "$ROOT")
oracle-repo: $(git -C "$ROOT" remote get-url origin 2>/dev/null || echo "local")
date: $(date +%Y-%m-%d)
mode: ${MODE:-default}
source: https://github.com/$OWNER/$REPO
${LEARNED_FROM}
BREADCRUMB

echo "✓ Breadcrumb dropped: $TARGET_REPO/.claude/INCUBATED_BY"

# Keep the breadcrumb OUT of the target repo's tracked history. Pick the mechanism
# by ownership:
# - NEW repo we just created ($NEW_REPO_CREATED): commit-ignore it in .gitignore so
#   the rule travels with clones. The initial commit above already staged it.
# - CLONE we don't own (may be public/foreign): use .git/info/exclude — local-only,
#   never committed, so we never edit someone else's committed .gitignore (#447).
if [ -d "$TARGET_REPO/.git" ]; then
  if [ "${NEW_REPO_CREATED:-0}" = "1" ]; then
    grep -qxF '.claude/INCUBATED_BY' "$TARGET_REPO/.gitignore" 2>/dev/null \
      || echo '.claude/INCUBATED_BY' >> "$TARGET_REPO/.gitignore"
    echo "✓ Ignored in git: .claude/INCUBATED_BY (committed .gitignore — new repo)"
  else
    grep -qxF '.claude/INCUBATED_BY' "$TARGET_REPO/.git/info/exclude" 2>/dev/null \
      || echo '.claude/INCUBATED_BY' >> "$TARGET_REPO/.git/info/exclude"
    echo "✓ Excluded from git: .claude/INCUBATED_BY (local .git/info/exclude)"
  fi

  # Either way: if a PRIOR incubation already committed the breadcrumb, an
  # exclude/ignore rule won't hide an already-tracked file. Auto-untrack it
  # (index-only; the file stays on disk) so "must be ignored" actually holds.
  if git -C "$TARGET_REPO" ls-files --error-unmatch .claude/INCUBATED_BY >/dev/null 2>&1; then
    git -C "$TARGET_REPO" rm --cached --quiet .claude/INCUBATED_BY
    echo "✓ Untracked previously-committed .claude/INCUBATED_BY (git rm --cached)"
  fi
fi
```

The breadcrumb enables:
- **Orphan detection**: Any Claude session can check who tracks this repo
- **Provenance chain**: `learned-from` links /learn → /incubate (#232)
- **/recap awareness**: /recap shows a warning when INCUBATED_BY exists (#229)

The breadcrumb stays out of the target repo's tracked history. Step 0.5 picks the
mechanism by ownership: a **committed `.gitignore` rule** for a repo we just created
(so the rule travels with every clone), or a machine-local **`.git/info/exclude`** for
a clone we don't own (#447 — never edit a foreign/public repo's committed .gitignore).
Either way it auto-untracks the file if a prior incubation already committed it
(`git rm --cached`, index-only — the breadcrumb stays on disk). Nothing manual is
left for you to remember.

### Step 0.6: Share the vault — symlink target ψ → parent oracle vault

An incubated repo that writes its own memory (retros, learnings, traces) into a
standalone `ψ/` strands that brain — the parent oracle never sees it. Point the
target's `ψ` at the parent vault so incubated work lands in one shared brain.

```bash
# Only if the parent actually has a vault to share
if [ -e "$ROOT/ψ" ]; then
  ln -sfn "$ROOT/ψ" "$TARGET_REPO/ψ"   # -n: don't descend into an existing symlink
  echo "✓ Vault shared: $TARGET_REPO/ψ → $ROOT/ψ"

  # Keep it OUT of the target's git history via LOCAL exclude (never the
  # committed .gitignore — target may be public; #447 rule).
  # GOTCHA (neo, 2026-08-16): a bare symlink `ψ` is NOT matched by `ψ/` — the
  # trailing slash only matches a directory. Exclude BOTH forms or the symlink
  # shows up as untracked.
  if [ -d "$TARGET_REPO/.git" ]; then
    for pat in 'ψ' 'ψ/'; do
      grep -qxF "$pat" "$TARGET_REPO/.git/info/exclude" 2>/dev/null \
        || echo "$pat" >> "$TARGET_REPO/.git/info/exclude"
    done
    echo "✓ Excluded from git: ψ and ψ/ (local .git/info/exclude)"
  fi
fi
```

**Consequence — the vault is now GLOBAL, not per-repo.** Once the target's `ψ`
symlinks into the parent, `incubate/`, `learn/`, and `memory/` are the same
directory on disk across every repo that shares that vault. `/incubate` or
`/learn` in one shows up for all of them. If the parent vault itself is a
symlink into a **private** companion repo (e.g. `neo-oracle/ψ → neo-oracle-vault`),
that's deliberate: it keeps memory out of open-source-bound repos while still
sharing one brain. Don't assume an incubated repo's memory is private to it.

### If just a name (no slash, no URL)

Try ghq first, then create with default org:

```bash
NAME="[NAME]"
ROOT="$(pwd)"
DEFAULT_ORG="laris-co"  # Configurable via --org flag

# Anchor to github.com/ (#487) — ghq enumerates dot-directories and non-GitHub
# hosts alike, so an unanchored match can select a backup tree, a gitlab clone,
# or any stray directory holding a `.git`. Measured on one machine: 54 dot-dir
# entries, 156 non-github.com. (Bodies deliberately live outside $(ghq root)
# entirely, so they never appear here — see "Directory Structure".)
MATCH=$(ghq list | grep '^github\.com/' | grep -i "/$NAME$" | head -1)
if [ -n "$MATCH" ]; then
  OWNER=$(echo "$MATCH" | cut -d'/' -f2)
  REPO=$(echo "$MATCH" | cut -d'/' -f3)
else
  OWNER="$DEFAULT_ORG"
  REPO="$NAME"
fi
# Then proceed with URL flow using OWNER/REPO
```

### Verify

```bash
ls -la "$ROOT/ψ/incubate/$OWNER/$REPO/"
```

---

## Step 1: Detect Workflow Mode

Check arguments for workflow flags:

| Argument | Mode | Action |
|----------|------|--------|
| (none) | Default | Clone + symlink + show status |
| `--wt <slug>` | Body | Claim `incubate/<slug>` + add worktree + symlink (**skip clone — the mother must already exist**) (#487) |
| `--flash` | Flash | Issue → branch → fix → PR → offload |
| `--contribute` | Contribute | Fork if needed → multi-feature PRs |
| `--status` | Status | List all incubations + bodies (skip clone) |
| `--offload` | Offload | Remove symlink (skip clone); `--wt`/`--all-wt` retire bodies |

> **Steps 0 and 0.5 do not run for `--wt`, `--status` or `--offload`.** Read the
> mode first, then jump straight to that mode's section. Running Step 0 on the
> `--wt` path would `git stash push -u` a *peer's* uncommitted work in the mother
> and then `ghq get -u` (a `git pull`) underneath them — the exact operation the
> `--wt` section forbids, printing the restore hint into the wrong transcript
> (#487). The `[ -d "$MOTHER/.git" ]` check at Step W2 is the entry gate.

`--wt` **always takes a value**. Bare `--wt` on create is an error — list the
existing slugs instead of guessing one. The branch is **always** `incubate/<slug>`,
never configurable.

**Calculate ACTUAL paths (replace variables with real values):**
```
REPO_DIR   = [ROOT]/ψ/incubate/[OWNER]/[REPO]/
SOURCE_DIR = [ROOT]/ψ/incubate/[OWNER]/[REPO]/origin/          ← symlink to mother
WORK_DIR   = [GHQ_ROOT]/github.com/[OWNER]/[REPO]/             ← mother working dir
BODY_LINK  = [ROOT]/ψ/incubate/[OWNER]/[REPO]/wt/[SLUG]/origin ← symlink to body
BODY_DIR   = [WT_ROOT]/[OWNER]/[REPO]/[SLUG]/                  ← agent working dir

WT_ROOT    = ${INCUBATE_WT_ROOT:-${XDG_STATE_HOME:-$HOME/.local/state}/incubate/worktrees}
```

⚠️ With `--wt`, **BODY_DIR is your working directory**, not WORK_DIR. The mother
is fetch-only.

⚠️ IMPORTANT: Always use literal paths. Never pass shell variables to subagents.

---

## Mode: Default (long-term dev)

After Step 0 (clone + symlink), the repo is ready for development.

**Verify working state:**
```bash
WORK_DIR="$ROOT/ψ/incubate/$OWNER/$REPO/origin"
echo "Branch: $(git -C "$WORK_DIR" branch --show-current)"
echo "Status: $(git -C "$WORK_DIR" status --short | wc -l) changed files"
echo "Remote: $(git -C "$WORK_DIR" remote get-url origin)"
echo "Last commit: $(git -C "$WORK_DIR" log --oneline -1)"
```

**Skip to Step 2** (create/update hub file).

---

## Mode: --wt (parallel bodies, one per agent) — #487

Use when **more than one agent develops the same repo at once**. Each agent gets
its own worktree on its own branch; they share the mother's object store and
never touch each other's files.

**Step 0 and Step 0.5 do NOT run on this path.** No clone, no `ghq get -u`, no
auto-stash — the mother must already exist, and the `[ -d "$MOTHER/.git" ]`
check at the top of W2 is the entry gate. Run plain `/incubate OWNER/REPO`
first if it does not.

### Step W1: Validate the slug

Two stages, both needed. `git check-ref-format` **accepts** `-lead` (which would
be read as an option by the next command) and `a/b` (which would nest inside
`wt/`), so the `case` guard is load-bearing — it runs first.

```bash
SLUG="[SLUG]"
case "$SLUG" in
  ''|-*|*/*|*[!a-zA-Z0-9._-]*)
    echo "✗ invalid slug '$SLUG' — [a-zA-Z0-9._-] only, no slashes, no leading dash"
    exit 2 ;;
esac
git check-ref-format --branch "incubate/$SLUG" >/dev/null 2>&1 \
  || { echo "✗ invalid slug '$SLUG'"; exit 2; }
```

### Step W2: Claim the body — and own it in the same command

The **only** atomic primitive is `git worktree add`. Branch creation is a ref
transaction (`O_EXCL` + `rename(2)` inside `.git`), so 8 concurrent adds of the
same slug yield exactly one `rc=0` and seven loud fatals — measured. `git worktree
add` also creates missing nested parent directories itself, so there is no `mkdir`
to race on either.

The claim and the ownership record must be **one** command, not two. With
`add -b` followed by a separate `worktree lock`, a body is observable *unowned*
for the ~10–20 ms between them, and a peer running `--offload --all-wt` in that
window finds an unlocked clean worktree, passes both ownership guards, and
retires it — measured, `✓ wt/<slug> retired` while the claiming agent was still
running. `git worktree add --lock --reason … -b …` closes the window; verified
on git 2.50.1.

```bash
ROOT="$(pwd)"
GHQ_ROOT=$(ghq root)
WT_ROOT="${INCUBATE_WT_ROOT:-${XDG_STATE_HOME:-$HOME/.local/state}/incubate/worktrees}"
MOTHER="$GHQ_ROOT/github.com/$OWNER/$REPO"
[ -d "$MOTHER/.git" ] || { echo "✗ no mother clone — run: /incubate $OWNER/$REPO"; exit 1; }

# -z is MANDATORY, never plain --porcelain (#487): git C-quotes the WHOLE
# `locked` line once the reason holds a non-ASCII byte or a `"`, and every
# `awk -F'|'` guard below then silently misses. See "Directory Structure".
wtlist() { git -C "$MOTHER" worktree list --porcelain -z 2>/dev/null | tr '\0' '\n'; }

# FETCH-ONLY mother (#487). NEVER `ghq get -u` on this path: it runs `git pull`,
# which is exactly why the auto-stash at Step 0 exists (#279) — and with N agents
# that stash silently pockets a PEER's uncommitted work, printing the restore
# hint into THIS agent's transcript where the peer will never see it.
# `git fetch` has no working tree to disturb. Measured: 8 concurrent fetches, 8x rc=0.
git -C "$MOTHER" fetch --prune origin >/dev/null 2>&1 || true

# Base the body on the remote default head — never on the mother's current
# checkout, or body #2 silently inherits body #1's work.
# FAIL CLOSED (#487): `symbolic-ref` exits 128 with EMPTY stdout when
# refs/remotes/origin/HEAD is absent, which is the case for every repo Step 0
# creates itself (`gh repo create` + `ghq get` of an empty repo never sets it,
# and neither does `fetch --prune`). The old `${FROM:+…}` then expanded to
# nothing and `worktree add -b` silently based the body on the mother's HEAD —
# measured: a body born on top of an unrelated `--flash` branch, no warning.
if [ -z "$FROM" ]; then
  FROM=$(git -C "$MOTHER" symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null)
fi
if [ -z "$FROM" ]; then
  git -C "$MOTHER" remote set-head origin -a >/dev/null 2>&1
  FROM=$(git -C "$MOTHER" symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null)
fi
if [ -z "$FROM" ]; then
  if git -C "$MOTHER" rev-parse --verify -q HEAD >/dev/null 2>&1; then
    echo "✗ cannot determine origin/HEAD on $OWNER/$REPO — refusing to guess"
    echo "  basing on the mother's checkout would inherit whatever it has checked out"
    echo "  → name the base: /incubate $OWNER/$REPO --wt $SLUG --from main"
    exit 1
  fi
  echo "ⓘ mother has no commits yet (E8) — body starts from its unborn HEAD"
fi

WT="$WT_ROOT/$OWNER/$REPO/$SLUG"
BODY="$ROOT/ψ/incubate/$OWNER/$REPO/wt/$SLUG"
ERR=$(mktemp "${TMPDIR:-/tmp}/incubate.XXXXXX")

# The ownership record is computed BEFORE the claim so `add --lock --reason`
# can write both at once. `|` is the delimiter and `"` is what makes git
# C-quote a reason, so neither survives the free-text field. Non-ASCII DOES
# survive — Thai tasks are first class, and `-z` reads them back exactly.
TASK=$(printf '%s' "${TASK:-(unstated)}" | tr '|\n"' '/  ')
OWNER_ID="${INCUBATE_AGENT:-$(id -un)@$(hostname -s)}"
REASON_NEW="incubate|$OWNER_ID|$(date -u +%Y-%m-%dT%H:%M:%SZ)|$TASK"

if wtlist | grep -qxF "worktree $WT" && [ -d "$WT" ]; then
  # Already mine (E4). Re-running the identical command is NOT an error.
  ACTION="↩ attached"
  rm -f "$ERR"
elif wtlist | grep -qxF "worktree $WT"; then
  # Registered but the directory is GONE (E9) — the 🧟 state. Testing only the
  # registry here used to fall through to W3, which symlinked ψ at a path that
  # does not exist and printed `cd <nothing>` as success (#487).
  echo "✗ wt/$SLUG is registered but its directory is gone: $WT"
  echo "  heal: git -C $MOTHER worktree unlock $WT; git -C $MOTHER worktree prune"
  echo "  then re-run: /incubate $OWNER/$REPO --wt $SLUG"
  echo "  (branch incubate/$SLUG and every commit on it are untouched)"
  rm -f "$ERR"; exit 1
elif [ -e "$WT" ] && [ -n "$(ls -A "$WT" 2>/dev/null)" ]; then
  # E5/E10: something occupies the path and git does not own it. This guard MUST
  # run before `add -b`: git creates the branch ref BEFORE it validates the path,
  # so a failed `-b` LEAVES `incubate/<slug>` behind and wedges the slug forever
  # — measured on git 2.50.1, `rc=128` yet `branch --list` shows it (#487).
  # An EMPTY directory is harmless crash residue (E6) and falls through.
  echo "✗ $WT already exists and git does not own it — nothing was touched"
  echo "  inspect it first (E10): ls -la $WT"
  rm -f "$ERR"; exit 1
elif git -C "$MOTHER" worktree add --lock --reason "$REASON_NEW" \
       -b "incubate/$SLUG" "$WT" ${FROM:+"$FROM"} >/dev/null 2>"$ERR"; then
  ACTION="🌱 created"
  rm -f "$ERR"
elif grep -q "already exists\|cannot lock ref\|already used by worktree" "$ERR"; then
  # Lost the race, or the branch outlived a previous body.
  # sub(/^worktree /,"")+$0, never $2 — `$2` truncates a path at its first space.
  HOLDER=$(wtlist | awk -v b="branch refs/heads/incubate/$SLUG" \
    '/^worktree /{sub(/^worktree /,""); p=$0; next} $0==b{print p; exit}')
  if [ -n "$HOLDER" ]; then
    # `f=0` on every `worktree` line and `exit` at the record boundary: without
    # them a holder carrying no `locked` line leaks the NEXT body's reason, and
    # the collision message names the wrong human and the wrong task (#487).
    REASON=$(wtlist | awk -v b="branch refs/heads/incubate/$SLUG" \
      '/^worktree /{f=0} $0==b{f=1} f&&/^locked /{sub(/^locked /,"");print;exit} f&&/^$/{exit}')
    WHO=$(printf '%s' "$REASON" | awk -F'|' '$1=="incubate"{print $2" · "$3}')
    WHY=$(printf '%s' "$REASON" | awk -F'|' '$1=="incubate"{print $4}')
    if [ "$REASON" = "initializing" ]; then
      WHO="(git's own lock — crashed mid-create, E11)"
      WHY="git -C $MOTHER worktree unlock $HOLDER, inspect, then retry"
    elif [ -n "$REASON" ] && [ -z "$WHO" ]; then
      WHO="(locked by another tool)"; WHY="—"
    fi
    echo "✗ Slug '$SLUG' is already claimed on $OWNER/$REPO"
    echo "    by:   ${WHO:-(unlocked)}"
    echo "    task: ${WHY:-(unstated)}"
    echo "    tree: $HOLDER"
    echo "  → join it:      cd $HOLDER"
    echo "  → or branch it: /incubate $OWNER/$REPO --wt ${SLUG}-2 --task \"...\""
    rm -f "$ERR"; exit 1
  fi
  # Branch exists but no worktree holds it — a previously offloaded body. Resume
  # it. Capture THIS attempt's stderr (2>"$ERR", never 2>&1): reporting the
  # stale `-b` error told operators the BRANCH was the blocker when the real
  # cause was the directory, and deleting the branch did not help (#487).
  if ! git -C "$MOTHER" worktree add --lock --reason "$REASON_NEW" \
         "$WT" "incubate/$SLUG" >/dev/null 2>"$ERR"; then
    cat "$ERR"; rm -f "$ERR"; exit 1
  fi
  ACTION="♻ revived"
  rm -f "$ERR"
else
  cat "$ERR"; rm -f "$ERR"; exit 1
fi
```

A slug collision is the **normal** outcome of N agents racing — it is a routing
decision, not a crash. That is why the message names the holder, the task, the
tree, and the two commands that resolve it.

### Step W3: Reconcile ownership, then link ψ

The lock reason is machine-local, it survives `kill -9`, it blocks an accidental
`worktree remove`, and it does **not** block commits inside the body. A lock
protects only its own entry — pruning unrelated worktrees still works (measured).

W2 already wrote it on the create and revive paths. W3 only handles the
`↩ attached` case, where a claim already exists and must **never** be
overwritten: re-attaching to your own body keeps the ORIGINAL owner and
timestamp, and a lock written by another tool is left completely alone.

```bash
wtlist() { git -C "$MOTHER" worktree list --porcelain -z 2>/dev/null | tr '\0' '\n'; }

EXISTING=$(wtlist | awk -v w="worktree $WT" \
  '$0==w{f=1;next} f&&/^locked /{sub(/^locked /,"");print;exit} f&&/^$/{exit}')

if [ -z "$EXISTING" ]; then
  # Unowned — a crash between an old `add` and its `lock`, or a peer's unlock.
  # Adopt it; do not leave a body nobody owns.
  git -C "$MOTHER" worktree lock --reason "$REASON_NEW" "$WT" 2>/dev/null
elif [ "$EXISTING" = "initializing" ]; then
  # git's OWN lock, left by a kill -9 mid-checkout (E11) — not a foreign tool.
  echo "⚠ wt/$SLUG was created but never finished (git's 'initializing' lock, E11)"
  echo "  the tree is half-populated. Heal, then re-run:"
  echo "    git -C $WT status"
  echo "    git -C $WT checkout -- ."
  echo "    git -C $MOTHER worktree unlock $WT"
  exit 1
else
  TASK=$(printf '%s' "$EXISTING" | awk -F'|' '$1=="incubate"{print $4}')
  TASK="${TASK:-(locked by another tool)}"
fi

mkdir -p "$BODY"
ln -sfn "$WT" "$BODY/origin"      # -sfn, NEVER -sf (see Anti-Patterns)

echo "${ACTION:-↩ attached} body wt/$SLUG → $WT"
echo "  branch: incubate/$SLUG · task: $TASK"
echo "  cd $WT"
```

`.origins` is **not** touched, and **Step 2 does not run for `--wt`** — the
mother is already recorded, the body's record *is* its lock reason, and no new
committed file is created anywhere. That is the whole point: N agents never
contend on the vault's git index.

### Step W4: The one discipline

The mother's `.git` is genuinely shared. Concurrent `fetch` is safe (measured), but
`git gc`, `git worktree prune`, and `git branch -D` reach **every** sibling body.

> **Your worktree, your branch. No git operation outside it.**

Cross-machine slugs are **not** coordinated: two agents on two machines can each
create `incubate/fix-auth` from different bases and only discover it at push time.
The namespace is per-mother, per-machine. Known limitation (#487).

---

## Mode: --flash (single-fix cycle)

Complete contribution cycle: Issue → Branch → Fix → PR → Offload.

### Step F1: Create Issue (document intent)
```bash
WORK_DIR="$ROOT/ψ/incubate/$OWNER/$REPO/origin"
# Compose issue title and description from user's intent
ISSUE_URL=$(gh issue create --repo "$OWNER/$REPO" --title "[TITLE]" --body "[DESCRIPTION]")
ISSUE_NUM=$(echo "$ISSUE_URL" | grep -oP '\d+$')
echo "Created: #$ISSUE_NUM"
```

### Step F2: Create Branch
```bash
BRANCH="issue-${ISSUE_NUM}-[short-description]"
git -C "$WORK_DIR" checkout -b "$BRANCH"
echo "Branch: $BRANCH"
```

### Step F3: Make Changes
Let the user describe what to fix. Make changes, then:
```bash
git -C "$WORK_DIR" add -A
git -C "$WORK_DIR" commit -m "[commit message]

Closes #$ISSUE_NUM"
git -C "$WORK_DIR" push -u origin "$BRANCH"
```

### Step F4: Create PR
```bash
PR_URL=$(gh pr create --repo "$OWNER/$REPO" \
  --title "[PR title]" \
  --body "$(cat <<'EOF'
## Summary
[what was fixed]

Closes #$ISSUE_NUM

---
**From**: [Oracle Name]
Rule 6: "Oracle Never Pretends to Be Human"
Written by an Oracle — AI speaking as itself.
EOF
)" --head "$BRANCH")
PR_NUM=$(echo "$PR_URL" | grep -oP '\d+$')
echo "PR: #$PR_NUM (closes #$ISSUE_NUM)"
```

### Step F5: Auto-offload + purge
```bash
cd "$ROOT"
MOTHER="$(ghq root)/github.com/$OWNER/$REPO"
wtlist() { git -C "$MOTHER" worktree list --porcelain -z 2>/dev/null | tr '\0' '\n'; }

# REFUSE to purge the mother while bodies live (#487). Every body's `.git` is a
# pointer file into the mother's admin dir, and the body's COMMITS live in the
# mother's object store — so deleting the mother destroys every unpushed commit
# on every `incubate/*` branch. Measured: after `rm -rf $MOTHER` and a fresh
# re-clone, `git cat-file -t <body-commit>` → `could not get object info`, and
# `branch --list 'incubate/*'` is empty. **This guard is the only protection
# there is.** Restoring the mother does NOT heal the bodies; only PUSHED
# branches survive it.
LIVE=$(wtlist | grep -c '^worktree ')
if [ "${LIVE:-0}" -gt 1 ]; then
  echo "✗ Refusing to purge $OWNER/$REPO — $((LIVE - 1)) body/bodies still live:"
  wtlist | awk '/^worktree /{sub(/^worktree /,""); print "    "$0}' | tail -n +2
  echo "  → retire them first: /incubate --offload $OWNER/$REPO --all-wt"
  exit 1
fi

# Unpushed work on a body branch dies with the mother even when no worktree
# holds it any more. Never purge over the top of it.
UNPUSHED=$(git -C "$MOTHER" log --oneline --branches='incubate/*' --not --remotes 2>/dev/null | head -5)
if [ -n "$UNPUSHED" ]; then
  echo "✗ Refusing to purge $OWNER/$REPO — unpushed commits on incubate/* branches:"
  printf '    %s\n' "$UNPUSHED"
  echo "  → push them first, or accept the loss explicitly by deleting $MOTHER by hand"
  exit 1
fi

unlink "$ROOT/ψ/incubate/$OWNER/$REPO/origin"
rmdir "$ROOT/ψ/incubate/$OWNER" 2>/dev/null

# Rename FIRST, then delete (#487). `rm -rf` on a large clone takes seconds, and
# a peer's `--wt` only checks `[ -d "$MOTHER/.git" ]` — anywhere in that window
# it passes, then creates a branch ref and a partial checkout inside a tree being
# deleted. `mv` is atomic and fails every peer's check instantly. Re-check the
# body count against the renamed path before the point of no return.
PURGING="$MOTHER.purging.$$"
mv "$MOTHER" "$PURGING"
LIVE=$(git -C "$PURGING" worktree list --porcelain -z 2>/dev/null | tr '\0' '\n' | grep -c '^worktree ')
if [ "${LIVE:-0}" -gt 1 ]; then
  mv "$PURGING" "$MOTHER"
  echo "✗ A body was claimed while purging — mother restored, nothing deleted"
  exit 1
fi
rm -rf "$PURGING"

# --flash owns the whole cycle, so it purges the manifest entry too. Same
# compare-and-swap as `--offload --purge`; see that section for why.
ORIGINS="$ROOT/ψ/incubate/.origins"
if [ -f "$ORIGINS" ]; then
  I=0
  while [ "$I" -lt 10 ]; do
    I=$((I + 1)); SIZE=$(wc -c < "$ORIGINS" | tr -d ' ')
    TMPFILE=$(mktemp "$ROOT/ψ/incubate/.origins.XXXXXX")
    grep -vxF "$OWNER/$REPO" "$ORIGINS" > "$TMPFILE" || true
    chmod 644 "$TMPFILE"
    if [ "$(wc -c < "$ORIGINS" | tr -d ' ')" = "$SIZE" ]; then
      mv "$TMPFILE" "$ORIGINS"; break
    fi
    rm -f "$TMPFILE"
  done
fi

echo "✓ Issue #$ISSUE_NUM → PR #$PR_NUM → Offloaded & Purged"
```

**Update hub file before offload** (Step 2), then offload.

---

## Mode: --contribute (multi-feature contribution)

For extended contribution over days/weeks. Forks if needed.

### Step C1: Fork if not your repo
```bash
WORK_DIR="$ROOT/ψ/incubate/$OWNER/$REPO/origin"
ME=$(gh api user --jq '.login')
if ! gh repo view "$OWNER/$REPO" --json viewerPermission --jq '.viewerPermission' | grep -qE 'ADMIN|MAINTAIN|WRITE'; then
  echo "No push access — forking..."
  gh repo fork "$OWNER/$REPO" --clone=false
  git -C "$WORK_DIR" remote add fork "https://github.com/$ME/$REPO.git"
  echo "Fork remote added. Push to 'fork' instead of 'origin'."
fi
```

### Step C2: Create feature branch
```bash
BRANCH="feat/[feature-name]"
git -C "$WORK_DIR" checkout -b "$BRANCH"
```

### Step C3: Work cycle (repeat per feature)
```bash
# ... make changes ...
git -C "$WORK_DIR" add -A
git -C "$WORK_DIR" commit -m "[commit message]"
REMOTE=$(git -C "$WORK_DIR" remote | grep fork || echo origin)
git -C "$WORK_DIR" push -u "$REMOTE" "$BRANCH"
gh pr create --repo "$OWNER/$REPO" \
  --title "[PR title]" \
  --body "[description]" \
  --head "$ME:$BRANCH"
```

### Step C4: Offload when all PRs done
```bash
unlink "$ROOT/ψ/incubate/$OWNER/$REPO/origin"
rmdir "$ROOT/ψ/incubate/$OWNER" 2>/dev/null
echo "✓ Offloaded (ghq kept for PR feedback)"
```

---

## Mode: --status (list incubations)

No clone needed. Lists every mother, every body under it, and everything that
needs attention. This is the only window a human has at 3am, so it names the
branch, the dirt, the owner, the task, and the absolute path.

Add `--include-offloaded` to also list entries in `.origins` whose symlinks
have been removed (#280) — surfaces the historical record without losing it.

State is carried by **glyph shape, never colour**: `●` dirty, `○` clean,
`⚠` needs attention, `🧟` registered-but-gone.

```bash
ROOT="$(pwd)"
INCLUDE_OFFLOADED="${1:-}"   # pass "--include-offloaded" to enable
GHQ_ROO

…(truncated)
