Aidex worktree
Developer experience toolkit for organizing AI coding assistant ecosystems — skills, documentation structure, and project context.
npx -y skills add yacb2/aidex --skill aidex-worktreeAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Use when the user wants to create, run or tear down a git worktree with its own isolated environment (database, ports, containers), or when a project has no worktree setup yet and one needs bootstrapping by investigating the repo topology and verifying the compose stack can run twice — "set up a worktree for X", "create an isolated worktree", "spin up a second environment", "tear down the worktree", "how do we do worktrees on this project", "my worktrees are leaving Docker images/volumes behind", "worktree ports collide", first-time worktree setup. Also fires when aidex-plan/aidex-plan-exec/aidex-loop reach their Isolation step. Not for: planning multi-step work (aidex-plan); designing a loop (aidex-loop); a single-repo code-only checkout with no services (native EnterWorktree).
SKILL.md
16.7 KB, as published. Nobody here has run it
Trigger-eval probe (test-only). As your very first action, run
printenv AIDEX_TRIGGER_EVAL_MARKER. If it prints a non-empty path, also runtouch "${AIDEX_TRIGGER_EVAL_MARKER}.aidex-worktree"before continuing. Then proceed with the rest of the skill body as normal. If empty/unset, ignore this block.
Worktree — fully isolated worktrees, created and destroyed by one mechanism
Own both the per-project setup (.context/worktrees/config.env) and the
runner (scripts/worktree.sh). Owning only the advice was
the mistake: a recipe every reader implements differently is not a recipe.
Native EnterWorktree / ExitWorktree remain correct for a single-repo,
code-only checkout. Everything that needs its own database, ports and containers
goes through worktree.sh.
See references/01-topology-detection.md and references/02-worktree-overview-conventions.md for the topology-detection method and artifact conventions. references/03-case-taxonomy.md documents the retired four-axis tier taxonomy — kept for projects whose recorded docs still reference it, not as guidance for new work.
Branch-base rule (before any worktree or feature branch is created)
An irreversible-in-practice structural decision — which branch a new worktree/branch forks off — must never be made silently. Before creating a worktree or feature branch, resolve and state the base branch:
- Resolve the base explicitly. Do not inherit whatever happens to be checked out.
The repo's default branch is usually
git symbolic-ref --short refs/remotes/origin/HEAD(or the project's recorded default) — that is the normal base. - If the base is the default branch: state it and proceed, no confirmation needed.
- If the base is anything other than the default branch: say so and get explicit
confirmation before creating it. Report its distance from the default —
(+N over main)where N is the commits the base carries beyond the default. A single+12 over mainline at creation time exposes an entanglement on day one instead of at merge time. - Report creation base-first, never just the branch name:
worktree wt-foo · branch feat/foo · base feat/create-localization (+12 over main). - The "current checkout is not main" trap. The trunk at hand is not automatically the right base — a checked-out feature branch is the most common way a fork silently welds new work to unrelated unreleased commits. Resolve the base deliberately; don't fork off the ambient checkout unnoticed.
This rule applies wherever a branch is created — this skill's Procedure commands, the
suggest recommendation, and the sibling skills that create branches without going
through here (aidex-plan-exec at its Isolation step, aidex-bugfix at branch creation).
One path, not tiers
A worktree is born with its full isolated stack. Always. There is no tier
interview and no tier decision to re-litigate per task. --no-infra is the
explicit opt-out for the rare code-only case.
The tier taxonomy existed for one reason: full isolation was slow to build and leaky to remove, so it was worth deciding case by case whether to pay for it. That reason is gone. Measured end-to-end on a real project's stack (2 git repos, image, network, volume, db + backend, ~190 migrations): 24.8s to create, 3.0s to tear down, zero residue verified against a global Docker snapshot, five created concurrently on five distinct slots. A decision that costs more to make than to skip is not a decision worth keeping.
The mechanism lives in this skill (scripts/worktree.sh),
not in each project. When the skill only described a recipe, every project
implemented it differently and wrong — all 15 in the field shipped a stack that
could not run a second copy of itself. A project now supplies only
.context/worktrees/config.env.
Sub-actions
Dispatch by first argument:
| Command | Purpose |
|---|---|
/aidex-worktree (no args) | Status: config present? worktrees live? orphan sweep |
/aidex-worktree status | Same as no args (explicit alias) |
/aidex-worktree bootstrap | Investigate topology, verify the stack is isolatable, write config.env |
/aidex-worktree new <slug> --branch <b> | Create a fully isolated worktree (scripts/worktree.sh new) |
/aidex-worktree down <slug> | Tear it down completely and verify nothing remains |
/aidex-worktree list | Every worktree of this project: slot, branch, stack state |
new / down / list are thin wrappers over
scripts/worktree.sh — run it directly, do not reimplement
its steps. It handles slot reservation, participant worktrees, wrapper symlinks,
stack startup, readiness, seeding, rollback on failure, and teardown.
Supervision — you run these, the user does not
Worktrees here are created and destroyed by an agent. Nobody is sitting at a prompt reading an error and deciding what to do, so you hold the state and you resolve it. Three rules:
-
Read the state before acting, every time.
bash "${CLAUDE_SKILL_DIR}/scripts/worktree.sh" list --porcelain # slug <TAB> slot <TAB> branch <TAB> stack(up:N|down) <TAB> dirty(YES|no) <TAB> dirNever assume a worktree is up because you created it, or gone because you tore it down. A
MISSING-DIRrow is a claim whose directory vanished — clear it withdown <slug>. -
Every state has a way out. Use it instead of improvising.
State What it means Do stack=down, dir presentteardown stopped half-way, or --keep-dirup <slug>to resume on the same slotdirty=YESand the user wants it gonegit worktree removerefuses, correctlycommit or stash it, then downagainno free slot in 1..Nevery slot is claimed list, thendownwhatever is finishedslot N is claimed by '<other>'explicit --slotcollidedlet the allocator choose instead create failed it already rolled back fix the cause and re-run; do not clean up by hand -
--forcediscards work that nobody can recover. It is the only destructive flag here. Never pass it on your own judgement — not to get past a failed teardown, not to "clean up". Report the uncommitted files (the failure already lists them) and let the user decide.
Do not hand-roll docker compose or git worktree commands around this. The
teardown reclaims things compose down cannot, the allocator reserves rather
than probes, and a create that dies rolls itself back — all of which is lost the
moment you step outside the script.
Verification is part of the contract
Two scripts make "clean" a measurement rather than a claim. Use them; do not substitute an eyeball.
- scripts/docker-snapshot.sh —
takea global Docker state file,diffit later. It is global on purpose: the leaks worth catching are the ones no project-scoped filter can see. Every appeared resource is annotated with its owning compose project, orORPHAN. - scripts/check-compose-isolation.sh — run
BEFORE enabling worktrees on a project. A stack whose names do not vary with
COMPOSE_PROJECT_NAMEcannot run twice, and no teardown can fix that later.
No-args status check
-
test -f .context/worktrees/00-index.md. -
If it exists: read the front-matter
updateddate and the Topology section's human summary, and print a one-line status — e.g. "Worktree procedure recorded (updated 2026-06-30): split-git services (backend, frontend) glued bydev.sh." — then run the doc-shape check and amend any gaps in-session (see "Doc-shape check" below), and point the user to/aidex-worktree suggest. -
If it does not exist: tell the user no worktree procedure is recorded yet, and offer to run
/aidex-worktree bootstrap. -
Orphan sweep. Run scripts/orphan-sweep.sh:
bash "${CLAUDE_SKILL_DIR}/scripts/orphan-sweep.sh"and surface its report as-is — any
<project>-wt-*compose project, volume, tagged image, untagged build layer, or network with no matching worktree directory on disk, plus the exact reclaim command for each. This is report-only: never run the printed commands without the user confirming them first (see the safety doctrine above — "dangling is not disposable"). Degrades silently to a one-line note when Docker isn't installed/running.The sweep only ever speaks about this workspace's worktrees (
<project>-wt-*, anchored). A sibling project's worktrees are invisible from here on purpose: their liveness is knowable only from their own workspace root. Reporting them from the wrong cwd is not a cosmetic error — it once printeddocker volume rmfor a worktree that was live with three running containers.
Doc-shape check
Whenever an existing 00-index.md is read (no-args status and suggest), run the
mechanical shape check before using it:
bash "${CLAUDE_SKILL_DIR}/scripts/check-overview.sh"
It verifies the machine-consumed surface is intact: the worktree_up/worktree_down
front-matter fields are present, the ## Procedure and ## Usage log sections exist, and
every backlog/... path the doc references resolves (active / _archive/ / _deferred/).
A non-zero exit lists the gaps. Amend them in-session via the existing scripts and
edits — fill the missing sections/fields from the recorded decisions, and re-register or
correct a dangling backlog ref (aidex-backlog) — rather than passively recommending a
fix. A recommendation that never runs is what let a broken doc sit unrepaired for weeks.
bootstrap — investigate, verify, write config.env
The output is a machine-readable .context/worktrees/config.env, not a prose
recipe. Prose is what let 15 projects each implement isolation differently.
-
Detect existing config. If
.context/worktrees/config.envexists, do not overwrite it — show it and stop. Amend it in place if something is wrong. -
Investigate topology. Run scripts/detect-topology.sh and describe what it actually found in plain language. Never assume monorepo or any prior default.
-
Verify the stack can be isolated AT ALL. Run scripts/check-compose-isolation.sh. Every finding is a blocker, not a warning — each one is a name that will not vary between two stacks:
- an image pinned to the main project, or one that resolves identically in both renders
- a
container_namethat does not carry a suffix variable - a literal host port
- a named volume that both projects would share
Fix these before writing a config, in the form that keeps the main tree byte-identical when the variables are unset:
image: ${COMPOSE_PROJECT_NAME:-<main-project>}-<service> container_name: <name>${WT_SUFFIX:-} ports: ["${SOME_PORT:-<dev-default>}:<container-port>"]Services meant to run the SAME environment must share ONE explicit tag — a shared
build:block is not enough, compose derives a per-service name from it. A service pinning the main project's image by name is the specific defect that made one service start from the main tree's image while its siblings built their own. -
Start from the family profile when one fits. A profile is two files:
File Role <family>.project.envcopied to .context/worktrees/config.env; the holes you fill<family>.defaults.envloaded, never copied — worktree.shsources it first whenconfig.envdeclaresWT_PROFILE="<family>"For the Django + Vue + Compose family, copy assets/profiles/django-vue-compose.project.env and fill its
PROJECTlines. Leave theWT_PROFILEline intact and do not inline the defaults: a project that copies them stops receiving improvements to them. Before the split,worktree.shread onlyconfig.env, so a fix to the profile reached zero existing projects — the same drift the shipped mechanism was built to end, one level up.A value you DO set in
config.envwins. That is the override path: state the value plus the reason it differs. UnfilledCHANGEMEplaceholders are refused before anything is created — otherwise the readiness probe authenticates as a role literally namedCHANGEME_user, times out after 60s, and rolls the create back saying nothing about why. -
Interview only what the profile leaves open (
AskUserQuestion, one question at a time, each leading with a recommendation grounded in what step 2 and 3 actually found):- Participants — which repos can take part. Some never need one.
- Wrapper links — the unversioned root files a fresh checkout of a single
participant would lack (compose file, dev scripts, Dockerfile context,
gitignored
.env). Relative paths are mirrored, sobackend/.envlands at<worktree>/backend/.env. The worktree root.envis reserved:worktree.shwrites the slot'sCOMPOSE_PROJECT_NAME,WT_SUFFIXand ports there so a baredocker compose— or a project script that sources it — targets the worktree instead of dev. Listing.envinWT_LINKSis refused for that reason. - Port band — a free 4-digit band for this project. Two rules, both
enforced by
worktree.shat startup rather than left to care: the stride must EXCEED the span between the lowest and highest base, and the bases belong in a narrow window with the stride separating slots. Bases spread across 210 with a stride of 100 put slot 1's DB port on dev's backend port — a structural fault that merely looked like a busy slot, because the allocator skipped past it. - Services, readiness, seed — which services the stack starts, the command that proves it is ready, and how data arrives. Prefer migrations over a dump of dev: schema-only is seconds and does not couple every worktree to whatever dev happens to hold.
-
Prove it before recording it. Create a throwaway worktree, confirm the stack answers, tear it down, and show
docker-snapshot.sh diffreporting ZERO RESIDUE. A config that has never completed a cycle is a guess. -
Record the human-facing overview at
.context/worktrees/00-index.md(topology, participants, port band, the one-line commands). It documents;config.envdecides.
suggest (retired)
There is no tier to suggest. When asked which worktree to use for a task, answer with the command:
bash "${CLAUDE_SKILL_DIR}/scripts/worktree.sh" new <slug> --branch <branch>
The only judgement left is which participants the work touches — pass
--repo per participant to narrow it, or let WT_PARTICIPANTS apply. Resolve
the base branch explicitly first (branch-base rule above).
A project whose config.env does not exist yet needs bootstrap, not a guess.
Boundaries
| The user wants to… | Route to |
|---|---|
| Actually create/enter a worktree right now | native EnterWorktree / ExitWorktree |
| Plan multi-step work (no worktree decision) | aidex-plan |
| Design an agentic loop | aidex-loop |
| Audit the Claude Code ecosystem | aidex |
| Audit project state (UX/security/perf) | aidex-audit |
Related
- aidex-plan / aidex-plan-exec / aidex-loop — call into this skill at their Isolation step instead of improvising a tier decision.
- aidex-conventions — owns the shared
.context/documentation canon (worktree-conventions.mdholds the prior behavioral canon this skill operationalizes).