Summary
Improve --lock PORT behavior and fix bugs with locked ports handling.
Full Decision Matrix: docs/decision-matrix.md
Terminology (from Decision Matrix)
| Term |
Definition |
| busy |
Port fails TCP bind: net.Listen("tcp", ":PORT") returns error |
| free |
Port successfully binds via net.Listen() |
| locked |
Allocation has locked: true in allocations.yaml |
| current dir |
Result of os.Getwd() after filepath.Clean() |
| name |
Allocation name from --name flag (default: main) |
Part 1: --lock PORT Command Improvements
1.1 Show directory in success message
Current:
Locked port 3001 for 'main'
Proposed:
Locked port 3001 for 'main' in ~/worktrees/feature-4191
1.2 Smart --force requirement
Require --force if:
- Port is locked for another directory (regardless of busy/free)
Block completely (even with --force) if:
- Port is busy on another directory — cannot reassign running service
- Error:
port 3001 is in use by /path/to/other/dir; stop the service first
Allow without --force if:
- Port is not allocated at all (first-time lock)
- Port is allocated to another directory but is free and unlocked (abandoned)
Special case — port busy but NOT in allocations:
- Without --force: error
port 3001 is in use
- With --force: create allocation (port remains busy, user takes responsibility)
1.3 Unlock old locked allocation on new lock
When locking a new port for directory+name that already has a locked port:
- Set
locked: false on the old port (keep allocation, just unlock)
- Only affects port with same name
Invariant: At most one locked port per directory+name
Part 2: Bug Fix — Locked Port Ignored When Busy
Problem
When directory has a locked port that is busy, port-selector creates a new allocation instead of returning the locked port.
# Directory has locked port 3001, user's service running on it
$ port-selector --list | grep feature-4191
3001 ~/worktrees/feature-4191 busy yes main
# Request port — creates NEW instead of returning locked!
$ port-selector
3056 # Wrong! Should return 3001
Root Cause
FindByDirectoryAndName() returns most recent port by LastUsedAt, ignoring locked status
- If returned port is busy, code creates new allocation
- Locked ports preserved but duplicates accumulate
Solution
Change priority in FindByDirectoryAndName():
- Locked + free → return
- Locked + busy → return (user's service already running)
- Unlocked + free → return
- Unlocked + busy → skip, find another or create new
Key insight: Busy locked port means user's service is already running on their reserved port. Return it — that's exactly what they want.
Part 3: Bug Fix — Multiple Locked Ports for Same Directory+Name
Problem
Directory can have multiple locked ports for same name:
$ port-selector --list | grep feature-4191
3001 ~/worktrees/feature-4191 free yes main
3004 ~/worktrees/feature-4191 free yes main # Two locked!
Root Cause
--lock 3001 --force doesn't unlock old locked port (3004).
Solution
Covered in Part 1.3: unlock old locked port when locking new one for same directory+name.
Implementation Checklist
Environment
- port-selector v0.9.5
- Linux
Spec v2.0 | Decision Matrix: docs/decision-matrix.md | 2026-02-02
Summary
Improve
--lock PORTbehavior and fix bugs with locked ports handling.Full Decision Matrix: docs/decision-matrix.md
Terminology (from Decision Matrix)
net.Listen("tcp", ":PORT")returns errornet.Listen()locked: truein allocations.yamlos.Getwd()afterfilepath.Clean()--nameflag (default:main)Part 1:
--lock PORTCommand Improvements1.1 Show directory in success message
Current:
Proposed:
1.2 Smart --force requirement
Require --force if:
Block completely (even with --force) if:
port 3001 is in use by /path/to/other/dir; stop the service firstAllow without --force if:
Special case — port busy but NOT in allocations:
port 3001 is in use1.3 Unlock old locked allocation on new lock
When locking a new port for directory+name that already has a locked port:
locked: falseon the old port (keep allocation, just unlock)Invariant: At most one locked port per directory+name
Part 2: Bug Fix — Locked Port Ignored When Busy
Problem
When directory has a locked port that is busy,
port-selectorcreates a new allocation instead of returning the locked port.Root Cause
FindByDirectoryAndName()returns most recent port byLastUsedAt, ignoring locked statusSolution
Change priority in
FindByDirectoryAndName():Key insight: Busy locked port means user's service is already running on their reserved port. Return it — that's exactly what they want.
Part 3: Bug Fix — Multiple Locked Ports for Same Directory+Name
Problem
Directory can have multiple locked ports for same name:
Root Cause
--lock 3001 --forcedoesn't unlock old locked port (3004).Solution
Covered in Part 1.3: unlock old locked port when locking new one for same directory+name.
Implementation Checklist
FindByDirectoryAndName()to prioritize locked portsEnvironment
Spec v2.0 | Decision Matrix: docs/decision-matrix.md | 2026-02-02