Skip to content

fix: lsof port backend counts only listeners (v1.0.1)#34

Merged
noahhyden merged 1 commit into
mainfrom
fix/lsof-listen-filter
Jul 20, 2026
Merged

fix: lsof port backend counts only listeners (v1.0.1)#34
noahhyden merged 1 commit into
mainfrom
fix/lsof-listen-filter

Conversation

@noahhyden

Copy link
Copy Markdown
Owner

The bug (found dogfooding a real repo)

With lsof as the port backend, ate_port_listening / ate_port_pids ran lsof -ti tcp:PORT, which matches any socket on the port — including ESTABLISHED sockets, i.e. any client connected to a dev-server port. Consequences on an lsof box:

  • status/doctor could report a port "held" by a client (observed: chrome, and a gnome-shell extension) rather than the server.
  • Worst case: ataegina down mistook a connected client for the port holder and refused to reap a backend ataegina itself launched — orphaning a real process (that was hitting a live DB).

The ss backend was already correct (-l = LISTEN only); only lsof was wrong.

The fix

Add -sTCP:LISTEN to both lsof invocations — listeners only, matching ss -l.

Proven at the OS level: with a real listener + a real client in a separate process, lsof -ti tcp:P returns both pids; lsof -ti tcp:P -sTCP:LISTEN returns only the listener.

Discipline

  • Spec/root-cause: documented in the commit + CHANGELOG.
  • Tests-first: tests/lsof_listen_filter.bats — 4 hermetic tests via a fake lsof that mirrors real lsof's LISTEN-vs-client output and exit codes: a client-only port reads stopped/free (status + doctor), a real listener is still detected (filter doesn't over-hide).
  • Live verification: reproduced the listener-vs-client pid difference with real lsof.
  • Mutation red-teaming: dropping -sTCP:LISTEN fails exactly the two client-only tests.
  • Regression: existing port_tools.bats / doctor.bats / status.bats unaffected; full suite 322/322 green locally.
  • Docs/release: CHANGELOG [1.0.1], VERSION=1.0.1, checksum regenerated.

Patch release v1.0.1; the tag is cut after merge (triggers the automated release).

🤖 Generated with Claude Code

…v1.0.1)

With `lsof` as the port tool, ate_port_listening / ate_port_pids ran
`lsof -ti tcp:PORT`, which also matches ESTABLISHED sockets — so any client
CONNECTED to a dev-server port (a browser, a peer's inbound connection) was
reported as the port's holder. Worst case: `ataegina down` mistook such a
client for the server and refused to reap a process ataegina itself launched,
orphaning it. Both branches now pass `-sTCP:LISTEN`, matching the `ss -l`
backend — only true listeners count.

Found dogfooding a real multi-worktree repo: `down` misattributed frontend
ports to `chrome` and a `gnome-shell` extension (clients connected to those
ports) and would not stop the backends it had started.

Also bumps VERSION to 1.0.1, regenerates the checksum, and adds a [1.0.1]
CHANGELOG entry.

New hermetic regression tests/lsof_listen_filter.bats (4 tests): a fake lsof
that mirrors real lsof's LISTEN-vs-client behavior + exit codes proves a
client-only port reads as stopped/free (status + doctor), while a real listener
is still detected. Mutation-verified: dropping -sTCP:LISTEN fails the
client-only tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@noahhyden
noahhyden merged commit b95feb9 into main Jul 20, 2026
8 checks passed
@noahhyden
noahhyden deleted the fix/lsof-listen-filter branch July 20, 2026 09:25
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