From ywc-agent-toolkit
Resolves Docker host-port collisions when running parallel Git worktrees by deriving a deterministic per-worktree port block and COMPOSE_PROJECT_NAME, tearing down only that worktree's stack.
How this skill is triggered — by the user, by Claude, or both
Slash command
/ywc-agent-toolkit:ywc-docker-isolateThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**Announce at start:** "I'm using the ywc-docker-isolate skill to isolate this worktree's Docker host ports."
Announce at start: "I'm using the ywc-docker-isolate skill to isolate this worktree's Docker host ports."
Deterministic, per-worktree Docker host-port isolation for parallel Git worktree
development. Each worktree derives a unique port block purely from its task
name — no central registry, no runtime coordination — and writes it to a
worktree-local env-file plus a .ywc-docker-ports persist file. The committed
compose / env-file is never mutated (per-worktree namespacing, not save/restore).
The deterministic engine lives in bundled scripts; this skill is the one-line
interface to them.
When tempted to skip a step, check this table first:
| Excuse | Reality |
|---|---|
| "Only one worktree is active right now — isolation is unnecessary" | Isolation is unconditional in the executor hook. A second worktree may start at any time, and the deterministic block is what guarantees they never collide. Run setup for every task worktree. |
"Just edit the compose ports: / committed .env to free the port" | The original compose and committed env-file are immutable (NFR-1). Isolation writes only a worktree-local env-file managed block + .ywc-docker-ports; mutating tracked files pollutes git diff and breaks the other worktrees. |
"ss is enough to check whether a port is free" | ss is Linux-only and absent on macOS. Port checks go through the cross-platform check_port_in_use (ss→lsof→netstat, fail-safe when none exist) — never call ss directly. |
| "Re-running setup can just re-compute the hash to get the same ports" | Reproducibility (AC2) comes from reading back .ywc-docker-ports, not from re-hashing. A re-hash would drift if an external process transiently held a port. Always read back the persist file; re-hash only on first allocation. |
"audit returned exit 0, so there are no leftover stacks" | audit exit is always 0 — residual stacks are signalled by non-empty stdout, not the exit code. Parse stdout; never gate on the audit exit code. |
| "A leftover stack from a prior run is harmless — proceed" | A stale ywc-<task>_* stack occupies the same deterministic port the next run derives, so resume is guaranteed to collide. Pre-flight audit must fail-loud (or --prune) before setup. |
The deterministic logic is in scripts/ — invoke it with a single line per mode.
The CLI contract (arguments, writes, exit codes) is authoritative and is what
ywc-parallel-executor integrates against.
setup — allocate and apply the port blockbash claude-code/skills/ywc-docker-isolate/scripts/setup-docker-ports.sh \
--task-name <task> --worktree-path <dir> [--compose-file <file>] [--port-vars VAR1,VAR2]
COMPOSE_PROJECT_NAME=ywc-<sanitized-task> + each *_PORT) and the .ywc-docker-ports persist file.COMPOSE_PROJECT_NAME + VAR=port lines)..ywc-docker-ports (deterministic, AC2) and live-checks every port before returning — a squatted port fails loud (AC13).teardown — remove only this worktree's stackbash claude-code/skills/ywc-docker-isolate/scripts/teardown-docker.sh \
(--task-name <task> | --project-name <proj>) --worktree-path <dir> [--keep-volumes]
docker compose -p <project> down --volumes (default; --keep-volumes preserves data), idempotent (exit 0 even with no running stack), deletes .ywc-docker-ports on success.compose down failed (LEAKED_DOCKER_STACK marker, wave non-blocking) or sanitize/malformed project (SANITIZE_ERROR marker, wave blocking). Both markers are written to stderr — distinguish the two by the marker string, not the exit code.audit — report leftover stacks for expected tasksbash claude-code/skills/ywc-docker-isolate/scripts/audit-docker-stacks.sh \
--expect <task1,task2,...> [--prune]
ywc-<task> stacks. Stdout non-empty = residual present (each line RESIDUAL_DOCKER_STACK: <project> (<n> container(s))); exit is always 0.--prune tears residual stacks down (down --volumes) and confirms each on stdout (pruned residual stack: <project>). Without --prune, the caller (parallel-executor Pre-flight) must fail-loud and abort the run on any residual. Gate on stdout non-empty only when --prune is not passed — the prune confirmation lines would otherwise read as residuals.One-line pointers only (no inline prose in the executor body):
audit --expect <wave tasks> — non-empty stdout → abort run with the printed stacks + --prune remediation.setup --task-name <t> --worktree-path <wt> — exit 1 → task BLOCKED + worktree preserved, wave continues.cleanup-worktree.sh, inside the 4e-DONE loop): teardown --task-name <t> --worktree-path <wt> — preserved (BLOCKED) worktrees skip 4g, so teardown is skipped automatically.ss directly instead of the cross-platform helper — breaks on macOS.audit exit code instead of stdout non-empty — misses every residual stack..ywc-docker-ports — breaks AC2 determinism under transient external port use.git diff (NFR-1).bash -n clean on all scripts/*.sh.setup twice with the same --task-name yields an identical port map (AC2).COMPOSE_PROJECT_NAME values (AC1).validate_ywc_skills.py PASS; README locale set (.md / .en.md / .ja.md / .ko.md) present.Action required: Read references/port-allocation.md for the hash formula,
var_indexsort rule, salt chain, modulo bias, and the AC2 determinism guarantee.
Action required: Read references/preconditions.md for compose detection, env-var mapping limits (short vs long syntax), the YAML parser priority, the platform port-check tool table, and the env-file vs shell-export precedence.
npx claudepluginhub yongwoon/ywc-agent-toolkitGuides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.