Terminal lifecycle browser qualification
This fixture uses the actual CLIPopup, Pinia store, xterm renderer/addon, dashboard styles, HTTP fetches and WebSocket transport. Only the server is synthetic; sound output is disabled. No actual shell, tmux, installed service, host credentials, wallet or native terminal session is accessed or created.
mkdir /path/to/empty-output
bash tests/apps/run-terminal-browser.sh /path/to/empty-output \
sha256:<qualified-browser-image> /path/to/isolated-locked-frontend
The locked frontend directory must have a retained clean npm-ci receipt and byte-identical package.json/package-lock.json. Its dependencies are mounted read-only. The browser image supplies Chromium/Playwright and is local and immutable; no pull/install is performed. The runner copies only the explicit source/config/font fixture inputs, records their hashes, and requires empty output. A uniquely named container is removed on EXIT, including timeouts. Bounds:90seconds,1CPU,1GiB/no swap,network-none,read-only root,256MiB browser tmp, 128MiB source/build tmp. No engine socket, service bus, home or host network.
At1440px and390px the browser verifies:
- Real xterm public buffer contains a server-sent synthetic marker; actual keyboard input and positive terminal resize dimensions reach the fixture.
- Bounded history snapshots retain100synthetic lines beyond the visible pane; replacing a snapshot and reconnecting reconstruct history exactly once. Scroll to the top and check actual rendered historical text, not only buffer contents.
- Dropping a real socket reconnects the SAME session and sends no creation POST.
- Closing during delayed list or successful creation prevents offscreen attach.
- New is disabled while its request is pending; repeated clicks do not create duplicate sessions. A late created session is recovered by a read, never by repeating its POST.
- Closing/reopening retains and visibly reattaches the actual xterm renderer. Buffer-only checks would miss the original invisible-renderer defect.
- The panel fits both viewport sizes; screenshots retain actual renderer output.
Mounted regressions additionally cover policy refusal, five-attempt retry exhaustion, old-session output after switching, unmount cancellation and an ambiguous creation reply. They use fake transport/renderer and are not substitutes for the browser fixture.
A separate protocol regression exercises the production capture arguments using an owned disposable tmux server (requires local tmux, not a running backend):
timeout 20s python3 tests/apps/test-terminal-scrollback.py
It uses a unique private socket, empty configuration/environment and synthetic output, never the default server or existing sessions. Child address-space/CPU limits and subprocess timeouts bound the fixture. Cleanup checks owned processes and verifies the private server no longer accepts sessions. The test proves visible-only capture loses existing history; the corrected snapshot retains up to1000 history lines plus the visible pane without blank padding, and evicts old history on overflow. It does not compile or execute the Rust handler. It can only recover history still retained by tmux, not deleted history or a destroyed server.
This is not native authentication, installed tmux/tools, reboot/process ownership, physical Companion keyboard/input, independent review or wholeD09/UAT acceptance.