Build Px4 macOS
PX4/PX4-Autopilot
Build PX4 board firmware on macOS in the px4-dev Docker container, including git worktrees, and stage commit-labeled artifacts without flashing hardware.
Clone or create repos for active development — the right hand of /learn.
$ npx skills add Soul-Brews-Studio/arra-oracle-skills-cli --skill incubate -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install Soul-Brews-Studio/arra-oracle-skills-cli incubate --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/Soul-Brews-Studio/arra-oracle-skills-cli.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/incubate .claude/skills/incubate && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "incubate" agent skill from https://github.com/Soul-Brews-Studio/arra-oracle-skills-cli/tree/alpha/skills/incubate into .claude/skills/incubate/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "incubate", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/Soul-Brews-Studio/arra-oracle-skills-cli/tree/alpha/skills/incubateType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add Soul-Brews-Studio/arra-oracle-skills-cli --skill incubate -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install Soul-Brews-Studio/arra-oracle-skills-cli incubate --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Soul-Brews-Studio/arra-oracle-skills-cli.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/incubate .agents/skills/incubate && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "incubate" agent skill from https://github.com/Soul-Brews-Studio/arra-oracle-skills-cli/tree/alpha/skills/incubate into .agents/skills/incubate/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "incubate", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add Soul-Brews-Studio/arra-oracle-skills-cli --skill incubate -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install Soul-Brews-Studio/arra-oracle-skills-cli incubate --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Soul-Brews-Studio/arra-oracle-skills-cli.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/incubate .cursor/skills/incubate && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "incubate" agent skill from https://github.com/Soul-Brews-Studio/arra-oracle-skills-cli/tree/alpha/skills/incubate into .cursor/skills/incubate/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "incubate", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/Soul-Brews-Studio/arra-oracle-skills-cli.git --path skills/incubate--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add Soul-Brews-Studio/arra-oracle-skills-cli --skill incubate -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install Soul-Brews-Studio/arra-oracle-skills-cli incubate --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Soul-Brews-Studio/arra-oracle-skills-cli.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/incubate .gemini/skills/incubate && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "incubate" agent skill from https://github.com/Soul-Brews-Studio/arra-oracle-skills-cli/tree/alpha/skills/incubate into .gemini/skills/incubate/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "incubate", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install Soul-Brews-Studio/arra-oracle-skills-cli incubateInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add Soul-Brews-Studio/arra-oracle-skills-cli --skill incubate -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/Soul-Brews-Studio/arra-oracle-skills-cli.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/incubate .github/skills/incubate && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "incubate" agent skill from https://github.com/Soul-Brews-Studio/arra-oracle-skills-cli/tree/alpha/skills/incubate into .github/skills/incubate/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "incubate", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add Soul-Brews-Studio/arra-oracle-skills-cli --skill incubate -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install Soul-Brews-Studio/arra-oracle-skills-cli incubate --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Soul-Brews-Studio/arra-oracle-skills-cli.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/incubate .opencode/skills/incubate && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "incubate" agent skill from https://github.com/Soul-Brews-Studio/arra-oracle-skills-cli/tree/alpha/skills/incubate into .opencode/skills/incubate/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "incubate", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
incubateClone or create repos for active development — the right hand of /learn.
Incubate is an agent skill from Soul-Brews-Studio/arra-oracle-skills-cli. 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).
Its SKILL.md is about 18k tokens, which your agent loads only when the skill is triggered. The skill folder holds 2 other files, including reference files (for example `references/worktree-recovery.md`).
It sits in Development, covering Git worktrees. It works with Git. The repository describes itself as: Install Oracle skills to Claude Code, OpenCode, Cursor, and 12+ AI coding agents. The licence is MIT.
3 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 9feea09. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
gitghFrom the folder's file list and the shell code blocks in SKILL.md.
Hosts in commands or code, which the agent is likely to contact:
github.comFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Incubate loads about 18k tokens when it runs, and up to ~22k if it reads all its reference files. Until then it costs about 104 tokens; SKILL.md has 3,332 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from Soul-Brews-Studio/arra-oracle-skills-cli at commit 9feea09, republished under its MIT licence (© Soul-Brews-Studio). 3,332 words, ~18,411 tokens.
.claude/skills/incubate/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub.Clone or create repos for active development → set up branches, make changes, push PRs.
"/learn reads the book. /incubate writes the next chapter."
/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| 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.
ψ/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/apiEvery 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 -zUnder 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.
Offload source, keep hub:
unlink ψ/incubate/OWNER/REPO/origin # Remove symlink
# ghq clone preserved for future use
# Hub file (REPO.md) remains in ψ/incubate/OWNER/REPO/Restore all origins after cloning (like git submodule init):
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.
CRITICAL: Capture ABSOLUTE paths first:
date "+🕐 %H:%M %Z (%A %d %B %Y)" && ROOT="$(pwd)"
echo "Incubating from: $ROOT"Clone or create, symlink origin, update manifest:
# 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"After clone/symlink, write .claude/INCUBATED_BY in the target repo (not the oracle repo):
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
fiThe breadcrumb enables:
learned-from links /learn → /incubate (#232)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.
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.
# 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
fiConsequence — 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.
Try ghq first, then create with default org:
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/REPOls -la "$ROOT/ψ/incubate/$OWNER/$REPO/"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,--statusor--offload. Read the mode first, then jump straight to that mode's section. Running Step 0 on the--wtpath wouldgit stash push -ua peer's uncommitted work in the mother and thenghq get -u(agit pull) underneath them — the exact operation the--wtsection 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.
After Step 0 (clone + symlink), the repo is ready for development.
Verify working state:
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).
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.
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.
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; }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.
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
fiA 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.
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.
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.
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).
Complete contribution cycle: Issue → Branch → Fix → PR → Offload.
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"BRANCH="issue-${ISSUE_NUM}-[short-description]"
git -C "$WORK_DIR" checkout -b "$BRANCH"
echo "Branch: $BRANCH"Let the user describe what to fix. Make changes, then:
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"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)"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.
For extended contribution over days/weeks. Forks if needed.
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'."
fiBRANCH="feat/[feature-name]"
git -C "$WORK_DIR" checkout -b "$BRANCH"# ... 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"unlink "$ROOT/ψ/incubate/$OWNER/$REPO/origin"
rmdir "$ROOT/ψ/incubate/$OWNER" 2>/dev/null
echo "✓ Offloaded (ghq kept for PR feedback)"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.
ROOT="$(pwd)"
INCLUDE_OFFLOADED="${1:-}" # pass "--include-offloaded" to enable
GHQ_ROOT=$(ghq root 2>/dev/null) # hoisted out of every loop — ~24ms per call (#487)
WT_ROOT="${INCUBATE_WT_ROOT:-${XDG_STATE_HOME:-$HOME/.local/state}/incubate/worktrees}"
# -z is MANDATORY, never plain --porcelain (#487) — see "Directory Structure".
wtlist() { git -C "$1" worktree list --porcelain -z 2>/dev/null | tr '\0' '\n'; }
echo "🌱 Incubations"
# Stock /bin/bash on macOS is 3.2.57 and has NO associative arrays: `declare -A`
# there makes `${ACTIVE[acme/api]}` an ARITHMETIC subscript and aborts with
# `division by 0 (error token is "b")`, rc=1 — `--status --include-offloaded`
# was hard-broken on stock bash and only appeared to work under Homebrew bash.
# Sets now live in temp files queried with `grep -qxF` (#487).
LINKS=$(mktemp "${TMPDIR:-/tmp}/incubate-links.XXXXXX")
ACTIVE=$(mktemp "${TMPDIR:-/tmp}/incubate-active.XXXXXX")
PORC=$(mktemp "${TMPDIR:-/tmp}/incubate-porc.XXXXXX")
# `find | sort`, never `for x in $(find)` — that word-splits paths on spaces.
# Sorting full paths also makes every mother precede its own bodies, always:
# all strings sharing a prefix form one contiguous block under lexical sort.
find "$ROOT/ψ/incubate" -name origin -type l -print 2>/dev/null | sort > "$LINKS"
# ISO8601 → "2h ago"; BSD `date -j -f` first, then GNU `date -d`, else raw stamp.
incubate_age() {
[ -n "$1" ] || return 0
_e=$(date -j -u -f '%Y-%m-%dT%H:%M:%SZ' "$1" +%s 2>/dev/null \
|| date -u -d "$1" +%s 2>/dev/null) || { printf '%s' "$1"; return 0; }
_d=$(( $(date -u +%s) - _e ))
[ "$_d" -lt 0 ] && _d=0 # clocks drift across a fleet; never print "-7h ago"
if [ "$_d" -lt 3600 ]; then printf '%sm ago' "$((_d/60))"
elif [ "$_d" -lt 86400 ]; then printf '%sh ago' "$((_d/3600))"
else printf '%sd ago' "$((_d/86400))"; fi
}
REPOS=0; BODIES=0; LIVE=0; DIRTY=0; LASTBODY=""; PORC_FOR=""
# Redirected, NOT piped — a piped `while` runs in a subshell and loses counters.
while IFS= read -r link; do
DIR=$(dirname "$link")
SLUG=${DIR#"$ROOT/ψ/incubate/"} # pure shell — a `sed` here would treat
TARGET=$(readlink "$link") # regex metachars in $ROOT as syntax
case "$SLUG" in
*/wt/*)
# ---- body ----
NAME=${SLUG##*/wt/}
PARENT=${SLUG%%/wt/*}
# Derive the parent from the body's OWN path (#487). The loop used to
# carry $PORC forward from whichever mother rendered last, so a body whose
# mother has no ψ link was drawn as a child of an unrelated repo with its
# owner and task blanked — measured: acme/api's orphan body rendered under
# acme/api-gateway. Sorted full paths put a mother immediately before its
# own bodies, but only when the mother's link exists.
if [ "$PARENT" != "$PORC_FOR" ]; then
if [ -L "$ROOT/ψ/incubate/$PARENT/origin" ]; then
wtlist "$ROOT/ψ/incubate/$PARENT/origin" > "$PORC"
PORC_FOR="$PARENT"
else
BODIES=$((BODIES + 1))
echo " ⚠ orphan body wt/$NAME — mother $PARENT is not incubated"
echo " $TARGET"
echo " heal: /incubate $PARENT (or: /incubate --offload $PARENT --all-wt)"
continue
fi
fi
BODIES=$((BODIES + 1))
if [ "$link" = "$LASTBODY" ]; then TEE="└─"; CONT=" "; else TEE="├─"; CONT="│ "; fi
if [ -d "$TARGET" ]; then
LIVE=$((LIVE + 1))
BRANCH=$(git -C "$TARGET" branch --show-current 2>/dev/null)
N=$(git -C "$TARGET" status --short 2>/dev/null | wc -l | tr -d ' ')
if [ "$N" -gt 0 ]; then STATE="● dirty $N"; DIRTY=$((DIRTY + 1)); else STATE="○ clean"; fi
else
BRANCH="?"; STATE="⚠ missing"
fi
# Owner + task come from the worktree lock reason cached in $PORC.
REASON=$(awk -v w="worktree $TARGET" '$0==w{f=1;next} f&&/^locked /{sub(/^locked /,"");print;exit} f&&/^$/{exit}' "$PORC")
WHO=$(printf '%s' "$REASON" | awk -F'|' '$1=="incubate"{print $2}')
WHEN=$(printf '%s' "$REASON" | awk -F'|' '$1=="incubate"{print $3}')
TASK=$(printf '%s' "$REASON" | awk -F'|' '$1=="incubate"{print $4}')
if [ "$REASON" = "initializing" ]; then
# git's OWN lock (E11), not a foreign tool's — name it as such.
WHO="⚠ crashed mid-create — git's own lock (E11)"; TASK="(unfinished)"
elif [ -n "$REASON" ] && [ -z "$WHO" ]; then
WHO="(locked by another tool)"
fi
printf ' %s %-16s %-11s %-22s %s\n' "$TEE" "wt/$NAME" "$STATE" "${BRANCH:-?}" "\"${TASK:-(unstated)}\""
printf ' %s %-16s %s · %s · %s\n' "$CONT" "" "${WHO:-(unlocked)}" "$(incubate_age "$WHEN")" "$TARGET"
;;
*)
# ---- mother ----
REPOS=$((REPOS + 1))
printf '\n %-34s origin ' "$SLUG"
if [ -d "$TARGET" ]; then
BRANCH=$(git -C "$TARGET" branch --show-current 2>/dev/null)
N=$(git -C "$TARGET" status --short 2>/dev/null | wc -l | tr -d ' ')
if [ "$N" -gt 0 ]; then STATE="● dirty $N"; DIRTY=$((DIRTY + 1)); else STATE="○ clean"; fi
echo "✓ ${BRANCH:-(detached)} · $STATE · $TARGET"
wtlist "$TARGET" > "$PORC"
else
echo "✗ broken → $TARGET"
: > "$PORC"
fi
PORC_FOR="$SLUG"
printf '%s\n' "$SLUG" >> "$ACTIVE"
# index()==1 is a literal prefix test — `grep "^$DIR/wt/"` would read any
# regex metachar in the path as syntax.
LASTBODY=$(awk -v p="$DIR/wt/" 'index($0,p)==1' "$LINKS" | tail -1)
;;
esac
done < "$LINKS"
echo ""
# Orphans — REPORT ONLY, never removed (Rule 6: Nothing is Deleted).
while IFS= read -r rslug; do
M="$GHQ_ROOT/github.com/$rslug"
[ -d "$M/.git" ] || continue
wtlist "$M" > "$PORC"
# Registered but gone from disk. Do NOT grep for `prunable` alone: a LOCKED
# worktree is never labelled prunable and `git worktree prune` skips it — and
# /incubate locks every body, so that is the COMMON case, not the rare one
# (measured, #487). Test the directory instead, and emit the heal command that
# actually works for each case.
# sub(/^worktree /,"")+$0, never $2 — `$2` truncates a path at its first space,
# and then EVERY live body reads as gone-from-disk (measured on a ghq root
# containing a space, #487).
awk '/^worktree /{sub(/^worktree /,""); p=$0; k=0; next}
/^locked/{k=1; next}
/^$/{if(p!=""){print (k?"L":"U")"\t"p; p=""}; next}
END{if(p!="")print (k?"L":"U")"\t"p}' "$PORC" \
| while IFS="$(printf '\t')" read -r flag wtpath; do
[ "$wtpath" = "$M" ] && continue
[ -d "$wtpath" ] && continue
if [ "$flag" = "L" ]; then
echo " 🧟 $wtpath (registered, gone from disk, locked)"
echo " heal: git -C $M worktree unlock $wtpath && git -C $M worktree prune"
else
echo " 🧟 $wtpath (registered, gone from disk)"
echo " heal: git -C $M worktree prune"
fi
done
# Registered, on disk, but with NO ψ symlink — a crash between `worktree add`
# and `ln -sfn`. Invisible to the render loop above (which is driven by ψ
# links) and to both other orphan classes, yet it still blocks the mother
# offload, so --status used to contradict --offload about how many bodies
# exist (#487).
awk '/^worktree /{sub(/^worktree /,""); print $0}' "$PORC" | tail -n +2 \
| while IFS= read -r wtpath; do
[ -d "$wtpath" ] || continue
[ -L "$ROOT/ψ/incubate/$rslug/wt/$(basename "$wtpath")/origin" ] && continue
echo " ⚠ $wtpath (registered, no ψ link)"
echo " heal: /incubate $rslug --wt $(basename "$wtpath")"
done
# The invisible orphan: admin dir deleted, body kept. `worktree list` cannot
# see it at all, so it is found by set-difference against what IS listed.
for d in "$WT_ROOT/$rslug"/*; do
[ -d "$d" ] || continue
grep -qxF "worktree $d" "$PORC" || echo " ⚠ $d (on disk, unknown to git) — report only"
done
done < "$ACTIVE"
# Repos and bodies are counted SEPARATELY — the old single counter printed
# "Total: 3" for 1 repo + 2 bodies (#487).
echo ""
echo " $REPOS repo · $BODIES bodies ($LIVE live) · $DIRTY dirty"
# --include-offloaded: also list .origins entries with no live symlink (#280)
if [ "$INCLUDE_OFFLOADED" = "--include-offloaded" ] && [ -f "$ROOT/ψ/incubate/.origins" ]; then
echo ""
echo "📦 Offloaded (in .origins, no live symlink)"
echo ""
OFFLOADED_COUNT=0
# De-dup through a private snapshot, never read the shared file raw (#487).
# The guarded append can leave a duplicate line (measured: 15/30 trials, 4
# concurrent runs) and this loop used to list the repo twice and print
# `Offloaded: 3` for two repos — disagreeing with the counter below, which
# already did `sort -u`, in the same command. A `sort -u | while` PIPE would
# put OFFLOADED_COUNT in a subshell and lose it, hence the temp file.
OSNAP=$(mktemp "${TMPDIR:-/tmp}/incubate-origins.XXXXXX")
sort -u "$ROOT/ψ/incubate/.origins" > "$OSNAP"
while IFS= read -r slug; do
[ -z "$slug" ] && continue
if ! grep -qxF "$slug" "$ACTIVE"; then
if [ -d "$GHQ_ROOT/github.com/$slug" ]; then
echo " $slug (ghq preserved at $GHQ_ROOT/github.com/$slug)"
else
echo " $slug (ghq absent — fully purged)"
fi
OFFLOADED_COUNT=$((OFFLOADED_COUNT + 1))
fi
done < "$OSNAP"
rm -f "$OSNAP"
echo ""
echo " Offloaded: $OFFLOADED_COUNT"
elif [ -f "$ROOT/ψ/incubate/.origins" ]; then
# `sort -u` absorbs the duplicate lines a guarded append can produce (#487).
TOTAL_RECORDED=$(sort -u "$ROOT/ψ/incubate/.origins" | grep -cv '^$')
OFFLOADED_HIDDEN=$((TOTAL_RECORDED - REPOS))
if [ "$OFFLOADED_HIDDEN" -gt 0 ]; then
echo " ($OFFLOADED_HIDDEN offloaded — use --include-offloaded to view)"
fi
fi
rm -f "$LINKS" "$ACTIVE" "$PORC"Crash states, heal recipes, and the full error catalogue live in
references/worktree-recovery.md.
Done. No hub file update needed.
Remove symlink, keep ghq clone and hub file.
| Invocation | Retires |
|---|---|
--offload <repo> | the mother symlink — refuses while bodies live |
--offload <repo> --wt <slug> | one body; the branch always survives |
--offload <repo> --all-wt | every body on this machine, one outcome line each |
… --purge | additionally drops .origins entry / deletes a merged branch |
Add --purge to also remove the entry from .origins manifest (#280) —
useful when you want the offloaded slug to NOT count toward the total any more.
ROOT="$(pwd)"
GHQ_ROOT=$(ghq root)
WT_ROOT="${INCUBATE_WT_ROOT:-${XDG_STATE_HOME:-$HOME/.local/state}/incubate/worktrees}"
SLUG="[OWNER/REPO or REPO]"
PURGE="" # "--purge" — also drop from .origins / delete a merged branch
WT="" # bare slug for ONE body (e.g. "fix-auth"), or the literal "--all-wt".
# NOT "--wt fix-auth" — see the dispatch guard below.
# ---- Resolve the repo EXACTLY (#487) ----
# The old `grep -i "$SLUG" | head -1` picked by readdir order: with acme/api and
# acme/api-gateway both present, `--offload api` selected acme/api-gateway — the
# WRONG repo, which becomes destructive once bodies exist. Exact match wins,
# suffix match is the fallback, ties are reported instead of guessed. `/wt/` is
# excluded so a body can never be mistaken for a mother.
# NO `xargs -I` (#487): BSD xargs — macOS, the primary machine — aborts with
# `command line cannot be assembled, too long` on any input line ≥ 255 bytes
# (measured: 254 ok, 255 rc=1), which a deep ghq root reaches easily. GNU xargs
# has no such limit, so this failed only on macOS. Same `while read` shape the
# --status block already uses.
CANDS=$(find "$ROOT/ψ/incubate" -name origin -type l -print 2>/dev/null \
| while IFS= read -r l; do d=$(dirname "$l"); printf '%s\n' "${d#"$ROOT/ψ/incubate/"}"; done \
| grep -v '/wt/' | sort)
# Exact first, then a LITERAL suffix match. Not `grep -xE ".*/$SLUG"`: `.` is a
# regex wildcard there too, so `--offload api.js` could select `acme/apiXjs`.
SEL=$(printf '%s\n' "$CANDS" | grep -xF "$SLUG")
[ -z "$SEL" ] && SEL=$(printf '%s\n' "$CANDS" \
| awk -v s="/$SLUG" 'length($0)>length(s) && substr($0,length($0)-length(s)+1)==s')
if [ -z "$SEL" ]; then
echo "✗ Not found: $SLUG"
# QUOTED — unquoted $CANDS word-splits any path containing a space.
echo "Active incubations:"; printf ' %s\n' "$CANDS"
exit 1
fi
if [ "$(printf '%s\n' "$SEL" | grep -c .)" -gt 1 ]; then
echo "✗ Ambiguous '$SLUG' — name the owner too:"; printf ' %s\n' "$SEL"
exit 2
fi
OWNER=$(dirname "$SEL"); REPO=$(basename "$SEL")
MOTHER="$GHQ_ROOT/github.com/$SEL"
# -z is MANDATORY, never plain --porcelain (#487) — see "Directory Structure".
# Without it a Thai or quoted --task makes every guard below misread its own
# lock as a foreign tool's and refuse the body forever.
wtlist() { git -C "$MOTHER" worktree list --porcelain -z 2>/dev/null | tr '\0' '\n'; }
# ---- Retire ONE body. Returns non-zero when it refuses; never destroys. ----
offload_body() {
_s="$1"
_wt="$WT_ROOT/$OWNER/$REPO/$_s"
_link="$ROOT/ψ/incubate/$OWNER/$REPO/wt/$_s"
if [ ! -d "$_wt" ]; then
echo " ⓘ wt/$_s — no directory on this machine"
if wtlist | grep -qxF "worktree $_wt"; then
echo " still registered — heal: git -C $MOTHER worktree unlock $_wt; git -C $MOTHER worktree prune"
return 1
fi
return 0
fi
if ! wtlist | grep -qxF "worktree $_wt"; then
echo " ⚠ wt/$_s — on disk but unknown to git; report only, not touched"; return 1
fi
_r=$(wtlist | awk -v w="worktree $_wt" \
'$0==w{f=1;next} f&&/^locked /{sub(/^locked /,"");print;exit} f&&/^$/{exit}')
_by=$(printf '%s' "$_r" | awk -F'|' '$1=="incubate"{print $2}')
_me="${INCUBATE_AGENT:-$(id -un)@$(hostname -s)}"
if [ -n "$_by" ] && [ "$_by" != "$_me" ]; then
echo " ✗ wt/$_s — held by $_by, refusing (their work, their call)"; return 1
fi
if [ "$_r" = "initializing" ]; then
# git's OWN lock, written during `worktree add` and left by a kill -9
# mid-checkout (E11). Classifying it as "another tool" wedged the body, the
# slug AND the mother offload permanently, with no --force to escape (#487).
echo " ⚠ wt/$_s — crashed mid-create (git's own 'initializing' lock, E11)"
echo " git -C $_wt status # see what landed"
echo " git -C $MOTHER worktree unlock $_wt"
echo " then re-run this offload"
return 1
fi
if [ -n "$_r" ] && [ -z "$_by" ]; then
echo " ✗ wt/$_s — locked by another tool, refusing"; return 1
fi
# Probe for dirt BEFORE unlocking (#487). `unlock` then a refused `remove`
# left the body in place with its owner/timestamp/task erased — the only
# ownership metadata the design has — so `--status` showed `(unlocked)`, a
# peer could re-claim it, and the very next `--all-wt` would delete work that
# had just been reported as "skipped, nothing was destroyed".
if [ -n "$(git -C "$_wt" status --porcelain 2>/dev/null)" ]; then
echo " ✗ wt/$_s — uncommitted work, NOT removed (still locked). Save it first:"
echo " cd $_wt && git add -A && git commit -m 'wip' && git push -u origin incubate/$_s"
return 1
fi
git -C "$MOTHER" worktree unlock "$_wt" 2>/dev/null || true
# NO --force. EVER. Plain `remove` fails closed on modified AND untracked-only
# trees and leaves the body intact — the one place Rule 6 matters most (#487).
if ! git -C "$MOTHER" worktree remove "$_wt" 2>/dev/null; then
# Lost a race with the body's own agent, or a submodule/permission problem.
# Restore the lock we took off: a refusal must leave NOTHING changed.
[ -n "$_r" ] && git -C "$MOTHER" worktree lock --reason "$_r" "$_wt" 2>/dev/null
echo " ✗ wt/$_s — could not be removed, NOT touched. Save any work first:"
echo " cd $_wt && git add -A && git commit -m 'wip' && git push -u origin incubate/$_s"
return 1
fi
unlink "$_link/origin" 2>/dev/null
rmdir "$_link" 2>/dev/null
rmdir "$ROOT/ψ/incubate/$OWNER/$REPO/wt" 2>/dev/null
echo " ✓ wt/$_s retired — branch incubate/$_s survives"
if [ "$PURGE" = "--purge" ]; then
# -d only, NEVER -D: -d refuses to delete an unmerged branch.
if git -C "$MOTHER" branch -d "incubate/$_s" 2>/dev/null; then
echo " ✂ branch incubate/$_s deleted"
else
echo " ⓘ branch incubate/$_s kept (unmerged — branch -d refuses, and we never -D)"
fi
fi
return 0
}
# ---- Dispatch ----
if [ "$WT" = "--all-wt" ]; then
# Collect outcomes, never abort the batch: with N agents, some bodies
# legitimately belong to live peers. Exit 1 if anything was skipped.
#
# The slug list is the UNION of the git registry and the disk (#487). Driving
# it from the disk glob alone made a registered-but-gone body invisible here
# while the mother guard below — which counts the REGISTRY — still saw it, so
# the refusal pointed at the command that had just silently done nothing:
# `--all-wt` rc=0 printing only its header, then `--offload` rc=1 forever.
# offload_body already handles both halves correctly once it is reached.
echo "🧹 Retiring all bodies of $SEL"
RC=0
SLUGS=$(mktemp "${TMPDIR:-/tmp}/incubate-slugs.XXXXXX")
{ wtlist | awk '/^worktree /{sub(/^worktree /,""); print $0}' | tail -n +2
for d in "$WT_ROOT/$OWNER/$REPO"/*; do [ -e "$d" ] && printf '%s\n' "$d"; done
} | while IFS= read -r p; do [ -n "$p" ] && basename "$p"; done | sort -u > "$SLUGS"
while IFS= read -r s; do
[ -z "$s" ] && continue
offload_body "$s" || RC=1
done < "$SLUGS"
rm -f "$SLUGS"
[ "$RC" -eq 0 ] || echo " ⚠ some bodies were skipped — nothing was destroyed"
exit $RC
elif [ -n "$WT" ]; then
# A flag-shaped value means the caller filled $WT the way the old comment read
# ("--wt fix-auth"). That used to fall through to a bare-slug lookup, print
# `ⓘ wt/--wt fix-auth — no directory` and exit 0 — the one silent no-op in the
# whole offload path (#487).
case "$WT" in -*) echo "✗ WT must be a bare slug (e.g. fix-auth), not a flag"; exit 2 ;; esac
offload_body "$WT"; exit $?
fi
# ---- Mother offload: refuse while bodies live (#487) ----
LIVE=$(wtlist | grep -c '^worktree ')
if [ "${LIVE:-0}" -gt 1 ]; then
echo "✗ $SEL still has $((LIVE - 1)) live body/bodies:"
wtlist | awk '/^worktree /{sub(/^worktree /,""); print " "$0}' | tail -n +2
echo " → retire them first: /incubate --offload $SEL --all-wt"
echo " (it retires what it can and names the exact heal for anything it will not touch)"
exit 1
fi
unlink "$ROOT/ψ/incubate/$SEL/origin"
rmdir "$ROOT/ψ/incubate/$OWNER" 2>/dev/null
echo "✓ Offloaded: $SEL"
echo " Hub file remains: $ROOT/ψ/incubate/$SEL/$REPO.md"
echo " ghq clone preserved for future use"
# --purge: remove from .origins manifest (#280)
if [ "$PURGE" = "--purge" ] && [ -f "$ROOT/ψ/incubate/.origins" ]; then
# RACE A, second half (#487). Step 0's append is guarded and safe, but THIS is
# still a read-modify-write of the same shared committed file, and `mv` only
# makes the swap atomic for READERS — it does nothing about lost UPDATES.
# Measured 20/20 trials: one purge against 8 concurrent appends, survivors
# ranged 0/8 to 6/8. A repo whose clone, symlink and hub file all exist
# vanished from the manifest, so --init never restored it again.
#
# Compare-and-swap: if a byte landed while we were rewriting, throw the
# rewrite away and start over; after 10 attempts give up rather than lose an
# entry. This narrows the window from the whole rewrite to a single syscall
# — it does not erase it, which is one more reason Principle 1 prefers
# --include-offloaded over --purge.
#
# mktemp in the DESTINATION directory, not $TMPDIR: $TMPDIR is often a
# different filesystem, where `mv` is copy+unlink rather than an atomic rename,
# and mktemp's 0600 mode would then land on a tracked file.
ORIGINS="$ROOT/ψ/incubate/.origins"
PURGED=""; I=0
while [ "$I" -lt 10 ]; do
I=$((I + 1))
SIZE=$(wc -c < "$ORIGINS" | tr -d ' ')
TMPFILE=$(mktemp "$ROOT/ψ/incubate/.origins.XXXXXX")
# -xF, NEVER "^${SEL}$": `.` is a regex wildcard, so purging `acme/api.js`
# would also delete `acme/apiXjs`.
grep -vxF "$SEL" "$ORIGINS" > "$TMPFILE" || true
chmod 644 "$TMPFILE"
if [ "$(wc -c < "$ORIGINS" | tr -d ' ')" = "$SIZE" ]; then
mv "$TMPFILE" "$ORIGINS"; PURGED=1; break
fi
rm -f "$TMPFILE"
done
if [ -n "$PURGED" ]; then
echo " ✂ Purged '$SEL' from .origins manifest"
echo " ⚠ This breadcrumb is gone — re-incubate to restore it (Principle 1: prefer --include-offloaded over --purge)"
else
echo " ⓘ .origins is being appended to concurrently — purge skipped, nothing lost"
echo " (the symlink is already gone; re-run --offload $SEL --purge when the fleet is idle)"
fi
fi--all-wt is not atomic and cannot be without a journal — but every step is
idempotent, so re-running after a partial failure is safe. Every refusal path
also restores whatever it touched, so a re-run starts from the same state.
Runs for default, --flash and --contribute only. It does not run for
--status, --offload, or --wt.
--wt is excluded deliberately (#487): REPO.md is a single committed file
that every body would append to, so N agents claiming bodies in the same minute
each commit and push the vault, two of three pushes are rejected
non-fast-forward, and the rebase collides in one hunk and drops a session
record. That is the ψ-git contention the --wt design exists to eliminate —
worse than the .origins.d/<slug>.yaml this file already rejects, because
.origins.d was at least one file per body. A body's record is its worktree
lock reason, not a hub-file entry.
For the modes that do run it: create or update the hub file:
# [REPO] Incubation Log
## Source
- **Origin**: ./origin/
- **GitHub**: https://github.com/OWNER/REPO
## Sessions
### [TODAY] — [mode]
- **Branch**: [branch-name]
- **Status**: active | offloaded | flash-completed
- **Changes**: [summary of what was done]
- **PRs**: #N, #M (if any)Append new sessions. Never overwrite existing entries (Nothing is Deleted).
announce-mode → bash substitutes $ROOT/$OWNER/$REPO/$WORK_DIR to absolute paths; never print "Location: ψ/..." literally. See CONVENTIONS.md.
echo "🌱 Incubating: $REPO"
echo ""
echo " Mode: default (long-term dev)"
echo " Location: $ROOT/ψ/incubate/$OWNER/$REPO/"
echo " Working: $WORK_DIR"
echo " Branch: $(git -C "$WORK_DIR" branch --show-current)"
echo " Remote: $(git -C "$WORK_DIR" remote get-url origin)"
echo " Status: $(git -C "$WORK_DIR" status --short | wc -l) changed files"
echo ""
echo " Next: make changes, commit, push, create PR"
echo " Done: /incubate --offload $OWNER/$REPO"announce-mode → bash substitutes; the body path is always absolute.
echo "🌱 Body claimed: $OWNER/$REPO wt/$SLUG"
echo ""
echo " Branch: incubate/$SLUG"
echo " Working: $WT"
echo " Task: $TASK"
echo " Owner: ${OWNER_ID:-${INCUBATE_AGENT:-$(id -un)@$(hostname -s)}}"
echo " Mother: $MOTHER (fetch-only — never pull it from here)"
echo ""
echo " Next: cd $WT — your worktree, your branch, no git op outside it"
echo " Done: /incubate --offload $OWNER/$REPO --wt $SLUG"⚡ Flash Complete: [REPO]
Issue: #N created
Branch: issue-N-description
PR: #M (closes #N)
Result: Offloaded & Purged
✓ Issue #N → PR #M → Doneannounce-mode → bash substitutes; never print "Location: ψ/..." literally.
echo "🤝 Contributing: $REPO"
echo ""
echo " Location: $ROOT/ψ/incubate/$OWNER/$REPO/"
echo " Fork: ${FORK_URL:-[none]}"
echo " Branches: ${BRANCHES:-[list]}"
echo " PRs: ${PRS:-[list]}"
echo ""
echo " Next: continue working, or /incubate --offload when done"The actual emission lives in the bash block under Mode: --status above — every
path is resolved via readlink, so all of them are absolute (CONVENTIONS.md).
Ordering is stable (repo, then slug, lexical) so two runs diff cleanly.
Example rendering:
🌱 Incubations
acme/hermes-gateway origin ✓ main · ○ clean · /opt/Code/github.com/acme/hermes-gateway
├─ wt/fix-auth ● dirty 3 incubate/fix-auth "restore session cookie on 401 retry"
│ nat@m5 · 2h ago · ~/.local/state/incubate/worktrees/acme/hermes-gateway/fix-auth
└─ wt/rate-limit ○ clean incubate/rate-limit "add token bucket to /v1/send"
nat@m5 · 20m ago · ~/.local/state/incubate/worktrees/acme/hermes-gateway/rate-limit
🧟 …/worktrees/acme/hermes-gateway/old (registered, gone from disk) — heal: git worktree prune
⚠ …/worktrees/acme/hermes-gateway/half (registered, no ψ link) — heal: /incubate acme/hermes-gateway --wt half
⚠ …/worktrees/acme/hermes-gateway/lost (on disk, unknown to git) — report only
1 repo · 2 bodies (2 live) · 1 dirtyFour states are reported and none is auto-removed (Rule 6): live bodies in
the tree, 🧟 registered-but-gone, ⚠ registered-with-no-ψ-link, and ⚠ on
disk but unknown to git. A body that is registered but has no ψ symlink is the
crash residue of dying between worktree add and ln -sfn; it must be listed
because it still blocks the mother offload, and --status used to be the one
command that could not see it (#487).
A mother with no bodies prints one line and nothing else. Repos and bodies are
counted separately — the old single counter reported Total: 3 for 1 repo with
2 bodies (#487).
The pattern is auto-added to .gitignore on first /incubate run (#250). If you need to add it manually:
# Ignore origin symlinks only (source lives in ghq)
# Note: no trailing slash — origin is a symlink, not a directory
ψ/incubate/**/originBodies need no new rule (#487). Because every body link is also named
origin, ψ/incubate/OWNER/REPO/wt/<slug>/origin is already matched by the
** in the existing pattern — confirmed with git check-ignore -v. Naming the
link after the slug instead would leave it untracked and unignored, which is
exactly why the filename is fixed. Do not add a wt/ rule.
Since the only thing inside wt/<slug>/ is that ignored symlink, git never sees
the directory at all — no .gitkeep, nothing committed, nothing to conflict on
when N agents work at once.
After incubation work, log to Oracle so it's discoverable via /trace:
Write to ψ/memory/learnings/YYYY-MM-DD_incubate-<slug>.md with frontmatter:
---
pattern: "Incubated [REPO]: [what was done — PR#, branch, outcome]"
date: <today>
source: incubate: OWNER/REPO
concepts: ["incubate", "development", <relevant-tags>]
---
# Incubated [REPO]
<body: what was done, PR#, branch, outcome>The Oracle's auto-memory layer picks up new files in ψ/memory/learnings/ automatically — no separate API call needed.
This connects /incubate to the shared knowledge layer.
| Wrong | Right |
|---|---|
git clone directly to ψ/ | ghq get then symlink |
Flat: ψ/incubate/repo-name | Org structure: ψ/incubate/owner/repo |
| Copy files | Symlink always |
| Manual clone outside ghq | Everything through ghq |
| Delete ghq clone after work | Offload symlink only (Nothing is Deleted) |
| Second clone of the same repo for a second agent | One mother, N worktrees (--wt) |
ln -sf onto an existing link | ln -sfn — -sf leaves the old target and drops a stray link inside it |
sort -u -o F F on a shared file | Guarded append — read-modify-write loses concurrent updates |
Rewriting .origins in place (grep -v … | mv) | Compare-and-swap on its byte size, and give up rather than lose a peer's append |
grep -v "^$SEL$" to drop a manifest line | grep -vxF "$SEL" — . is a regex wildcard, so acme/api.js also matches acme/apiXjs |
mkdir/flock/.lock dirs to claim a slug | git worktree add -b — the ref transaction is the claim |
worktree add -b then a separate worktree lock | worktree add --lock --reason … — two commands leave the body observable unowned |
worktree add -b onto an existing path | Guard with [ -e ] first — git creates the branch before validating the path and leaks it on failure |
git worktree list --porcelain for parsing | --porcelain -z | tr '\0' '\n' — plain porcelain C-quotes any non-ASCII or quoted lock reason |
awk '/^worktree /{print $2}' | sub(/^worktree /,""); print $0 — $2 truncates at the first space |
Treating locked initializing as a foreign tool | It is git's own lock (E11) — report the crash, offer the unlock |
worktree unlock before checking for dirt | Probe status --porcelain first — a refused offload must not drop the ownership record |
git worktree remove --force | Plain remove — it fails closed on dirty and untracked-only trees |
git branch -D incubate/<slug> | -d only, and only under --purge |
ghq get -u on the mother while bodies exist | git fetch --prune — pulling stashes a peer's uncommitted work |
rm -rf the mother while bodies live | Refuse, then mv aside before deleting — bodies' commits live in the mother's object store and do not come back |
Bodies under $(ghq root) | Outside it — ghq reads a worktree's .git file as a repo and lists phantoms |
xargs -I{} over a list of paths | while IFS= read -r — BSD xargs dies on any line ≥ 255 bytes |
git gc / worktree prune / branch -D from inside a body | Your worktree, your branch — no git op outside it |
Committing a per-body manifest — or a REPO.md session block — per body | git worktree list --porcelain -z + the lock reason (no ψ git race) |
declare -A in a skill's bash | Sorted temp file + grep -qxF — stock macOS bash is 3.2 |
--wt <slug>: one git worktree body per agent on the shared mother clone (#487)--flash: complete cycle (issue → branch → PR → offload + purge) for quick fixes--contribute: fork-aware multi-feature workflow for external repos--status: query all active incubations and their bodies without cloning--offload: remove symlink, keep ghq and hub file; --wt/--all-wt retire bodiesorigin/ symlink structure allows easy offload without losing ghq clone.origins manifest enables --init restore after fresh git clone/learn: learn = LEFT hand (study), incubate = RIGHT hand (work)git worktree + POSIX shell only. No maw, no fleet, no extra
toolchain — --wt works on a fresh machine with git and ghq alone.
Needs git ≥ 2.36 for worktree list --porcelain -z..origins line, no REPO.md session block (Step 2 does not run
for --wt) — so N agents never contend on the vault's git index..origins format is unchanged. A vault written by the previous /incubate
reads identically here, and vice versa. No migration, mandatory or otherwise.--wt gives the old behaviour, minus four measured bugs
(lost .origins entries, ln -sf mis-linking, the bash-3.2 --status abort,
and --offload selecting the wrong repo).$(ghq root) — $INCUBATE_WT_ROOT, defaulting to
${XDG_STATE_HOME:-$HOME/.local/state}/incubate/worktrees — because ghq reads
a linked worktree's .git file as a repository and would list every body as a
phantom entry to every other skill on the shelf.incubate/fix-auth; the conflict only surfaces at
push time.--purge on .origins is compare-and-swap, not atomic.
It gives up rather than lose a concurrent append, so the worst case is a
breadcrumb that stays instead of a breadcrumb that vanishes.--flash --purge refuses over live bodies and over
unpushed incubate/* commits. Push early.INCUBATE_AGENT overrides the owner id. Without it the fallback is
$(id -un)@$(hostname -s), which is identical for N agents on one host — so
"is this my body?" degrades to "is this my host?".references/worktree-recovery.md.ARGUMENTS: $ARGUMENTS
© Soul-Brews-Studio, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 1 other file (references) in skills/incubate of Soul-Brews-Studio/arra-oracle-skills-cli.
Open the folder on GitHubat commit 9feea09
Incubate next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Incubate this skillSoul-Brews-Studio/arra-oracle-skills-cli | 123 | — | ~18k | Automated safety check: Pass | MIT | |
| Build Px4 macOSPX4/PX4-Autopilot | 13k | — | ~1.1k | Automated safety check: Pass | BSD-3-Clause | |
| Git Worktree IsolationjnMetaCode/superpowers-zh | 8.3k | 1 repos | ~982 | Automated safety check: Pass | MIT | |
| WorktreeAgentsMesh/AgentsMesh | 2.4k | — | ~553 | Automated safety check: Notes | Custom licence | |
| Cursor Composer Task DelegateChachamaru127/claude-code-harness | 3.2k | — | ~4.4k | Automated safety check: Notes | MIT | |
| Git Worktree Setupk8zdev/k8z | 155 | — | ~930 | Automated safety check: Pass | None |
PX4/PX4-Autopilot
Build PX4 board firmware on macOS in the px4-dev Docker container, including git worktrees, and stage commit-labeled artifacts without flashing hardware.
jnMetaCode/superpowers-zh
Sets up an isolated workspace before feature work or plan execution, preferring native worktree tools and falling back to git worktree, with instructions in Chinese.
AgentsMesh/AgentsMesh
Creates or reuses an isolated AgentsMesh Git worktree from a verified base branch, preserves existing changes, and optionally starts the worktree-scoped development environment.
Chachamaru127/claude-code-harness
Hands one implementation task to Cursor Composer in an isolated git worktree, then reviews its diff and cherry-picks the result into the main branch.
k8zdev/k8z
Automate git worktree creation and copy private files (not tracked in git) to new workdirs.
michaelshimeles/skills
Start a new task in an isolated Git worktree branched from origin/main so multiple agents can work on the same repo in parallel without conflicts.
Soul-Brews-Studio/arra-oracle-skills-cli
Mine Claude Code sessions — timeline, gaps, repo attribution, session history.
Soul-Brews-Studio/arra-oracle-skills-cli
Hand off the current session to the next one. An agent skill from Soul-Brews-Studio/arra-oracle-skills-cli.
Soul-Brews-Studio/arra-oracle-skills-cli
Clone and track external repos. An agent skill from Soul-Brews-Studio/arra-oracle-skills-cli.
Soul-Brews-Studio/arra-oracle-skills-cli
Convene a Round Table (โต๊ะกลม): 3–8 souls — persona files, Oracles, or real people's DNA — debate ONE stakes-bearing decision over exactly 4 rounds (OPEN, DEEPEN, CONVERGE, VOTE), one subagent per…
Soul-Brews-Studio/arra-oracle-skills-cli
Bring an Oracle into the current repo — its soul, ALL its skills and its expertise — by link, never by copy.
Soul-Brews-Studio/arra-oracle-skills-cli
Find projects, code, and knowledge across git history, repos, docs, and Oracle.
Works with
Categories
Clone or create repos for active development — the right hand of /learn. Incubate is an agent skill from Soul-Brews-Studio/arra-oracle-skills-cli. Clone or create repos for active development — the right hand of /learn.
Incubate fits situations like: user says incubate [repo]; wants to set up a dev workflow; study/exploration (use /learn); finding projects (use /trace).
Run `npx skills add Soul-Brews-Studio/arra-oracle-skills-cli --skill incubate -a claude-code`. Or copy the skill folder (skills/incubate in Soul-Brews-Studio/arra-oracle-skills-cli) into .claude/skills/incubate in your project. Claude Code loads it when a task matches its description.
Run `npx skills add Soul-Brews-Studio/arra-oracle-skills-cli --skill incubate -a codex`. Or copy the skill folder (skills/incubate in Soul-Brews-Studio/arra-oracle-skills-cli) into .agents/skills/incubate in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add Soul-Brews-Studio/arra-oracle-skills-cli --skill incubate -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/incubate, .gemini/skills/incubate, .github/skills/incubate and .opencode/skills/incubate in your project.
Going by SKILL.md and its folder, Incubate needs the command-line tools its instructions call (git and gh).
SKILL.md names 1 domain. In commands or code: github.com; the agent is likely to contact it when it follows the instructions. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Incubate is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 18k tokens (SKILL.md is roughly 74k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 3.3k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Incubate: Build Px4 macOS (PX4/PX4-Autopilot, 13k stars), Git Worktree Isolation (jnMetaCode/superpowers-zh, 8.3k stars), Worktree (AgentsMesh/AgentsMesh, 2.4k stars) and Cursor Composer Task Delegate (Chachamaru127/claude-code-harness, 3.2k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
Soul-Brews-Studio (a GitHub organization) maintains it in Soul-Brews-Studio/arra-oracle-skills-cli, which has 123 GitHub stars. The repository holds 31 skills in this directory. The repository was last updated on October 3, 2026.
Source: Soul-Brews-Studio/arra-oracle-skills-cli on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.