SessionPool accepts headedHostCount; when > 0 it launches a second
Chromium in headed mode and creates the first N sessions in it.
Joiners (sessions N..count) stay headless in the main browser.
Each headed context gets a 960×900 viewport — tall enough to show
the full game table (deck + opponent row + own 2×3 card grid +
status area) without clipping. Horizontal tiling still fits two
windows side-by-side on a 1920-wide display.
window.moveTo is kept as a best-effort tile-placement hint, but
viewport from newContext() is what actually sizes the window
(window.resizeTo is a no-op on modern Chromium / Wayland).
Verified: 1-room tiled run plays a full game cleanly; 2-room
parallel tiled had one window get closed mid-run, which is
consistent with a user manually dismissing a window — tiled mode
is a best-effort hands-on debugging aid, not an automation mode.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Owns BrowserContexts, seeds via POST /api/auth/register with the
invite code on cold start, warm-starts via localStorage injection of
the cached JWT, falls back to POST /api/auth/login if the token is
rejected. Exposes acquire(n) for scenarios.
Infrastructure changes needed to import the real GolfBot class from
tests/e2e/bot/ without the Task-10 structural-interface workaround:
- Add @playwright/test as devDep so value-imports in e2e/bot/*.ts
resolve at runtime (Page/Locator/expect are pulled even as types)
- Remove rootDir from tsconfig so TS follows cross-package imports;
add a paths entry so TS can resolve @playwright/test from the soak
package's node_modules when compiling files under tests/e2e/bot
- Drop the local GolfBot structural interface + its placeholder
GamePhase/StartGameOptions/TurnResult types; re-export the real
class from types.ts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>