Skip to content

feat: per-service port drift in wt new and wt setup#2

Merged
pkudinov merged 12 commits into
mainfrom
feat/port-drift-allocation
Apr 25, 2026
Merged

feat: per-service port drift in wt new and wt setup#2
pkudinov merged 12 commits into
mainfrom
feat/port-drift-allocation

Conversation

@pkudinov
Copy link
Copy Markdown
Contributor

Summary

When wt new or wt setup allocates ports, in-use ports now drift forward by 1 instead of failing or silently skipping the slot.

  • Per-service drift. Only the conflicting service moves; the slot's other services stay at their natural ports. Drift respects both OS-level binds and ports already reserved by other wt allocations in the registry.
  • Listener identification. OS conflicts surface a stderr line like Port 3200 (web) in use by node[12345]; using 3201 instead. via lsof (best-effort, never throws).
  • Internal conflicts (another wt slot owns the port) skip the OS probe and report reserved by slot 1 (web) instead.
  • JSON mode. Drift information appears as data.portDrifts: PortDrift[] in --json payloads from both commands; stderr lines are suppressed in that mode.
  • Slot selection is now purely registry-based — findAvailablePortSafeSlot (which silently skipped slots with port conflicts) is gone, so you no longer end up in a higher slot for non-obvious reasons.
  • wt setup port-reuse fix. Re-running setup on an existing worktree (e.g., from the post-checkout hook) now reuses the registered ports verbatim. Previously it recomputed via the formula, which is invisible today but would silently overwrite drifted ports once drift exists.
  • Cap. Drift cap is 65535 with a hard error if a service can't find a free port.

Spec: docs/superpowers/specs/2026-04-24-port-drift-allocation-design.md
Plan: docs/superpowers/plans/2026-04-25-port-drift-allocation.md

Bumps version to 0.4.1.

Test Plan

  • `pnpm lint` clean
  • `pnpm test` — 100 passing, 1 skipped (pre-existing docker integration)
  • `pnpm exec tsc --noEmit` clean
  • `pnpm build` clean
  • In a downstream consumer with a real `wt.config.json`: occupy a slot's natural port (`python3 -m http.server 3100`), run `wt new test/drift-smoke --no-install`, confirm stderr reports the drift line and the worktree is created at the next port. Then `wt remove`.
  • Same downstream repo: re-run `wt setup` on an existing worktree and confirm registered ports are unchanged.

🤖 Generated with Claude Code

pkudinov and others added 12 commits April 24, 2026 23:57
Replaces the current behavior where slots are skipped (auto path) or
errors are thrown (--slot N path) when ports are already in use. Per-
service drift forward by 1 until a port is both OS-free and not in the
registry, with a stderr line identifying the listener via lsof.

Also fixes a latent bug in setup.ts where re-running setup on an
existing allocation would overwrite registered ports with formula
values — invisible today but harmful once drift exists.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Eight-task plan with TDD steps for each: types, parseLsofOutput,
describeListener, allocateServicePorts core, wiring into new.ts,
wiring into setup.ts (with the existing-allocation port-reuse fix),
removal of dead code, and final verification.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ocument conflict invariant

Replace hard-coded 3200/4200 with OS-assigned ephemeral ports to prevent
spurious test failures when those ports are bound on the developer's machine
or CI runner. Add an explanatory comment next to the conflict! non-null
assertion to document the invariant that makes it safe.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…gging

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…t-slot test coverage

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@pkudinov pkudinov merged commit 06886e1 into main Apr 25, 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