fleet-port-audit
Why It Exists
local-dev-stack's own port-block invariant tells one repo how to pick a
distinct 54N2x Supabase block and a distinct WEB_PORT/API_PORT pair. It
says nothing about auditing an entire fleet of repos that grew that way
independently, over time, without a shared allocation ledger. This skill is
what filled that gap, after a full audit of a real multi-repo fleet found a
live collision pileup: two repos defaulting to the identical
WEB_PORT=3010/API_PORT=4010, a third repo's Supabase block byte-for-byte
identical to an unrelated repo's, and one repo's web process silently
binding a framework's own default port because its command line never
referenced the env var that was supposed to control it.
Fixing that pileup took a full plan, six parallel implementation agents, and
three rounds of architect review — the first two rounds each caught a real
regression the mechanical fix had introduced (a stale API URL left pointing
at a vacated port; a Supabase auth redirect URL that broke local OAuth after
a web port moved; a gitignored .env.local file invisible to the sweep tool
because grep on that machine was silently gitignore-aware). Every invariant
in this skill traces back to one of those specific, real findings — including
the discovery, weeks after the fleet was declared collision-free, that a
"free" port was actually held by a root-owned launchd socket the whole time,
invisible to anything short of sudo lsof.
What It Does
Two independent axes, audited and allocated separately: the app dev-server
ports (WEB_PORT/API_PORT) each repo's web/api processes actually bind,
and each repo's own local Supabase port block. A repo can be clean on one axis
and colliding on the other — treating them as one problem is how the original
pileup went unnoticed for as long as it did.
The fleet's actual port map — which repo owns which port, right now — is
deliberately not shipped inside this skill package. It's a single
structured YAML file the skill reads via an external, independently-updated
source: $FLEET_PORT_REGISTRY if set, a fixed dotfiles-relative default if
not, or an explicit ask-and-remember prompt if neither resolves. Baking a
live, frequently-mutated fleet map into a redistributed skill package means
every consumer gets a stale copy the moment anyone else's allocation changes.
How To Use It
skills add git@github.com:catesandrew/next-starters.git --skill skills/fleet-port-audit -g
Set FLEET_PORT_REGISTRY to a real YAML file (see
references/registry-schema.yaml for the shape), or let the skill prompt you
for a path the first time it runs. From there: "what port should <new-repo>
use" allocates the next free pair on both axes from the registry's current
maximum; "audit the fleet" sweeps every registered repo for drift between
what's declared and what's actually read.
Gotchas & Invariants
The full 8 invariants live in SKILL.md. The two worth knowing before you
run this against a real fleet for the first time:
- A declared-correct port value proves nothing on its own. Check both
where a port is declared (
.env.dev) and where it's actually read (the process-compose/Procfile command line's--port/API_PORT=argument). This exact bug — awebcommand with no--portflag at all — shipped twice in the rollout that produced this skill, each time silently falling back to the framework's own default and colliding with an unrelated repo. sudo lsof -i :<port> -P -n, never a barelsof, before calling a port free. A root-owned listener — commonly a Docker Desktop or OrbStack port-forwarding proxy held via macOSlaunchdsocket activation — is invisible to an unprivileged process list. This isn't theoretical: a port the fleet's own registry had marked free for weeks turned out to already be held this way, found only when a new repo's dev server refused to bind it.
Related Skills
local-dev-stack— the single-repo operational stack whose own port-block invariant is the origin of the convention this skill audits across the whole fleet. Companion, not a superset:local-dev-stackowns one repo's stack; this skill owns the cross-repo layer above it.