Skip to content

feat: wt setup --repair, --dry-run, and idempotent Docker#3

Merged
pkudinov merged 19 commits into
mainfrom
feat/setup-repair
Apr 27, 2026
Merged

feat: wt setup --repair, --dry-run, and idempotent Docker#3
pkudinov merged 19 commits into
mainfrom
feat/setup-repair

Conversation

@pkudinov
Copy link
Copy Markdown
Contributor

Summary

Fixes the bug where re-running wt setup could break a healthy worktree's Docker services (port-bind conflicts during spurious recreates), and adds a way to repair stale port allocations in place without losing the worktree directory.

Plain wt setup is now idempotent for Docker

Per-service compose-config hashing (sha256 truncated to 12 hex chars) is stored in the allocation. On every wt setup, current hashes are compared to stored; only services whose hash actually changed are stop+force-recreated, the rest stay running. The up invocation is --no-recreate so Docker's own diff never triggers a surprise recreate. Migration: missing serviceHashes (pre-upgrade allocations) are treated as in-sync — current hashes are stored as a baseline without recreating anything.

wt setup --repair

Re-allocates ports for an existing worktree as if creating it fresh now, treating the slot's own currently-registered ports as not-reserved (allocateServicePorts gets a new excludeSlot option). Useful when an external process has seized a port, or when an allocation predates port-drift (v0.4.1). wt remove is intentionally not the answer — it would delete the worktree directory and any uncommitted work.

wt setup --dry-run

Used with --repair. Prints the proposed reallocation and exits without writing to the registry, env files, Docker, or running postSetup. Errors out if used without --repair.

Targeted Docker recreate path

When recreation is needed, the sequence is compose stop <svcs>compose up -d --force-recreate --no-deps <svcs> → final compose up -d --no-recreate --remove-orphans. The explicit stop releases the port before the new container needs to bind it, so the original failure mode is gone.

JSON output

Adds portChanges (per-service { service, registered, proposed, reason }), recreatedDockerServices, repaired, and dryRun to setup payloads.

Spec: `docs/superpowers/specs/2026-04-25-setup-repair-design.md`
Plan: `docs/superpowers/plans/2026-04-25-setup-repair-and-idempotent-docker.md`

Bumps version to 0.4.2.

Test Plan

  • `pnpm lint` clean
  • `pnpm test` — 118 passing, 1 skipped (pre-existing docker integration suite)
  • `pnpm exec tsc --noEmit` clean
  • `pnpm build` clean
  • In a downstream consumer, run `wt setup` twice on a healthy worktree — second run produces no docker output (no recreation).
  • In a downstream consumer with a stale port allocation: `wt setup --repair --dry-run` shows the expected reallocation; `wt setup --repair` applies it; the worktree directory and database are preserved.

🤖 Generated with Claude Code

pkudinov and others added 19 commits April 25, 2026 17:29
Adds a rebalancing repair flow that re-runs allocateServicePorts for an
existing worktree (treating the slot's own current ports as not-reserved)
and applies the result. --dry-run previews the change set without
writing. postSetup runs on apply when ports change, gated by --install.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bundles the plain-setup Docker idempotence fix (--no-recreate) and the
repair-mode targeted recreate path (stop + force-recreate just the
services whose ports changed) into the same spec. Both share the same
ensureDockerServices change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ashing

Docker recreation is now driven by comparing per-service compose-config
hashes (stored in the allocation) against current generation. Plain
`wt setup` recreates only services whose hash changed; if none changed,
it's a true no-op. Repair, idempotent setup, and config-change recovery
all fall out of the same hash-diff mechanism — no extra flag.

Migration for pre-existing allocations: missing serviceHashes treated as
in-sync; current hashes are stored on first run, so future config edits
are detected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ten-task TDD plan covering: PortChange type, registry schema extension,
allocateServicePorts excludeSlot option, computeServiceHashes helper,
ensureDockerServices recreateServices flag with hash return,
formatRepairPreview output helper, setup.ts auto-detect docker,
--repair / --dry-run flags + CLI wiring, README and SKILL updates,
final verification.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add --repair to re-allocate ports for an existing worktree (excluding
its own current ports), --dry-run to preview without writing, flag
validation (--dry-run requires --repair; --repair errors on fresh
worktrees), and a no-op short-circuit when nothing changed.
…ural-port-revert and env-patch

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@pkudinov pkudinov merged commit 3c0636c into main Apr 27, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant