Problem
When Docker from another project hijacks an allocated port, --list shows busy with no indication that the process is from a foreign directory. Users can't distinguish "my process is running" from "someone else stole my port" (#81 fixes the directory display, but doesn't add conflict detection).
Additionally, there's no diagnostic tool to find and fix various allocation health issues.
Feature 1: hijacked status in --list
When a port is busy and the process runs from a different directory than the allocation's, show STATUS=hijacked instead of busy, plus footnotes:
PORT DIRECTORY NAME SOURCE STATUS PROCESS ASSIGNED
3018 ~/code/alfagen/mercury main free hijacked docker-proxy 2026-02-26 14:17
3022 ~/code/alfagen/mercury www free free - 2026-03-01 15:27
! Port 3018: used by docker-proxy from ~/worktrees/feature/issue-2110-refactor-monkey-patch-initializers-payme
Detection logic
procInfo := port.GetPortProcess(alloc.Port)
if procInfo != nil && procInfo.Cwd != "" {
if filepath.Clean(procInfo.Cwd) != filepath.Clean(alloc.Directory) {
status = "hijacked"
}
}
When procInfo.Cwd == "" (no root access to read /proc/PID/cwd) — keep busy, can't determine hijacked.
Feature 2: --doctor command
Diagnostic command with 5 checks. By default read-only, --fix auto-fixes safe issues.
Checks
| # |
Check |
Detection |
Fix |
| 1 |
Hijacked ports |
busy + procInfo.Cwd != alloc.Directory |
Suggest --forget + restart |
| 2 |
Stale directories |
os.Stat(alloc.Directory) → IsNotExist |
--fix: remove allocation |
| 3 |
Orphan ports |
busy port in range with no allocation |
Suggest --scan |
| 4 |
Stale external |
StatusExternal + port is now free |
--fix: remove allocation |
| 5 |
Unlocked busy |
busy + not locked (vulnerable to hijack) |
Suggest --lock |
Output format
Checking port allocations...
! Hijacked ports (allocated to X, used by process from Y):
Port 3018: allocated to ~/code/alfagen/mercury, used by docker-proxy
from ~/worktrees/feature/issue-2110-...
Fix: port-selector --forget (from ~/code/alfagen/mercury), then re-run
! Stale directories (directory no longer exists):
Port 3005: ~/code/alfagen/mercury/chore/1988-universe
Fix: run with --fix to clean up
! Orphan ports (busy but not tracked):
Port 8080: used by node (pid=12345)
Fix: port-selector --scan
! Stale external allocations (port now free):
Port 3099: was used by docker-proxy
Fix: run with --fix to clean up
! Unlocked busy ports (vulnerable to hijacking):
Port 3022: ~/code/alfagen/mercury (ruby)
Fix: port-selector --lock
Summary: 5 issues found (2 auto-fixable with --fix)
--fix behavior
- Auto-fixes checks 2 (stale dirs) and 4 (stale external) — safe deletions of clearly invalid data
- Only suggests for checks 1, 3, 5 — require user decision
Implementation plan
Files to modify:
cmd/port-selector/main.go — add --doctor case in main(), new runDoctor(fix bool), hijacked detection in runList(), parseFixFromArgs(), update printHelp()
Tests:
TestRunList_HijackedStatus — allocate port to dir A, occupy from dir B → verify "hijacked"
TestRunDoctor_StaleDirectory — allocate to non-existent dir → verify detection
TestRunDoctor_StaleExternal — external allocation for free port → verify detection
TestRunDoctor_UnlockedBusy — unlocked busy port → verify detection
TestRunDoctor_Fix — stale allocations → --fix → verify cleanup
TestRunDoctor_NoIssues — clean state → "No issues found"
Usage
port-selector --doctor # Diagnose issues (read-only)
port-selector --doctor --fix # Auto-fix safe issues
Problem
When Docker from another project hijacks an allocated port,
--listshowsbusywith no indication that the process is from a foreign directory. Users can't distinguish "my process is running" from "someone else stole my port" (#81 fixes the directory display, but doesn't add conflict detection).Additionally, there's no diagnostic tool to find and fix various allocation health issues.
Feature 1:
hijackedstatus in--listWhen a port is busy and the process runs from a different directory than the allocation's, show
STATUS=hijackedinstead ofbusy, plus footnotes:Detection logic
When
procInfo.Cwd == ""(no root access to read/proc/PID/cwd) — keepbusy, can't determine hijacked.Feature 2:
--doctorcommandDiagnostic command with 5 checks. By default read-only,
--fixauto-fixes safe issues.Checks
--forget+ restartos.Stat(alloc.Directory)→IsNotExist--fix: remove allocation--scanStatusExternal+ port is now free--fix: remove allocation--lockOutput format
--fixbehaviorImplementation plan
Files to modify:
cmd/port-selector/main.go— add--doctorcase inmain(), newrunDoctor(fix bool), hijacked detection inrunList(),parseFixFromArgs(), updateprintHelp()Tests:
TestRunList_HijackedStatus— allocate port to dir A, occupy from dir B → verify "hijacked"TestRunDoctor_StaleDirectory— allocate to non-existent dir → verify detectionTestRunDoctor_StaleExternal— external allocation for free port → verify detectionTestRunDoctor_UnlockedBusy— unlocked busy port → verify detectionTestRunDoctor_Fix— stale allocations →--fix→ verify cleanupTestRunDoctor_NoIssues— clean state → "No issues found"Usage