bun test runs every file in ONE process, so a 500ms setTimeout(process.exit(0))
armed in afterAll fired mid-way through a LATER file and killed the entire
suite with exit 0 and no summary — only ~16 of 434 files ran, and every
downstream failure was invisible (observed live throughout this wave's
enumeration). Changes, all guarded by fault injection:
- Replace every delayed-exit teardown with a time-boxed close of the file's
own browser (8 files across browse/ and design/); stub the daemon
/shutdown timer instead of letting its unconditional process.exit tear
the runner down.
- test/no-suicide-exit.test.ts: static tripwire — no *.test.ts may schedule
a delayed process.exit again.
- test/exit-propagation.test.ts + fixtures: fault injection with REAL bun
output proves the truncation shape (exit 0, no summary) and that
scripts/test-free-shards.ts now detects it: a shard exiting 0 WITHOUT
bun's final summary line is treated as FAILED (exit code alone is not
evidence of completion).
- handoff: the three headed-mode integration tests are darwin-skipped with
a pointer to the known macOS headed-launch breakage (#2242/#2554); they
keep running on Linux CI. Un-skip in the browse-daemon wave.
- feedback-roundtrip: repair the handler call sites unmasked by the fix —
handlers take (command, args, session, bm); passing the manager where a
session belongs broke all six tests.
- user-slug-fallback: HOME isolation makes endpoint_hash deterministic.
Fixes#2421, #2435.
Contributed by @sneakygriff (PR #2172) with repairs from @time-attack
(PR #2230 feedback-roundtrip hunks); supersedes PR #2252 by @whd4 (same
defect, credited).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* plan: batch command endpoint + multi-tab parallel execution for GStack Browser
* refactor: extract TabSession from BrowserManager for per-tab state
Move per-tab state (refMap, lastSnapshot, frame) into a new TabSession
class. BrowserManager delegates to the active TabSession via
getActiveSession(). Zero behavior change — all existing tests pass.
This is the foundation for the /batch endpoint: both /command and /batch
will use the same handler functions with TabSession, eliminating shared
state races during parallel tab execution.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: update handler signatures to use TabSession
Change handleReadCommand and handleSnapshot to take TabSession instead of
BrowserManager. Change handleWriteCommand to take both TabSession (per-tab
ops) and BrowserManager (global ops like viewport, headers, dialog).
handleMetaCommand keeps BrowserManager for tab management.
Tests use thin wrapper functions that bridge the old 3-arg call pattern to
the new signatures via bm.getActiveSession().
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add POST /batch endpoint for parallel multi-tab execution
Execute multiple commands across tabs in a single HTTP request.
Commands targeting different tabs run concurrently via Promise.allSettled.
Commands targeting the same tab run sequentially within that group.
Features:
- Batch-safe command subset (text, goto, click, snapshot, screenshot, etc.)
- newtab/closetab as special commands within batch
- SSE streaming mode (stream: true) for partial results
- Per-command error isolation (one tab failing doesn't abort the batch)
- Max 50 commands per batch, soft batch-level timeout
A 143-page crawl drops from ~45 min (serial HTTP) to ~5 min (20 tabs
in parallel, batched commands).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add batch endpoint integration tests
10 tests covering:
- Multi-tab parallel execution (goto + text on different tabs)
- Same-tab sequential ordering
- Per-command error isolation (one tab fails, others succeed)
- Page-scoped refs (snapshot refs are per-session, not global)
- Per-tab lastSnapshot (snapshot -D with independent baselines)
- getSession/getActiveSession API
- Batch-safe command subset validation
- closeTab via page.close preserves at-least-one-page invariant
- Parallel goto on 3 tabs simultaneously
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: harden codex-review E2E — extract SKILL.md section, bump maxTurns to 25
The test was copying the full 55KB/1075-line codex SKILL.md into the fixture,
requiring 8 Read calls just to consume it and exhausting the 15-turn budget
before reaching the actual codex review command. Now extracts only the
review-relevant section (~6KB/148 lines), reducing Read calls from 8 to 1.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: move batch endpoint plan into BROWSER.md as feature documentation
The batch endpoint is implemented — document it as an actual feature in
BROWSER.md (architecture, API shape, design decisions, usage pattern)
and remove the standalone plan file.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: bump version and changelog (v0.15.16.0)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: gstack <ship@gstack.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>