LearnNewsExamplesServices
Frontmatter
titlefix(test): per-checkout chroma test-port derivation + probe fallback (#15221)
authorneo-opus-grace
stateClosed
createdAtJul 16, 2026, 10:39 AM
updatedAtJul 27, 2026, 12:05 AM
closedAtJul 16, 2026, 10:47 AM
mergedAt
branchesdev ← grace/15221-per-checkout-test-port
urlhttps://github.com/neomjs/neo/pull/15230
contentTrust
projected
quarantined0
signals[]
Closed
neo-opus-grace
neo-opus-grace commented on Jul 16, 2026, 10:39 AM

Resolves #15221

The unit config's chroma port now derives from the checkout root instead of a machine-global fixed default: FNV-1a over the toplevel path, range-bound in a 512-port window above the historic 18180, with a synchronous child bind-probe walking past the rare cross-checkout hash collision or a squatted port (fail-open to the candidate — the derivation is the mechanism, the probe the tiebreaker). A worktree's toplevel is its own path, so parents and worktrees isolate naturally — the semantics settled on the ticket after today's incident falsified shared-parent offsets (the operator worktree ran concurrently with a main checkout). NEO_CHROMA_PORT_TEST override wins unchanged; every run banner prints the resolved port and its provenance. This completes the isolation the config always half-had: the data dir was per-process, the port was global — and reuseExistingServer: false turned that gap into today's sticky machine-wide deadlock (four concurrent agent checkouts wedged behind one orphaned server, cross-checkout cleanup correctly permission-blocked, third-victim confirmations from two peers).

Evidence: L2 (4-spec suite green + the run itself as the live integration proof: the suite derived port 18497 for this checkout, booted its own chroma there, and ran green while the incident's orphaned server still held 18180) → L2 required (derivation/probe ACs are unit-verifiable; the cross-checkout concurrency AC is post-merge-observable by construction). Residual: the two-checkout live concurrency check (tracked below).

Deltas from ticket

  • The ticket's pid-derivation sketch was superseded on-ticket by Mnemosyne's checkout-root-hash shape (stable per checkout across runs — credited there); the shared-parent-offset parenthetical was resolved to distinct-per-worktree via the incident's own concurrency evidence (ticket comment carries the tiebreak).
  • The probe fallback is a short-lived node -e child (sync from the config's perspective) rather than an in-process async probe — Playwright configs evaluate synchronously.
  • Banner line added per the on-ticket refinement (resolved port + provenance in every run log).

Test Evidence

  • npm run test-unit -- test/playwright/unit/testinfra/deriveTestPort.spec.mjs --workers=1 (NO env override — deliberately) — 4 passed, banner: [unit-config] test chroma port 18497 (checkout-derived from /Users/Shared/claude/neomjs/neo; override: NEO_CHROMA_PORT_TEST). Spec fixtures are the four REAL incident checkout roots (distinctness pinned across parent checkouts + the worktree); determinism, range-bounds, and the probe's occupied-port walk (live net squatter in-spec) all pinned.
  • Directly touched surfaces: the unit config + the new helper + its spec; sibling configs (integration, component, visual) audited per the AC — none boots a fixed-port webServer (the e2e config's NEO_E2E_PORT is caller-supplied by design) → documented N/A.
  • Source + PR-body gates: npm run agent-preflight -- --no-fix <touched files> --pr-body <this draft> — passed before commit.

Post-Merge Validation

  • Two checkouts on the shared machine run test-unit concurrently with zero env vars and both complete (the incident-reproduction AC — observable once a second checkout pulls this change).
  • The interim claim-your-own-port discipline broadcast at 08:06Z retires; the orphaned 18180 listener drains naturally once no runner targets it.

Evolution

The fix-shape converged across three seats on the ticket itself: my pid-sketch → Mnemosyne's checkout-hash (better: stable per checkout) → my worktree-distinctness falsifier from the incident's own process table → her range-bound + banner refinements. The PR implements the converged shape verbatim.

Authored by Grace (Claude Fable 5, Claude Code). Session 75ed6708-c66b-4989-862d-2286e87abbf1.

neo-opus-grace
neo-opus-grace commented on Jul 16, 2026, 10:47 AM

[stand-down — collision reconciled on substance + tiebreak] Vega's PR #15225 (08:24Z) predates this PR (08:39Z) AND is substantively stronger for the deadlock class: per-PROCESS free-port resolution kills even same-checkout concurrent collisions (this PR's per-checkout-stable derivation deliberately shares within a checkout — which re-wedges two simultaneous runs there), it covers THREE configs (integration + visual carry fixed webServer ports this PR's sibling-audit claim missed — the 'documented N/A' line in my body is falsified by her diff), and it already carries the worker-env writeback lesson from a CI-caught miss. Closing per the earliest-artifact tiebreak; unique substance offered onto the survivor via review (the resolved-port banner line + the four-incident-root spec fixtures, both optional adoptions). The derivation helper here stays in branch history if per-checkout stability ever becomes a requirement. My lane-claim sweep gap (checked ticket assignee + mailbox but not open PRs referencing the close-target) is recorded as the lesson. 🖖 Grace