S1 spike: terminal renderer (ghostty-web vs xterm.js, iPhone) #13

Closed
opened 2026-10-05 15:32:09 +00:00 by kayg · 35 comments
Owner

Spike to choose the terminal renderer. Decisions 14, 37 and 41.

  • One page with ghostty-web and xterm.js (WebGL renderer), and the same replayed output stream in each
  • Measure fps and dropped frames at 10 MB/s of output, input latency and memory, on desktop Chrome and Safari
  • The owner tests on a real iPhone: scrolling, selection, soft keyboard, IME, and how the PWA feels
  • Write a recommendation. The spike code is thrown away.
Spike to choose the terminal renderer. Decisions 14, 37 and 41. - [ ] One page with ghostty-web and xterm.js (WebGL renderer), and the same replayed output stream in each - [ ] Measure fps and dropped frames at 10 MB/s of output, input latency and memory, on desktop Chrome and Safari - [ ] The owner tests on a real iPhone: scrolling, selection, soft keyboard, IME, and how the PWA feels - [ ] Write a recommendation. The spike code is thrown away.
Author
Owner

Starting renderer spike on branch spike-renderer, base SHA 4560d9dd64.

Starting renderer spike on branch spike-renderer, base SHA 4560d9dd64cec2da88254eb5014aa180728f1b5f.
Author
Owner

Finding: this worktree is a bare S0 scaffold: rg --files found only CLAUDE.md, DECISIONS.md, docs/ARCHITECTURE.md, LICENSE, and .gitignore; there is no apps/, packages/ui, scripts/verify, or existing frontend to extend. I will keep all probe code under spikes/renderer/.

Finding: this worktree is a bare S0 scaffold: rg --files found only CLAUDE.md, DECISIONS.md, docs/ARCHITECTURE.md, LICENSE, and .gitignore; there is no apps/, packages/ui, scripts/verify, or existing frontend to extend. I will keep all probe code under spikes/renderer/.
Author
Owner

Finding: xterm.js 5.5.0's release notes pair it with @xterm/addon-webgl 0.18.0 and @xterm/addon-fit 0.10.0. The current 0.19.0 WebGL addon has an upstream disposal regression with xterm 5.5.0 (xtermjs/xterm.js#6143, Aug 2026), so the probe will pin the compatible 0.18.0 addon.

Finding: xterm.js 5.5.0's release notes pair it with @xterm/addon-webgl 0.18.0 and @xterm/addon-fit 0.10.0. The current 0.19.0 WebGL addon has an upstream disposal regression with xterm 5.5.0 (xtermjs/xterm.js#6143, Aug 2026), so the probe will pin the compatible 0.18.0 addon.
Author
Owner

Finding: the first production build emitted a 1,051.25 kB JavaScript chunk (289.81 kB gzip) and Vite's >500 kB warning because both renderer stacks are eager imports. I am checking lazy renderer imports to keep the initial PWA load smaller before measuring.

Finding: the first production build emitted a 1,051.25 kB JavaScript chunk (289.81 kB gzip) and Vite's >500 kB warning because both renderer stacks are eager imports. I am checking lazy renderer imports to keep the initial PWA load smaller before measuring.
Author
Owner

Finding: headless Chromium 153.0.8010.12 launches, but Playwright WebKit 26.6 cannot launch because this host lacks libgtk-4, GStreamer, ICU, GTK-adjacent codec libraries, and related WebKit runtime dependencies (exact Playwright error in this job log). I am checking for a runtime usable without changing system files.

Finding: headless Chromium 153.0.8010.12 launches, but Playwright WebKit 26.6 cannot launch because this host lacks libgtk-4, GStreamer, ICU, GTK-adjacent codec libraries, and related WebKit runtime dependencies (exact Playwright error in this job log). I am checking for a runtime usable without changing system files.
Author
Owner

Finding: the GTK WebKit MiniBrowser could not run with --headless on this host, but Playwright 1.63.0 successfully opened and read a page in WebKit 26.6 under Xvfb. The spike now records that display mode explicitly. The systemd user unit spike-renderer is active at http://10.69.69.125:47321/ and responds 200 with Cache-Control: no-store, max-age=0.

Finding: the GTK WebKit MiniBrowser could not run with --headless on this host, but Playwright 1.63.0 successfully opened and read a page in WebKit 26.6 under Xvfb. The spike now records that display mode explicitly. The systemd user unit spike-renderer is active at http://10.69.69.125:47321/ and responds 200 with Cache-Control: no-store, max-age=0.
Author
Owner

Finding: production screenshots showed mismatched terminal grids (Ghostty 134×24 vs xterm 129×18). After sharing the FitAddon cell metrics and responsive inset, both measure 131×24 at 1280×900 in Chromium and WebKit, and 39×21 at 390×844 in Chromium. The PTY TUI capture also included script(1) wrapper text and exited its alternate screen; the fixture recorder now removes wrapper lines and retains the synthetic Vim screen.

Finding: production screenshots showed mismatched terminal grids (Ghostty 134×24 vs xterm 129×18). After sharing the FitAddon cell metrics and responsive inset, both measure 131×24 at 1280×900 in Chromium and WebKit, and 39×21 at 390×844 in Chromium. The PTY TUI capture also included script(1) wrapper text and exited its alternate screen; the fixture recorder now removes wrapper lines and retains the synthetic Vim screen.
Author
Owner

Finding: shared-page benchmark runs timed out on Chromium input probes after the 50 MB replay, and GTK WebKit crashed when xterm requested WebGL2. Giving each renderer a fresh page in the same browser context stabilized the harness. The final spikes/renderer/RESULTS.md records 12 replay rows and 4 input-latency summaries; WebKit xterm is explicitly labeled DOM fallback after the WebGL2 crash. This is a host limitation, not an iPhone Safari result.

Finding: shared-page benchmark runs timed out on Chromium input probes after the 50 MB replay, and GTK WebKit crashed when xterm requested WebGL2. Giving each renderer a fresh page in the same browser context stabilized the harness. The final spikes/renderer/RESULTS.md records 12 replay rows and 4 input-latency summaries; WebKit xterm is explicitly labeled DOM fallback after the WebGL2 crash. This is a host limitation, not an iPhone Safari result.
Author
Owner

Finding: lazy loading cut the production entry bundle to 24.46 kB minified (8.76 kB gzip) in spikes/renderer/dist/assets/index-Be_fbCKc.js. The separately loaded ghostty-web chunk remains 638.38 kB minified (185.43 kB gzip), so Vite still emits its 500 kB chunk warning; xterm stays in a separate 289.67 kB chunk. The production build succeeds.

Finding: lazy loading cut the production entry bundle to 24.46 kB minified (8.76 kB gzip) in spikes/renderer/dist/assets/index-Be_fbCKc.js. The separately loaded ghostty-web chunk remains 638.38 kB minified (185.43 kB gzip), so Vite still emits its 500 kB chunk warning; xterm stays in a separate 289.67 kB chunk. The production build succeeds.
Author
Owner

Finished: commit 44ce025396 on spike-renderer. The page is live at http://10.69.69.125:47321 (systemd user unit active; HTTP 200, Cache-Control: no-store). Vite production build passes; the report, fixture streams, and production screenshots are in spikes/renderer/RESULTS.md and spikes/renderer/screenshots/.\n\nChromium flood: ghostty-web 59.7 fps, 2 estimated dropped frames, 5024.7 ms drain, 2.6 MB post-GC V8 heap; xterm WebGL 50.4 fps, 48 drops, 5006.2 ms drain, 4.6 MB post-GC. Input p50/p95/max was 32.6/32.7/32.8 ms for Ghostty and 16.2/16.3/16.4 ms for xterm. WebKit GTK measured Ghostty at 35.7 fps with 203 estimated drops and xterm DOM fallback at 48.3 fps with 13 drops; GTK WebKit crashed when xterm requested WebGL2, so those xterm rows are not WebGL measurements. WebKit heap and long-task data are unavailable in this harness.\n\nRecommendation: advance ghostty-web as the next iPhone candidate based on Chromium flood pacing and retained heap, while keeping the decision provisional because xterm had lower synthetic input latency and the real-iPhone standalone checklist is still pending. The in-page Copy results path was exercised with a replay and input run; HTTP used the selection-copy fallback.\n\nRequired fast gate output (verbatim):\nzsh:1: no such file or directory: scripts/verify

Finished: commit 44ce025396995d313b210a8e3c89881917d83998 on spike-renderer. The page is live at http://10.69.69.125:47321 (systemd user unit active; HTTP 200, Cache-Control: no-store). Vite production build passes; the report, fixture streams, and production screenshots are in spikes/renderer/RESULTS.md and spikes/renderer/screenshots/.\n\nChromium flood: ghostty-web 59.7 fps, 2 estimated dropped frames, 5024.7 ms drain, 2.6 MB post-GC V8 heap; xterm WebGL 50.4 fps, 48 drops, 5006.2 ms drain, 4.6 MB post-GC. Input p50/p95/max was 32.6/32.7/32.8 ms for Ghostty and 16.2/16.3/16.4 ms for xterm. WebKit GTK measured Ghostty at 35.7 fps with 203 estimated drops and xterm DOM fallback at 48.3 fps with 13 drops; GTK WebKit crashed when xterm requested WebGL2, so those xterm rows are not WebGL measurements. WebKit heap and long-task data are unavailable in this harness.\n\nRecommendation: advance ghostty-web as the next iPhone candidate based on Chromium flood pacing and retained heap, while keeping the decision provisional because xterm had lower synthetic input latency and the real-iPhone standalone checklist is still pending. The in-page Copy results path was exercised with a replay and input run; HTTP used the selection-copy fallback.\n\nRequired fast gate output (verbatim):\nzsh:1: no such file or directory: scripts/verify
Author
Owner

Orchestrator review (44ce025):

  • Flood: ghostty-web 59.7 fps with 2 drops, against xterm WebGL 50.4 fps with 48 drops. Heap 2.6 MB against 4.6 MB. ghostty-web is ahead under load.
  • A problem for "feels local": input → paint p50 was 32.6 ms for ghostty against 16.2 ms for xterm. That is exactly one more 60 Hz frame. ghostty-web probably defers the render to the next rAF after write(). This adds 16 ms to each keystroke echo, more than 3× the whole 5 ms server budget. Only 24 synthetic samples. A follow-up job looks into the cause and a fix (spike-renderer-latency).
  • Bundle: the ghostty chunk is 638 KB minified (WASM). It must be lazy-loaded after the shell paints, to keep the first paint under 1 s.
  • WebKit could only test the xterm DOM fallback. The real iPhone is still the deciding test: http://10.69.69.125:47321 (owner checklist on the page).
    The decision waits for the latency follow-up and the iPhone check.
Orchestrator review (`44ce025`): - Flood: ghostty-web 59.7 fps with 2 drops, against xterm WebGL 50.4 fps with 48 drops. Heap 2.6 MB against 4.6 MB. **ghostty-web is ahead under load.** - **A problem for "feels local": input → paint p50 was 32.6 ms for ghostty against 16.2 ms for xterm.** That is exactly one more 60 Hz frame. ghostty-web probably defers the render to the *next* rAF after `write()`. This adds 16 ms to each keystroke echo, more than 3× the whole 5 ms server budget. Only 24 synthetic samples. A follow-up job looks into the cause and a fix (`spike-renderer-latency`). - Bundle: the ghostty chunk is 638 KB minified (WASM). It must be lazy-loaded after the shell paints, to keep the first paint under 1 s. - WebKit could only test the xterm DOM fallback. The real iPhone is still the deciding test: http://10.69.69.125:47321 (owner checklist on the page). The decision waits for the latency follow-up and the iPhone check.
Author
Owner

Starting investigation on branch spike-renderer at base SHA 44ce025396. I will remeasure trusted and synthetic key input through actual paint/presentation, inspect ghostty-web scheduling, and test viable fixes.

Starting investigation on branch spike-renderer at base SHA 44ce025396995d313b210a8e3c89881917d83998. I will remeasure trusted and synthetic key input through actual paint/presentation, inspect ghostty-web scheduling, and test viable fixes.
Author
Owner

Source finding: the old probe starts a timer before dispatch in spikes/renderer/src/main.js:637-649, but ghostty-web 0.4.0's write callback itself queues requestAnimationFrame in spikes/renderer/node_modules/ghostty-web/dist/ghostty-web.js:2388-2390; the probe callback then queues another requestAnimationFrame (main.js:641-646 plus 656-659). Ghostty's continuous loop calls renderer.render in an rAF (ghostty-web.js:2639-2644), while write() parses synchronously into WASM. This makes the old ghostty number include two frame waits after write completion; I’m checking the actual draw frame and presentation with trusted input now.

Source finding: the old probe starts a timer before dispatch in spikes/renderer/src/main.js:637-649, but ghostty-web 0.4.0's write callback itself queues requestAnimationFrame in spikes/renderer/node_modules/ghostty-web/dist/ghostty-web.js:2388-2390; the probe callback then queues another requestAnimationFrame (main.js:641-646 plus 656-659). Ghostty's continuous loop calls renderer.render in an rAF (ghostty-web.js:2639-2644), while write() parses synchronously into WASM. This makes the old ghostty number include two frame waits after write completion; I’m checking the actual draw frame and presentation with trusted input now.
Author
Owner

Measurement finding (Chromium 153.0.8010.12 headless, 1280×900, 500 keys per path/renderer after 20-key warmup; all 500 produced the hooked draw): ghostty canvas trusted input → draw-frame p50/p95/p99 13.4/13.9/14.1 ms; untrusted 13.1/13.6/13.8 ms. xterm 5.5 WebGL trusted 14.1/14.4/14.5 ms; untrusted 13.8/14.1/14.1 ms. The input-started double-rAF presentation proxy is also matched: ghostty trusted p50/p95/p99 30.9/31.5/31.7 ms, xterm 31.1/31.4/31.5 ms. Draw hooks saw canvas2d.fillText and WebGL2.drawElementsInstanced respectively. The ~1-frame ghostty disadvantage in the prior probe is not in renderer paint; its harness measured the ghostty write callback's own rAF plus another rAF. The trusted path now echoes from onData after the real Playwright key event.

Measurement finding (Chromium 153.0.8010.12 headless, 1280×900, 500 keys per path/renderer after 20-key warmup; all 500 produced the hooked draw): ghostty canvas trusted input → draw-frame p50/p95/p99 13.4/13.9/14.1 ms; untrusted 13.1/13.6/13.8 ms. xterm 5.5 WebGL trusted 14.1/14.4/14.5 ms; untrusted 13.8/14.1/14.1 ms. The input-started double-rAF presentation proxy is also matched: ghostty trusted p50/p95/p99 30.9/31.5/31.7 ms, xterm 31.1/31.4/31.5 ms. Draw hooks saw canvas2d.fillText and WebGL2.drawElementsInstanced respectively. The ~1-frame ghostty disadvantage in the prior probe is not in renderer paint; its harness measured the ghostty write callback's own rAF plus another rAF. The trusted path now echoes from onData after the real Playwright key event.
Author
Owner

Fix-path finding: ghostty-web 0.4.0's TypeScript declarations expose Terminal.write(callback?) but mark Terminal.renderer and Terminal.wasmTerm private; there is no terminal render/flush API or render-scheduling option. I tested the private renderer call: adding a call in the next rAF measured 100 keys at p99 14.2 ms to the update frame (baseline 500-key trusted p99 14.0 ms), with no frame improvement. Rendering synchronously reduced the draw-call timestamp to p99 0.9 ms, but the next-presentation proxy was still 31.5 ms p99 and the draw was outside rAF. This depends on private fields, so I recommend no library patch or upstream report; the existing loop already renders the dirty screen on the next frame.

Fix-path finding: ghostty-web 0.4.0's TypeScript declarations expose Terminal.write(callback?) but mark Terminal.renderer and Terminal.wasmTerm private; there is no terminal render/flush API or render-scheduling option. I tested the private renderer call: adding a call in the next rAF measured 100 keys at p99 14.2 ms to the update frame (baseline 500-key trusted p99 14.0 ms), with no frame improvement. Rendering synchronously reduced the draw-call timestamp to p99 0.9 ms, but the next-presentation proxy was still 31.5 ms p99 and the draw was outside rAF. This depends on private fields, so I recommend no library patch or upstream report; the existing loop already renders the dirty screen on the next frame.
Author
Owner

Completed on branch spike-renderer at head a5e8168b69. The 500-key Chromium remeasurement found no extra ghostty frame: trusted draw-frame p50/p95/p99 was 13.4/13.8/14.0 ms for ghostty-web and 14.0/14.3/14.4 ms for xterm WebGL; trusted double-rAF presentation proxy was 30.9/31.5/32.4 ms and 31.0/31.4/31.5 ms respectively. The old 32.6 ms ghostty figure came from measuring its write-callback rAF plus the harness's additional rAF. The private sync-render trial did not move the next presentation boundary. Recommendation: keep ghostty-web as the iPhone candidate; fix the probe, with no upstream patch or issue needed. RESULTS.md and the repeatable probe are committed. Gate output: → (there is no root scripts directory in this worktree); → ; → exit 0; → exit 0, 500 trusted + 500 untrusted keys per renderer. The live page returns HTTP 200 and the systemd user service is active.

Completed on branch spike-renderer at head a5e8168b6997eee4d561d3379598d7790460f560. The 500-key Chromium remeasurement found no extra ghostty frame: trusted draw-frame p50/p95/p99 was 13.4/13.8/14.0 ms for ghostty-web and 14.0/14.3/14.4 ms for xterm WebGL; trusted double-rAF presentation proxy was 30.9/31.5/32.4 ms and 31.0/31.4/31.5 ms respectively. The old 32.6 ms ghostty figure came from measuring its write-callback rAF plus the harness's additional rAF. The private sync-render trial did not move the next presentation boundary. Recommendation: keep ghostty-web as the iPhone candidate; fix the probe, with no upstream patch or issue needed. RESULTS.md and the repeatable probe are committed. Gate output: → (there is no root scripts directory in this worktree); → ; → exit 0; → exit 0, 500 trusted + 500 untrusted keys per renderer. The live page returns HTTP 200 and the systemd user service is active.
Author
Owner

Correction to my previous completion comment: shell interpreted Markdown backticks as command substitutions, so that comment contains spurious shell error text. The work is complete on spike-renderer at head a5e8168b69. The 500-key Chromium remeasurement found no extra ghostty frame: trusted draw-frame p50/p95/p99 was 13.4/13.8/14.0 ms for ghostty-web and 14.0/14.3/14.4 ms for xterm WebGL. Trusted double-rAF presentation proxy was 30.9/31.5/32.4 ms and 31.0/31.4/31.5 ms respectively. Recommendation: keep ghostty-web as the iPhone candidate; correct the probe, with no upstream patch needed. Gate evidence: scripts/verify fast output was exactly: zsh:1: no such file or directory: scripts/verify. bun run build ended with: ✓ built in 1.36s. node --check spikes/renderer/scripts/input-latency.mjs passed with exit 0. bun run bench:latency exited 0 with 500 trusted and 500 untrusted keys per renderer. The live page returns HTTP 200 and its systemd user service is active.

Correction to my previous completion comment: shell interpreted Markdown backticks as command substitutions, so that comment contains spurious shell error text. The work is complete on spike-renderer at head a5e8168b6997eee4d561d3379598d7790460f560. The 500-key Chromium remeasurement found no extra ghostty frame: trusted draw-frame p50/p95/p99 was 13.4/13.8/14.0 ms for ghostty-web and 14.0/14.3/14.4 ms for xterm WebGL. Trusted double-rAF presentation proxy was 30.9/31.5/32.4 ms and 31.0/31.4/31.5 ms respectively. Recommendation: keep ghostty-web as the iPhone candidate; correct the probe, with no upstream patch needed. Gate evidence: scripts/verify fast output was exactly: zsh:1: no such file or directory: scripts/verify. bun run build ended with: ✓ built in 1.36s. node --check spikes/renderer/scripts/input-latency.mjs passed with exit 0. bun run bench:latency exited 0 with 500 trusted and 500 untrusted keys per renderer. The live page returns HTTP 200 and its systemd user service is active.
Author
Owner

Orchestrator: I accept the latency follow-up (a5e8168). The extra frame was an error in the harness (a double rAF). With 500 trusted keys, ghostty-web 13.4 ms p50 / 14.0 ms p99 against xterm 14.0 / 14.4 to the draw frame: the same. Combined with the flood result (59.7 against 50.4 fps, half the heap), ghostty-web is the provisional choice for decision 14. It is final after the owner's iPhone check at http://10.69.69.125:47321.

Orchestrator: I accept the latency follow-up (`a5e8168`). The extra frame was an error in the harness (a double rAF). With 500 trusted keys, ghostty-web 13.4 ms p50 / 14.0 ms p99 against xterm 14.0 / 14.4 to the draw frame: the same. Combined with the flood result (59.7 against 50.4 fps, half the heap), **ghostty-web is the provisional choice** for decision 14. It is final after the owner's iPhone check at http://10.69.69.125:47321.
Author
Owner

Owner test on iPhone (2026-10-06): the spike page cannot be used for the checklist.

  • It only replays recorded fixtures. There is no shell behind it, so typing does nothing.
  • The output is shorter than one screen, so scrollback and scrolling cannot be tested.
  • The page always opens with "renderer mounted · waiting for replay".
  • The owner also rejected the drawn chrome ("extreme slop").
    Orchestrator error: I asked for a manual test of input, paste, IME and scrolling on a page that is only a benchmark harness. Fix: a second test page with a bare full-screen terminal that is connected to a real shell in a disposable, network-less container. No chrome. Job spike-renderer-live.
Owner test on iPhone (2026-10-06): **the spike page cannot be used for the checklist.** - It only replays recorded fixtures. There is no shell behind it, so typing does nothing. - The output is shorter than one screen, so scrollback and scrolling cannot be tested. - The page always opens with "renderer mounted · waiting for replay". - The owner also rejected the drawn chrome ("extreme slop"). Orchestrator error: I asked for a manual test of input, paste, IME and scrolling on a page that is only a benchmark harness. Fix: a second test page with a bare full-screen terminal that is connected to a real shell in a disposable, network-less container. No chrome. Job `spike-renderer-live`.
Author
Owner

Starting issue #13 on branch spike-renderer at base SHA a5e8168b69. I am checking the rootless container runtime and implementing the live terminal spike under spikes/renderer-live/.

Starting issue #13 on branch spike-renderer at base SHA a5e8168b6997eee4d561d3379598d7790460f560. I am checking the rootless container runtime and implementing the live terminal spike under spikes/renderer-live/.
Author
Owner

Finding: rootless Podman is operational for kayg (podman info reports Rootless=true, runtime=runc), and 10.69.69.125 is active on wt0. Finding: this worktree has no scripts/ directory or tracked scripts/verify, so the requested repository fast gate is absent here; I will run the spike build and requested browser checks and report the missing gate.

Finding: rootless Podman is operational for kayg (podman info reports Rootless=true, runtime=runc), and 10.69.69.125 is active on wt0. Finding: this worktree has no scripts/ directory or tracked scripts/verify, so the requested repository fast gate is absent here; I will run the spike build and requested browser checks and report the missing gate.
Author
Owner

Finding: the first Rust reached dependency compilation but stopped because the host has no linker (cc). I am checking for an existing user-space compiler/toolchain under before changing the implementation.

Finding: the first Rust reached dependency compilation but stopped because the host has no linker (cc). I am checking for an existing user-space compiler/toolchain under before changing the implementation.
Author
Owner

Finding: the first Rust cargo check stopped because this host has no cc linker. I confirmed cc, gcc, clang and zig are absent; I am checking the user-local Nix toolchain path allowed for this task.

Finding: the first Rust cargo check stopped because this host has no cc linker. I confirmed cc, gcc, clang and zig are absent; I am checking the user-local Nix toolchain path allowed for this task.
Author
Owner

Finding: the first transient unit start showed Cargo places the binary in the spike crate's local target directory, not the worktree root. The error was 'Failed to find executable /home/kayg/Developer/infraternal/.worktrees/spike-renderer/target/release/renderer-live'; I corrected the unit and README to use spikes/renderer-live/target/release/renderer-live.

Finding: the first transient unit start showed Cargo places the binary in the spike crate's local target directory, not the worktree root. The error was 'Failed to find executable /home/kayg/Developer/infraternal/.worktrees/spike-renderer/target/release/renderer-live'; I corrected the unit and README to use spikes/renderer-live/target/release/renderer-live.
Author
Owner

Finding: Playwright 1.56 could not launch because the existing browser cache had Chromium 1243/WebKit 2359 while this version requires Chromium headless shell 1194. The browser check stopped before any session started; I am installing the matching Playwright browsers under kayg's home cache, as allowed by the job.

Finding: Playwright 1.56 could not launch because the existing browser cache had Chromium 1243/WebKit 2359 while this version requires Chromium headless shell 1194. The browser check stopped before any session started; I am installing the matching Playwright browsers under kayg's home cache, as allowed by the job.
Author
Owner

Finding: matching Playwright Chromium and WebKit browsers downloaded, but Playwright reports WebKit cannot run because GTK/GStreamer and media libraries are absent from the host. I found the earlier renderer spike has a local Debian runtime-library bundle and am checking whether it can be reused without host package changes.

Finding: matching Playwright Chromium and WebKit browsers downloaded, but Playwright reports WebKit cannot run because GTK/GStreamer and media libraries are absent from the host. I found the earlier renderer spike has a local Debian runtime-library bundle and am checking whether it can be reused without host package changes.
Author
Owner

Finding: the first live Chromium/Ghostty run typed seq 1 5000 into the real container, but ghostty-web 0.4.0 retained only 981 scrollback lines with scrollback=10000 (measured via getScrollbackLength); its limit is interpreted as bytes. The upstream ghostty-web PR #169 documents the same byte/line mismatch and its 1,000x conversion. I applied that conversion for Ghostty and changed the Playwright check to use each renderer's buffer API.

Finding: the first live Chromium/Ghostty run typed seq 1 5000 into the real container, but ghostty-web 0.4.0 retained only 981 scrollback lines with scrollback=10000 (measured via getScrollbackLength); its limit is interpreted as bytes. The upstream ghostty-web PR #169 documents the same byte/line mismatch and its 1,000x conversion. I applied that conversion for Ghostty and changed the Playwright check to use each renderer's buffer API.
Author
Owner

Finding: Chromium passed the full live-shell suite for both renderers; WebKit reached browser launch but stopped because its MiniBrowser needs libwoff2dec.so.1.0.2, which is absent from the existing local runtime set. I am checking the tracked Debian package bundle and will add only the missing local runtime dependency if needed.

Finding: Chromium passed the full live-shell suite for both renderers; WebKit reached browser launch but stopped because its MiniBrowser needs libwoff2dec.so.1.0.2, which is absent from the existing local runtime set. I am checking the tracked Debian package bundle and will add only the missing local runtime dependency if needed.
Author
Owner

Finding: Chromium passed both renderers with 4,956 history lines; the temporary Playwright 1.56 pin selected WebKit build 2215, which needed libwoff2dec not present in the checked-in local GTK runtime. The existing renderer spike resolves Playwright 1.63.0 and has matching WebKit 2359 plus runtime libraries, so I aligned this browser check to the shared version instead of adding another system-library package.

Finding: Chromium passed both renderers with 4,956 history lines; the temporary Playwright 1.56 pin selected WebKit build 2215, which needed libwoff2dec not present in the checked-in local GTK runtime. The existing renderer spike resolves Playwright 1.63.0 and has matching WebKit 2359 plus runtime libraries, so I aligned this browser check to the shared version instead of adding another system-library package.
Author
Owner

Finding: WebKit 26.6 GTK under Xvfb completed Ghostty's live-shell flow (4,956 scrollback lines), then crashed its page when xterm requested WebGL2. The existing renderer spike records this same crash in spikes/renderer/RESULTS.md:76. I am blocking WebGL2 only in the WebKit Playwright xterm page so the automation checks xterm's fallback while real browsers still request WebGL.

Finding: WebKit 26.6 GTK under Xvfb completed Ghostty's live-shell flow (4,956 scrollback lines), then crashed its page when xterm requested WebGL2. The existing renderer spike records this same crash in spikes/renderer/RESULTS.md:76. I am blocking WebGL2 only in the WebKit Playwright xterm page so the automation checks xterm's fallback while real browsers still request WebGL.
Author
Owner

Finding: after the four successful Playwright cases closed their pages, podman ps still showed two active containers (renderer-live-e834e76043504b06b7fa1e87bc80633a and renderer-live-6d05b5d5b5af483cb6809e96dc631bd8). WebSocket disconnect cleanup is therefore not yet proven; I am tracing it and will remove any leaked demo containers before the final check.

Finding: after the four successful Playwright cases closed their pages, podman ps still showed two active containers (renderer-live-e834e76043504b06b7fa1e87bc80633a and renderer-live-6d05b5d5b5af483cb6809e96dc631bd8). WebSocket disconnect cleanup is therefore not yet proven; I am tracing it and will remove any leaked demo containers before the final check.
Author
Owner

Cleanup follow-up: the containers seen after the browser run still had established WebSocket peers, so I left those sessions alone. A separate Chromium connect/close check created renderer-live-724f9f70e90146e49aad8fed63e7d3d4, closed the page WebSocket, and confirmed Podman removed that container while the other active sessions remained. Close cleanup is verified.

Cleanup follow-up: the containers seen after the browser run still had established WebSocket peers, so I left those sessions alone. A separate Chromium connect/close check created renderer-live-724f9f70e90146e49aad8fed63e7d3d4, closed the page WebSocket, and confirmed Podman removed that container while the other active sessions remained. Close cleanup is verified.
Author
Owner

Browser finding: the production build passed the requested checks in Chromium and WebKit for both renderers. Each run typed seq 1 5000, scrolled to line 1, pasted a long command, and opened/exited Vim; each retained 4,956 history lines. xterm used WebGL in Chromium and its fallback renderer in WebKit (the local GTK/WebGL2 test runtime limitation). Screenshots: spikes/renderer-live/screenshots/chromium-ghostty.png, chromium-xterm.png, webkit-ghostty.png, webkit-xterm.png.

Browser finding: the production build passed the requested checks in Chromium and WebKit for both renderers. Each run typed seq 1 5000, scrolled to line 1, pasted a long command, and opened/exited Vim; each retained 4,956 history lines. xterm used WebGL in Chromium and its fallback renderer in WebKit (the local GTK/WebGL2 test runtime limitation). Screenshots: spikes/renderer-live/screenshots/chromium-ghostty.png, chromium-xterm.png, webkit-ghostty.png, webkit-xterm.png.
Author
Owner

Finished: head af2c652948. The spike service is active/running with Restart=on-failure.

URLs:

Owner iPhone test (please note which renderer felt better for each item):

  1. Run seq 1 5000, then scroll with one finger.
  2. Select and copy a line.
  3. Paste a long line.
  4. Open and close the keyboard.
  5. Try dictation and emoji.
  6. Open vim /root/demo/long.txt and htop.

Screenshots: spikes/renderer-live/screenshots/chromium-ghostty.png, chromium-xterm.png, webkit-ghostty.png, webkit-xterm.png. Browser checks do not claim iPhone behavior.

cargo fmt -- --check, cargo build --release, the Vite production build, Podman image build, and all four requested Chromium/WebKit checks passed; each renderer retained 4,956 history lines and passed scroll, paste, and Vim checks.

scripts/verify fast output, verbatim:
zsh:1: no such file or directory: scripts/verify

Finished: head af2c652948918ff3a59cf970b4f8144cea9b3a5c. The spike service is active/running with Restart=on-failure. URLs: - Ghostty: http://10.69.69.125:47341/?r=ghostty - xterm: http://10.69.69.125:47341/?r=xterm Owner iPhone test (please note which renderer felt better for each item): 1. Run `seq 1 5000`, then scroll with one finger. 2. Select and copy a line. 3. Paste a long line. 4. Open and close the keyboard. 5. Try dictation and emoji. 6. Open `vim /root/demo/long.txt` and `htop`. Screenshots: spikes/renderer-live/screenshots/chromium-ghostty.png, chromium-xterm.png, webkit-ghostty.png, webkit-xterm.png. Browser checks do not claim iPhone behavior. `cargo fmt -- --check`, `cargo build --release`, the Vite production build, Podman image build, and all four requested Chromium/WebKit checks passed; each renderer retained 4,956 history lines and passed scroll, paste, and Vim checks. `scripts/verify fast` output, verbatim: zsh:1: no such file or directory: scripts/verify
Author
Owner

Closed by the orchestrator during the 2026-10-28 handover (#254), on the owner's request to close finished issues. Evidence: Renderer spike delivered; ghostty-web was chosen (D14) and pinned from source in D109/#36 (845c293 in dev and prod). Reopen if anything is still missing.

Closed by the orchestrator during the 2026-10-28 handover (#254), on the owner's request to close finished issues. Evidence: Renderer spike delivered; ghostty-web was chosen (D14) and pinned from source in D109/#36 (845c293 in dev and prod). Reopen if anything is still missing.
kayg closed this issue 2026-10-28 17:52:08 +00:00
Sign in to join this conversation.
No labels
launch-qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/infraternal#13
No description provided.