Terminal stress test and instant cross-session search #18

Open
opened 2026-10-06 11:39:55 +00:00 by kayg · 28 comments
Owner

Owner request (2026-10-06): stress the terminal with a "behemoth" of output: mixed text, Unicode and emoji, and binary data. Output of any kind must scroll extremely fast. Scrollback must stay searchable. Binary output is left out of search by default, so the index stays small and fast. With about 50 SSH panes open on many hosts, finding one command must be instant.

Part 1 (job stress-render): fixtures plus measurements of ghostty-web in the core-screen mockup: maximum parse throughput, frames during a flood, input latency during a flood, memory, and the behaviour on binary data.
Part 2: the search design. The grill questions are in this thread. The decisions follow the owner's answers.

Owner request (2026-10-06): stress the terminal with a "behemoth" of output: mixed text, Unicode and emoji, and binary data. Output of any kind must scroll extremely fast. Scrollback must stay searchable. Binary output is left out of search by default, so the index stays small and fast. With about 50 SSH panes open on many hosts, finding one command must be instant. Part 1 (job `stress-render`): fixtures plus measurements of ghostty-web in the core-screen mockup: maximum parse throughput, frames during a flood, input latency during a flood, memory, and the behaviour on binary data. Part 2: the search design. The grill questions are in this thread. The decisions follow the owner's answers.
Author
Owner

Starting terminal stress test on branch spike-renderer, based at 4560d9dd64. Current branch HEAD: af2c652948.

Starting terminal stress test on branch spike-renderer, based at 4560d9dd64cec2da88254eb5014aa180728f1b5f. Current branch HEAD: af2c652948918ff3a59cf970b4f8144cea9b3a5c.
Author
Owner

Fixture evidence: the committed generator emits exact decimal sizes (behemoth 50,000,000 B; binary and mixed 20,000,000 B each). Local generation verified behemoth as valid UTF-8 with 228,916 lines and a 24,664-character longest line. Mixed intentionally becomes invalid UTF-8 at its first 128,000-byte binary block (offset 3,872,000).

Fixture evidence: the committed generator emits exact decimal sizes (behemoth 50,000,000 B; binary and mixed 20,000,000 B each). Local generation verified behemoth as valid UTF-8 with 228,916 lines and a 24,664-character longest line. Mixed intentionally becomes invalid UTF-8 at its first 128,000-byte binary block (offset 3,872,000).
Author
Owner

Image gate passed: podman build produced ccc8b9d461fe; a 256 MiB read-only container populated /root/demo/stress with behemoth.txt=50,000,000 B, binary.bin=20,000,000 B, mixed.txt=20,000,000 B, bash.elf=1,265,648 B (ELF magic 7f 45 4c 46). Restarted spike-renderer-live.service; it is active and both 47341 and the 47351 mockup proxy return HTTP 200.

Image gate passed: podman build produced ccc8b9d461fe; a 256 MiB read-only container populated /root/demo/stress with behemoth.txt=50,000,000 B, binary.bin=20,000,000 B, mixed.txt=20,000,000 B, bash.elf=1,265,648 B (ELF magic 7f 45 4c 46). Restarted spike-renderer-live.service; it is active and both 47341 and the 47351 mockup proxy return HTTP 200.
Author
Owner

Confirmed renderer failure: the unmodified mockup terminal (only an in-memory terminal handle probe) crashes Chromium 61 ms after Enter on ; only 86,552 of 50,000,000 payload bytes / 23 binary frames reached the page. The target crash prevents prompt, frame, heap, input-echo, and final-screenshot capture. Reproduction data is in spikes/stress/.run/live-browser.json; crash screenshot also fails with . This is present without the performance probes.

Confirmed renderer failure: the unmodified mockup terminal (only an in-memory terminal handle probe) crashes Chromium 61 ms after Enter on ; only 86,552 of 50,000,000 payload bytes / 23 binary frames reached the page. The target crash prevents prompt, frame, heap, input-echo, and final-screenshot capture. Reproduction data is in spikes/stress/.run/live-browser.json; crash screenshot also fails with . This is present without the performance probes.
Author
Owner

Confirmed renderer failure: the mockup terminal (only the in-memory terminal handle probe) crashes Chromium 61 ms after Enter on cat behemoth.txt; only 86,552 of 50,000,000 payload bytes (23 binary frames) reached the page. The target crash prevented prompt, frame, heap, input-echo, and final-screenshot capture. Reproduction data: spikes/stress/.run/live-browser.json. page.screenshot also failed with Target crashed. This occurred without performance probes.

Confirmed renderer failure: the mockup terminal (only the in-memory terminal handle probe) crashes Chromium 61 ms after Enter on cat behemoth.txt; only 86,552 of 50,000,000 payload bytes (23 binary frames) reached the page. The target crash prevented prompt, frame, heap, input-echo, and final-screenshot capture. Reproduction data: spikes/stress/.run/live-browser.json. page.screenshot also failed with Target crashed. This occurred without performance probes.
Author
Owner

Finding: the disposable PTY WebSocket is not the observed behemoth bottleneck. Reading 50,000,000-byte behemoth.txt without a browser terminal delivered 50,228,984 bytes over 12,879 frames direct (3,982.7 ms, 12.55 MB/s) and 50,228,984 over 13,067 frames through the mockup Bun proxy (3,027.8 ms, 16.51 MB/s). The in-page terminal previously crashed Chromium after 86,552 payload bytes; isolated offscreen ghostty parsing also crashes at its 64 KiB checkpoint. Evidence: spikes/stress/.run/proxy-throughput.json, spikes/stress/.run/live-browser.json, spikes/stress/.run/raw-parser.json.

Finding: the disposable PTY WebSocket is not the observed behemoth bottleneck. Reading 50,000,000-byte behemoth.txt without a browser terminal delivered 50,228,984 bytes over 12,879 frames direct (3,982.7 ms, 12.55 MB/s) and 50,228,984 over 13,067 frames through the mockup Bun proxy (3,027.8 ms, 16.51 MB/s). The in-page terminal previously crashed Chromium after 86,552 payload bytes; isolated offscreen ghostty parsing also crashes at its 64 KiB checkpoint. Evidence: spikes/stress/.run/proxy-throughput.json, spikes/stress/.run/live-browser.json, spikes/stress/.run/raw-parser.json.
Author
Owner

Finding: a 4 KiB-window classifier threshold of 1% is clean on these fixtures when it marks binary if invalid UTF-8 bytes OR disallowed C0 bytes reach 1%; ESC, TAB, LF, and CR are excluded. Exact analyzer evidence: 0/12,208 behemoth text windows triggered (max invalid UTF-8 0.073%, max disallowed C0 0%); 4,883/4,883 random binary windows triggered (max 46.069% invalid UTF-8, 12.476% C0); 161/4,883 mixed-file windows triggered, including the embedded random regions. Repro: spikes/stress/analyze-fixtures.py spikes/stress/.run/fixtures.

Finding: a 4 KiB-window classifier threshold of 1% is clean on these fixtures when it marks binary if invalid UTF-8 bytes OR disallowed C0 bytes reach 1%; ESC, TAB, LF, and CR are excluded. Exact analyzer evidence: 0/12,208 behemoth text windows triggered (max invalid UTF-8 0.073%, max disallowed C0 0%); 4,883/4,883 random binary windows triggered (max 46.069% invalid UTF-8, 12.476% C0); 161/4,883 mixed-file windows triggered, including the embedded random regions. Repro: spikes/stress/analyze-fixtures.py spikes/stress/.run/fixtures.
Author
Owner

Raw parser isolation (16 KiB crash checkpoints): Terminal.write() completed 209,715,200 bytes in 19,476 ms desktop (10.77 MB/s) and 61,418 ms on iPhone 15 Pro at 4x CPU throttle (3.41 MB/s). The other fixtures still crash Chromium; last fully completed checkpoints: desktop behemoth 81,920 B, binary 98,304 B, mixed 81,920 B; iPhone behemoth 81,920 B, binary 212,992 B, mixed 81,920 B. Sustained yes uses 1 MiB progress checkpoints to keep host instrumentation overhead bounded; write durations exclude checkpoint callbacks. No PTY/WebSocket is in the timed write loop.

Raw parser isolation (16 KiB crash checkpoints): Terminal.write() completed 209,715,200 bytes in 19,476 ms desktop (10.77 MB/s) and 61,418 ms on iPhone 15 Pro at 4x CPU throttle (3.41 MB/s). The other fixtures still crash Chromium; last fully completed checkpoints: desktop behemoth 81,920 B, binary 98,304 B, mixed 81,920 B; iPhone behemoth 81,920 B, binary 212,992 B, mixed 81,920 B. Sustained yes uses 1 MiB progress checkpoints to keep host instrumentation overhead bounded; write durations exclude checkpoint callbacks. No PTY/WebSocket is in the timed write loop.
Author
Owner

Live mockup reproduction (second full matrix): desktop Chromium again crashed on cat behemoth.txt after 87,135 received bytes / 76 ms, cat binary.bin after 216,274 bytes / 727 ms, and cat mixed.txt after 81,660 bytes / 45 ms. This confirms the file-output crash is repeatable on the current running mockup; final prompt paint and end screenshot are unavailable because the Chromium target crashes.

Live mockup reproduction (second full matrix): desktop Chromium again crashed on cat behemoth.txt after 87,135 received bytes / 76 ms, cat binary.bin after 216,274 bytes / 727 ms, and cat mixed.txt after 81,660 bytes / 45 ms. This confirms the file-output crash is repeatable on the current running mockup; final prompt paint and end screenshot are unavailable because the Chromium target crashes.
Author
Owner

Gate availability finding: the spike-renderer HEAD has no scripts/verify file and no Cargo.toml ( is empty; neither exists in the worktree root). s1-base contains the Rust gate dispatcher, but its Rust workspace is not present on this spike branch. I will attempt the required scripts/verify fast command at finish and report its exact result; the spike's Node/Python syntax checks and measured browser/parser gates are available here.

Gate availability finding: the spike-renderer HEAD has no scripts/verify file and no Cargo.toml ( is empty; neither exists in the worktree root). s1-base contains the Rust gate dispatcher, but its Rust workspace is not present on this spike branch. I will attempt the required scripts/verify fast command at finish and report its exact result; the spike's Node/Python syntax checks and measured browser/parser gates are available here.
Author
Owner

Extended desktop yes | head -c 200M result: at the 25-minute cap the page had received 248,600,214 WebSocket bytes (0.166 MB/s) and still had no natural prompt; Ctrl-C recovered the shell. During the flood: 23.17 fps, 466.8 ms longest frame, 8,336 gaps >50 ms, no >1 s gap, 10,601 long tasks >50 ms (875,341 ms total, 441 ms max). The 🧪 key was not sent to the WebSocket or painted within 25 minutes. Renderer RSS was 239,924 KiB after output and 243,052 KiB after scroll; screenshots were visually clean.

Extended desktop yes | head -c 200M result: at the 25-minute cap the page had received 248,600,214 WebSocket bytes (0.166 MB/s) and still had no natural prompt; Ctrl-C recovered the shell. During the flood: 23.17 fps, 466.8 ms longest frame, 8,336 gaps >50 ms, no >1 s gap, 10,601 long tasks >50 ms (875,341 ms total, 441 ms max). The 🧪 key was not sent to the WebSocket or painted within 25 minutes. Renderer RSS was 239,924 KiB after output and 243,052 KiB after scroll; screenshots were visually clean.
Author
Owner

The 4x iPhone 15 Pro emulation reproduced the file-renderer failures: behemoth 86,602 received bytes / 1,062 ms, binary 233,560 bytes / 6,441 ms, mixed 86,575 bytes / 943 ms. The page target crashed in each case, so prompt paint, frame and memory results are unavailable; the screenshot attempts also failed with Target crashed.

The 4x iPhone 15 Pro emulation reproduced the file-renderer failures: behemoth 86,602 received bytes / 1,062 ms, binary 233,560 bytes / 6,441 ms, mixed 86,575 bytes / 943 ms. The page target crashed in each case, so prompt paint, frame and memory results are unavailable; the screenshot attempts also failed with Target crashed.
Author
Owner

The 4x iPhone flood hit the 25-minute cap without a natural prompt. Its active command WebSocket delivered 108,714,899 B (0.0725 MB/s over the full cap); the first observed close was about 7m38s after Enter, giving about 0.237 MB/s before disconnect. Six code-1006 WebSocket closes were recorded as the mockup auto-reconnected. The output screenshot shows repeated root prompts concatenated with ^C at the bottom, so the page visibly corrupts its session state after reconnect. No >1s frame gap or canvas context loss was recorded; 🧪 was not sent or painted. RSS: 218,636 KiB after output, 226,716 KiB after scroll.

The 4x iPhone flood hit the 25-minute cap without a natural prompt. Its active command WebSocket delivered 108,714,899 B (0.0725 MB/s over the full cap); the first observed close was about 7m38s after Enter, giving about 0.237 MB/s before disconnect. Six code-1006 WebSocket closes were recorded as the mockup auto-reconnected. The output screenshot shows repeated root prompts concatenated with ^C at the bottom, so the page visibly corrupts its session state after reconnect. No >1s frame gap or canvas context loss was recorded; 🧪 was not sent or painted. RSS: 218,636 KiB after output, 226,716 KiB after scroll.
Author
Owner

Finished on branch spike-renderer at 37c5e7dbfd. Report and four reviewed flood screenshots: spikes/stress/RESULTS.md. All six live file-fixture runs crashed Chromium; the three fixture types also crashed isolated Terminal.write after 82–213 KiB. Desktop yes received 248,600,214 bytes over 25 minutes (0.166 MB/s), with no prompt or input echo. iPhone emulation received 108,714,899 bytes (0.072 MB/s full-window), recorded six code-1006 closes, and showed reconnect prompt corruption. Direct/proxy behemoth samples were 12.55/16.51 MB/s. Binary heuristic flagged 0/12,208 behemoth windows, all 4,883 binary windows, and 161 injected-binary mixed windows with no text-only false positives. Fixture scan and JS/Python syntax checks passed. Required gate output (verbatim): zsh:1: no such file or directory: scripts/verify (exit 127; scripts/verify is absent on this spike branch).

Finished on branch spike-renderer at 37c5e7dbfda0d766284b97f80cf10c501eebef46. Report and four reviewed flood screenshots: spikes/stress/RESULTS.md. All six live file-fixture runs crashed Chromium; the three fixture types also crashed isolated Terminal.write after 82–213 KiB. Desktop yes received 248,600,214 bytes over 25 minutes (0.166 MB/s), with no prompt or input echo. iPhone emulation received 108,714,899 bytes (0.072 MB/s full-window), recorded six code-1006 closes, and showed reconnect prompt corruption. Direct/proxy behemoth samples were 12.55/16.51 MB/s. Binary heuristic flagged 0/12,208 behemoth windows, all 4,883 binary windows, and 161 injected-binary mixed windows with no text-only false positives. Fixture scan and JS/Python syntax checks passed. Required gate output (verbatim): zsh:1: no such file or directory: scripts/verify (exit 127; scripts/verify is absent on this spike branch).
Author
Owner

Orchestrator: showstopper found. ghostty-web 0.4 takes down the Chromium renderer process within the first ~80–220 KB of the behemoth, binary and mixed fixtures (desktop and phone), even in the isolated Terminal.write() path without the network. Long y\n floods survive. This affects DECISIONS 14 (renderer) and DECISIONS 94 (libghostty-vt in the shim, the same VT core in native form). Follow-up job ghostty-crash: reduce the input to a minimal reproducer, find out if the WASM VT core or the JS renderer is at fault, run the same input through native libghostty-vt (the engine of the shim lane) and through xterm.js, and draft an upstream issue. The other findings are recorded for the tracer: the live path at 0.166 MB/s against 10.8 MB/s for raw write (no flow control in the mockup), no input echo during a flood, the 1006 reconnects on the phone without resume, and a binary heuristic with 0 false positives on Unicode text.

Orchestrator: **showstopper found.** ghostty-web 0.4 takes down the Chromium renderer process within the first ~80–220 KB of the behemoth, binary and mixed fixtures (desktop and phone), even in the isolated `Terminal.write()` path without the network. Long `y\n` floods survive. This affects DECISIONS 14 (renderer) and DECISIONS 94 (libghostty-vt in the shim, the same VT core in native form). Follow-up job `ghostty-crash`: reduce the input to a minimal reproducer, find out if the WASM VT core or the JS renderer is at fault, run the same input through native libghostty-vt (the engine of the shim lane) and through xterm.js, and draft an upstream issue. The other findings are recorded for the tracer: the live path at 0.166 MB/s against 10.8 MB/s for raw write (no flow control in the mockup), no input echo during a flood, the 1006 reconnects on the phone without resume, and a binary heuristic with 0 false positives on Unicode text.
Author
Owner

Starting renderer crash minimisation on branch spike-renderer. Starting HEAD: 37c5e7dbfd; merge-base/base SHA: 4560d9dd64. Scope: spikes/stress/crash/.

Starting renderer crash minimisation on branch spike-renderer. Starting HEAD: 37c5e7dbfda0d766284b97f80cf10c501eebef46; merge-base/base SHA: 4560d9dd64cec2da88254eb5014aa180728f1b5f. Scope: spikes/stress/crash/.
Author
Owner

Starting crash minimization and attribution on branch spike-renderer; base SHA 37c5e7dbfd. I’ll work under spikes/stress/crash/.

Starting crash minimization and attribution on branch spike-renderer; base SHA 37c5e7dbfda0d766284b97f80cf10c501eebef46. I’ll work under spikes/stress/crash/.
Author
Owner

Upstream check (coder/ghostty-web), before the crash job reports. Open items that may be our crash:

  • #141 "WASM memory corruption after freeing terminal that processed multi-codepoint grapheme clusters" (our behemoth has ZWJ, flags and combining stacks)
  • PR #142 "fix: zero-initialize WASM page buffers to prevent memory corruption"
  • #186 hyperlink arena exhaustion spins the write path; #177 viewport row read fix; #189 a single render() throw stops the render loop
  • #182 "chore(release): 0.5.0-rc.0", a release candidate that has not shipped; #197 Kitty graphics addon (relevant to DECISIONS 93)
    Plan (owner, 2026-10-06): if the cause is in the library, we send a fix PR upstream (or add our reproducer to an existing issue or PR). First test whether 0.5.0-rc.0 or PR #142 already fixes the reproducers.
Upstream check (coder/ghostty-web), before the crash job reports. Open items that may be our crash: - #141 "WASM memory corruption after freeing terminal that processed multi-codepoint grapheme clusters" (our behemoth has ZWJ, flags and combining stacks) - PR #142 "fix: zero-initialize WASM page buffers to prevent memory corruption" - #186 hyperlink arena exhaustion spins the write path; #177 viewport row read fix; #189 a single render() throw stops the render loop - #182 "chore(release): 0.5.0-rc.0", a release candidate that has not shipped; #197 Kitty graphics addon (relevant to DECISIONS 93) Plan (owner, 2026-10-06): if the cause is in the library, we send a fix PR upstream (or add our reproducer to an existing issue or PR). First test whether 0.5.0-rc.0 or PR #142 already fixes the reproducers.
Author
Owner

Finding: isolated Ghostty-WASM failure reproduces before network transport. The exact parser-page path completes 16,384 B of behemoth.txt, then Chromium crashes while writing the 81,920–98,304 B interval; spikes/stress/crash/probe.mjs records WASM memory at 11.6 MB and renderer RSS below 150 MiB before exit. Node 22 running ghostty-web 0.4.0's bundled WASM without a DOM/canvas also throws RuntimeError: memory access out of bounds on that interval (spikes/stress/crash/wasm-core.mjs). This currently points at WASM VT core, not canvas-only or host OOM; exact byte/content reduction is in progress.

Finding: isolated Ghostty-WASM failure reproduces before network transport. The exact parser-page path completes 16,384 B of behemoth.txt, then Chromium crashes while writing the 81,920–98,304 B interval; spikes/stress/crash/probe.mjs records WASM memory at 11.6 MB and renderer RSS below 150 MiB before exit. Node 22 running ghostty-web 0.4.0's bundled WASM without a DOM/canvas also throws RuntimeError: memory access out of bounds on that interval (spikes/stress/crash/wasm-core.mjs). This currently points at WASM VT core, not canvas-only or host OOM; exact byte/content reduction is in progress.
Author
Owner

Finding: the behemoth failure is content-dependent. Replacing all combining marks with ASCII makes the 82,541 B prefix complete; retaining only U+0301 COMBINING ACUTE ACCENT or only U+0651 ARABIC SHADDA still traps the bundled WASM. Removing ANSI CSI, removing the long trace, or replacing emoji/symbols alone removes or changes less relevant content; the long trace by itself completes. Controlled variants and results are in spikes/stress/crash/reduce-content.py and .run/content-results.json (ignored runtime evidence). I’m reducing mark count/context next.

Finding: the behemoth failure is content-dependent. Replacing all combining marks with ASCII makes the 82,541 B prefix complete; retaining only U+0301 COMBINING ACUTE ACCENT or only U+0651 ARABIC SHADDA still traps the bundled WASM. Removing ANSI CSI, removing the long trace, or replacing emoji/symbols alone removes or changes less relevant content; the long trace by itself completes. Controlled variants and results are in spikes/stress/crash/reduce-content.py and .run/content-results.json (ignored runtime evidence). I’m reducing mark count/context next.
Author
Owner

Finding: content bisection now has byte-exact first failing prefixes: behemoth 82,541 B (82,540 B completes), mixed 82,144 B (82,143 B completes), binary 106,342 B (106,341 B completes), using 16 KiB writes. A direct Node call to ghostty_terminal_write with one reused WASM input buffer still traps at the same offsets; it bypasses Terminal.write's per-call JS allocation/free and has no DOM/canvas. This attributes the fault to the bundled WASM terminal core, not its canvas or per-write wrapper allocation. Peak browser RSS remained ~148 MiB and WASM memory ~11.6 MB before process exit; no OOM evidence.

Finding: content bisection now has byte-exact first failing prefixes: behemoth 82,541 B (82,540 B completes), mixed 82,144 B (82,143 B completes), binary 106,342 B (106,341 B completes), using 16 KiB writes. A direct Node call to ghostty_terminal_write with one reused WASM input buffer still traps at the same offsets; it bypasses Terminal.write's per-call JS allocation/free and has no DOM/canvas. This attributes the fault to the bundled WASM terminal core, not its canvas or per-write wrapper allocation. Peak browser RSS remained ~148 MiB and WASM memory ~11.6 MB before process exit; no OOM evidence.
Author
Owner

Native check: libghostty-vt 0.2.2 via the crate's Rust binding and Zig 0.15.2 passed all three minimized-prefix candidates, all complete fixtures, and an 80x24→205x56 resize before input. It also passed a drop/recreate canary after each feed. Full-fixture results at 205x56 with 64 MiB max_scrollback: behemoth 50,000,000 B in 1,230 ms (40.65 MB/s), peak RSS 117,360 KiB including the loaded input file; binary 20,000,000 B in 1,038 ms (19.28 MB/s), peak 29,776 KiB; mixed 20,000,000 B in 545 ms (36.70 MB/s), peak 58,568 KiB. The 82,541 B text prefix, 106,342 B raw binary prefix, and 192,253 B browser-decoded binary prefix all completed; no native panic/trap/hang. These runs are on infraternal-dev, i9-9900K, 8 CPUs, load average 2.97, using release builds.

Native check: libghostty-vt 0.2.2 via the crate's Rust binding and Zig 0.15.2 passed all three minimized-prefix candidates, all complete fixtures, and an 80x24→205x56 resize before input. It also passed a drop/recreate canary after each feed. Full-fixture results at 205x56 with 64 MiB max_scrollback: behemoth 50,000,000 B in 1,230 ms (40.65 MB/s), peak RSS 117,360 KiB including the loaded input file; binary 20,000,000 B in 1,038 ms (19.28 MB/s), peak 29,776 KiB; mixed 20,000,000 B in 545 ms (36.70 MB/s), peak 58,568 KiB. The 82,541 B text prefix, 106,342 B raw binary prefix, and 192,253 B browser-decoded binary prefix all completed; no native panic/trap/hang. These runs are on infraternal-dev, i9-9900K, 8 CPUs, load average 2.97, using release builds.
Author
Owner

Finding — full xterm comparison: all three full fixtures completed without a renderer crash in xterm.js 5's DOM default and WebGL, using the 16 KiB write harness at 205×56 (Chrome 153.0.8010.12). Throughput, decimal MB/s: behemoth DOM 0.851 / WebGL 3.217; binary DOM 0.335 / WebGL 0.714; mixed DOM 1.050 / WebGL 3.279. DOM peak renderer RSS was 386,428 / 447,288 / 365,720 KiB; WebGL 260,828 / 242,140 / 276,796 KiB. Harness: spikes/stress/crash/probe.mjs; xterm uses the exact fixture bytes after the same streaming UTF-8 replacement decode. I also captured Chromium's SIGSEGV_ACCERR stack on the Ghostty repro; Crash Reports contained no minidump. One discrepancy under investigation: the new fixed-geometry iPhone probe passes 86,602 bytes while the existing raw-parser harness again crashes after 81,920 bytes.

Finding — full xterm comparison: all three full fixtures completed without a renderer crash in xterm.js 5's DOM default and WebGL, using the 16 KiB write harness at 205×56 (Chrome 153.0.8010.12). Throughput, decimal MB/s: behemoth DOM 0.851 / WebGL 3.217; binary DOM 0.335 / WebGL 0.714; mixed DOM 1.050 / WebGL 3.279. DOM peak renderer RSS was 386,428 / 447,288 / 365,720 KiB; WebGL 260,828 / 242,140 / 276,796 KiB. Harness: spikes/stress/crash/probe.mjs; xterm uses the exact fixture bytes after the same streaming UTF-8 replacement decode. I also captured Chromium's SIGSEGV_ACCERR stack on the Ghostty repro; Crash Reports contained no minidump. One discrepancy under investigation: the new fixed-geometry iPhone probe passes 86,602 bytes while the existing raw-parser harness again crashes after 81,920 bytes.
Author
Owner

Further minimization: the smallest content-controlled text trigger found is spikes/stress/crash/reproducers/minimal-combining-5679.bin (5,679 B; SHA-256 0047de6d657e0a1f69e16b9cdf8c6dc9343c98086c8b44df9276223f4bf3428f). It is 334 repetitions of 5a cc81 5a cc81 5a cc81 5a cc81 5a cc81 0d 0a, followed by 5a. The 5,678-byte prefix completes; adding that final Z traps both through Terminal.write() and direct reused-buffer ghostty_terminal_write in Node, and Chromium logs SIGSEGV_ACCERR at 205x56. It is valid UTF-8 with no escape sequences. Native libghostty-vt 0.2.2 release completes it (3.356 ms, peak RSS 4,292 KiB). This local reduction and replay are documented in spikes/stress/crash/RESULTS.md.

Further minimization: the smallest content-controlled text trigger found is spikes/stress/crash/reproducers/minimal-combining-5679.bin (5,679 B; SHA-256 0047de6d657e0a1f69e16b9cdf8c6dc9343c98086c8b44df9276223f4bf3428f). It is 334 repetitions of 5a cc81 5a cc81 5a cc81 5a cc81 5a cc81 0d 0a, followed by 5a. The 5,678-byte prefix completes; adding that final Z traps both through Terminal.write() and direct reused-buffer ghostty_terminal_write in Node, and Chromium logs SIGSEGV_ACCERR at 205x56. It is valid UTF-8 with no escape sequences. Native libghostty-vt 0.2.2 release completes it (3.356 ms, peak RSS 4,292 KiB). This local reduction and replay are documented in spikes/stress/crash/RESULTS.md.
Author
Owner

Additional checks: native libghostty-vt 0.2.2 completes the 106,342-byte first-failing raw binary prefix after resize (5.354 ms, peak RSS 7,580 KiB, follow-up canary passed). xterm.js 5.5.0 completes the exact 5,679-byte text reproducer in DOM (9.1 ms) and WebGL (7.7 ms), in addition to completing all three full fixtures in both modes. During review, a synthetic 16 KiB all-0xff chunk exposed a direct-probe buffer sizing hazard: decoded UTF-8 was 49,152 B for a 16,384 B allocation. The direct WASM probe now allocates and guards 3x chunk capacity; the synthetic payload completes, while both real WASM reproducers still trap. Details are in spikes/stress/crash/RESULTS.md.

Additional checks: native libghostty-vt 0.2.2 completes the 106,342-byte first-failing raw binary prefix after resize (5.354 ms, peak RSS 7,580 KiB, follow-up canary passed). xterm.js 5.5.0 completes the exact 5,679-byte text reproducer in DOM (9.1 ms) and WebGL (7.7 ms), in addition to completing all three full fixtures in both modes. During review, a synthetic 16 KiB all-0xff chunk exposed a direct-probe buffer sizing hazard: decoded UTF-8 was 49,152 B for a 16,384 B allocation. The direct WASM probe now allocates and guards 3x chunk capacity; the synthetic payload completes, while both real WASM reproducers still trap. Details are in spikes/stress/crash/RESULTS.md.
Author
Owner

Measurement correction from reviewer pass: the native probe's first timer started before terminal construction/resize, so its small-input write times included setup. A pre-fix ordering assertion failed; the timer now starts immediately before the first 16 KiB write, and the release cases were rerun. Updated results: behemoth 50 MB 1,285.939 ms (38.882 MB/s), raw binary 20 MB 921.948 ms (21.693 MB/s), mixed 20 MB 491.873 ms (40.661 MB/s), browser-decoded binary 36,255,730 B 636.964 ms (56.920 MB/s), minimal 5,679 B 1.647 ms, raw binary prefix 106,342 B 5.966 ms, and one-ESC variant 192,253 B 3.553 ms. All completed with follow-up canaries; peak RSS values and method are updated in spikes/stress/crash/RESULTS.md. Earlier native times in this issue thread are superseded by these corrected samples.

Measurement correction from reviewer pass: the native probe's first timer started before terminal construction/resize, so its small-input write times included setup. A pre-fix ordering assertion failed; the timer now starts immediately before the first 16 KiB write, and the release cases were rerun. Updated results: behemoth 50 MB 1,285.939 ms (38.882 MB/s), raw binary 20 MB 921.948 ms (21.693 MB/s), mixed 20 MB 491.873 ms (40.661 MB/s), browser-decoded binary 36,255,730 B 636.964 ms (56.920 MB/s), minimal 5,679 B 1.647 ms, raw binary prefix 106,342 B 5.966 ms, and one-ESC variant 192,253 B 3.553 ms. All completed with follow-up canaries; peak RSS values and method are updated in spikes/stress/crash/RESULTS.md. Earlier native times in this issue thread are superseded by these corrected samples.
Author
Owner

Content reduction within the repeated combining-row family: 333 rows plus final Z (5,662 B) completes; 334 rows plus Z (5,679 B) traps; 335 rows plus Z (5,696 B) traps. The 334 rows without the final Z (5,678 B) complete. This establishes the first failing row count for this controlled pattern, not a global minimum over arbitrary byte strings. The exact inputs are the pattern described in spikes/stress/crash/RESULTS.md; the 5,679-byte artifact remains unchanged.

Content reduction within the repeated combining-row family: 333 rows plus final Z (5,662 B) completes; 334 rows plus Z (5,679 B) traps; 335 rows plus Z (5,696 B) traps. The 334 rows without the final Z (5,678 B) complete. This establishes the first failing row count for this controlled pattern, not a global minimum over arbitrary byte strings. The exact inputs are the pattern described in spikes/stress/crash/RESULTS.md; the 5,679-byte artifact remains unchanged.
Author
Owner

Final result and renderer comparison:\n\n| Trigger | Component | Native affected? | xterm.js affected? | Recommended mitigation |\n|---|---|---|---|---|\n| Valid UTF-8: 334 rows of five Z+U+0301 clusters plus final Z; 5,678 B completes, 5,679 B traps | ghostty-web 0.4.0 bundled WASM VT write/state; reproduces in Node without DOM/canvas and direct ghostty_terminal_write | No. libghostty-vt 0.2.2 release completes with canary | No. Exact payload completes in DOM and WebGL; all full fixtures complete too | Keep arbitrary PTY writes off 0.4.0 until a patched stable build passes these artifacts; owner decides renderer policy |\n| Binary fixture: 106,342 raw bytes normalize to 192,253 UTF-8 bytes; retaining one context-dependent ESC still traps, replacing all ESC with x completes | Same bundled WASM write/state path | No. Raw prefix, normalized variant, and full fixtures complete | No. Full binary fixture completes in both modes | Same pending-fix mitigation; blanket escape stripping is unsafe |\n\nAttribution: the text trap is RuntimeError: memory access out of bounds in Node and Chromium logs SIGSEGV_ACCERR. WASM memory was 10,158,080 B and Node RSS about 74 MB after trap; Chromium started near 131,176 KiB renderer RSS. No OOM evidence or minidump. Native release completed all three fixtures and the reproducers with follow-up canaries. xterm DOM rates were 0.335–1.050 MB/s at 365,720–447,288 KiB peak RSS; WebGL was 0.714–3.279 MB/s at 242,140–276,796 KiB.\n\nMobile note: existing iPhone emulation crashes on full behemoth at 81,920 B. The focused probe completes the 5,679 B trigger at fitted 56x41, but crashes at 205x56; the geometry/harness discrepancy remains unresolved.\n\nNo matching upstream report was found; draft only at spikes/stress/crash/UPSTREAM-ISSUE.md.\n\nFinish: branch spike-renderer head ddd446d9bd. Available checks passed: JS syntax, Python syntax, Rust format, release build, full diff check. Required gate output verbatim: zsh:1: no such file or directory: scripts/verify (scripts/verify fast exited 127 because this branch has no scripts/ directory).

Final result and renderer comparison:\n\n| Trigger | Component | Native affected? | xterm.js affected? | Recommended mitigation |\n|---|---|---|---|---|\n| Valid UTF-8: 334 rows of five Z+U+0301 clusters plus final Z; 5,678 B completes, 5,679 B traps | ghostty-web 0.4.0 bundled WASM VT write/state; reproduces in Node without DOM/canvas and direct ghostty_terminal_write | No. libghostty-vt 0.2.2 release completes with canary | No. Exact payload completes in DOM and WebGL; all full fixtures complete too | Keep arbitrary PTY writes off 0.4.0 until a patched stable build passes these artifacts; owner decides renderer policy |\n| Binary fixture: 106,342 raw bytes normalize to 192,253 UTF-8 bytes; retaining one context-dependent ESC still traps, replacing all ESC with x completes | Same bundled WASM write/state path | No. Raw prefix, normalized variant, and full fixtures complete | No. Full binary fixture completes in both modes | Same pending-fix mitigation; blanket escape stripping is unsafe |\n\nAttribution: the text trap is RuntimeError: memory access out of bounds in Node and Chromium logs SIGSEGV_ACCERR. WASM memory was 10,158,080 B and Node RSS about 74 MB after trap; Chromium started near 131,176 KiB renderer RSS. No OOM evidence or minidump. Native release completed all three fixtures and the reproducers with follow-up canaries. xterm DOM rates were 0.335–1.050 MB/s at 365,720–447,288 KiB peak RSS; WebGL was 0.714–3.279 MB/s at 242,140–276,796 KiB.\n\nMobile note: existing iPhone emulation crashes on full behemoth at 81,920 B. The focused probe completes the 5,679 B trigger at fitted 56x41, but crashes at 205x56; the geometry/harness discrepancy remains unresolved.\n\nNo matching upstream report was found; draft only at spikes/stress/crash/UPSTREAM-ISSUE.md.\n\nFinish: branch spike-renderer head ddd446d9bdcced58c07e88170fd54afab3ac878e. Available checks passed: JS syntax, Python syntax, Rust format, release build, full diff check. Required gate output verbatim: zsh:1: no such file or directory: scripts/verify (scripts/verify fast exited 127 because this branch has no scripts/ directory).
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#18
No description provided.