fix(brainstorm-server): address adversarial review findings

From a two-reviewer adversarial pass:

- [High] EADDRINUSE fallback clobbered the shared .last-port: onListen wrote the
  bound port unconditionally, so a fallback to a random port overwrote the
  preferred port another live session still owns — stranding that session's open
  tab forever. Now persist only when we bound the preferred port (not on
  fallback). The fallback test now asserts .last-port integrity (teeth-verified).

- [Medium] maybeOpenBrowser ran the URL through a shell (exec + JSON.stringify),
  which does NOT neutralize $(...) in a url-host. Platform launchers now use
  execFile with the URL as an argv element (no shell). The operator-set
  BRAINSTORM_OPEN_CMD path stays shell-based (trusted input).

- [Medium] --open was a silent no-op on native Windows (no win32 branch). Added.

- [Medium] helper.js reconnect/status/tombstone had only substring-grep tests.
  Added behavioral tests driving the state machine against a mocked browser:
  Reconnecting+backoff (500->1000->2000), tombstone after the grace period, and
  reload-on-recovery.

- [Low] status pill showed a false 'Connected' before the socket opened; now
  starts 'Connecting…' until onopen.

Not changed (flagged): stop-server.sh's PID-ownership check still matches any
'node ... server.cjs' (narrow residual — a recycled PID onto an unrelated node
server.cjs); robust fix needs fragile cross-platform process introspection.
This commit is contained in:
Jesse Vincent
2026-06-09 15:59:59 -07:00
parent 7b815ed8c8
commit f8f87ff43a
5 changed files with 109 additions and 14 deletions

View File

@@ -117,11 +117,15 @@ async function runTests() {
let outB = ''; b.stdout.on('data', d => outB += d.toString());
for (let i = 0; i < 60 && !outB.includes('server-started'); i++) await sleep(50);
const portB = firstServerStarted(outB).port;
const persisted = fs.readFileSync(portFile, 'utf8').trim();
a.kill(); b.kill(); await sleep(100); fs.rmSync(dir, { recursive: true, force: true });
assert.notStrictEqual(portB, 3415, 'must not bind the already-taken port');
assert(portB >= 49152, 'should fall back to a random high port');
// The fallback must NOT clobber the shared port file — A still owns 3415 and
// its open tab must keep reconnecting there.
assert.strictEqual(persisted, '3415', 'fallback must not overwrite .last-port');
});
await test('auto-opens the browser once, on the first screen', async () => {