# Scrollback unit correction — proof audit pass 1

PR [#8766](https://github.com/pingdotgg/t3code/pull/8766) ([GitCafe #8766](https://git.cafe/pingdotgg/t3code/pulls/8766)); issue [#8701](https://git.cafe/pingdotgg/t3code/issues/8701).

Read-only review of `git diff origin/main...issue-8701-existing`: exactly three files, 38 insertions / 4 deletions. Base [3b95e7831b](https://git.cafe/pingdotgg/t3code/commit/3b95e7831b11b17627dff16843bc087bfca397ec); candidate [3a18c58f01](https://git.cafe/pingdotgg/t3code/commit/3a18c58f01871f26d346a18770d3a8e1edf591ad). The checkout remains on main. No checkout changes, tests, servers, browser sessions, comments, or pushes were performed. Only this external audit artifact was written and uploaded.

## Finding and proof boundary

Web and Electron desktop share the WASM core. The candidate changes its `max_scrollback` value from 10,000 to 10,000,000 and renames the constant from rows to bytes. Android's JNI creation path makes the same correction. iOS uses a separate GhosttyKit surface and is untouched. The pinned libghostty-vt revision is `9f62873bf195e4d8a762d768a1405a5f2f7b1697`; its checked-in header describes this option as lines, so the header alone does not establish the claimed byte semantics. The candidate regression test exercises the real WASM engine at 80 columns / 10 rows with 1,200 numbered lines and requires more than 1,000 scrollback rows. This is behavioral evidence of retention capacity when run, not proof of a scrollbar, wheel, touch gesture, native Android behavior, or exact 10 MB allocation. The test was inspected, not executed in this pass.

Sources: [web core](https://git.cafe/pingdotgg/t3code/blob/3a18c58f01871f26d346a18770d3a8e1edf591ad/apps/web/src/terminal/ghostty/core.ts), [core test](https://git.cafe/pingdotgg/t3code/blob/3a18c58f01871f26d346a18770d3a8e1edf591ad/apps/web/src/terminal/ghostty/core.test.ts), [Android JNI](https://git.cafe/pingdotgg/t3code/blob/3a18c58f01871f26d346a18770d3a8e1edf591ad/apps/mobile/modules/t3-terminal/android/src/main/cpp/t3_terminal_jni.cpp), [native module architecture](https://git.cafe/pingdotgg/t3code/blob/3b95e7831b11b17627dff16843bc087bfca397ec/apps/mobile/modules/t3-terminal/README.md).

Existing screenshots are historical context only. They do not establish current candidate behavior. Every current real-app fixture below is PENDING. All interaction/time fixtures require a recording; still images can supplement them but cannot close them.

## Minimal mandatory fixture matrix

Dimensions are client content viewports: CSS pixels for web/Electron; logical dp/points for native. Record actual OS, build SHA, device density, viewport, terminal panel dimensions, font, and measured terminal cols/rows. Keep font and panel height comparable between before/after. Native narrow/wide can be portrait/landscape on one device. These representative dimensions are targets, not a demand for exact hardware. Themes are deliberately paired with widths, not a full Cartesian product: this change does not modify theme rendering.

| ID | Mandatory surface / scope | Viewport; theme | Fixture and pass condition | Evidence type | Current proof / Linux reach |
|---|---|---|---|---|---|
| C1 | Yes — shared web/desktop nonvisual retention logic | Engine 80 cols × 10 rows; fixed test colors | Run candidate `GhosttyTerminalCore scrollback` test; >1,000 retained scrollback rows after 1,200 writes. Run identical regression on base to establish it catches the old limit. | Test output + exact SHA; NOT visual or scrolling proof | Test source inspected; execution PENDING. Linux runnable with dependencies. |
| C2 | Yes — Android nonvisual configuration; iOS scope control | N/A; N/A | Confirm JNI passes 10,000,000 to `max_scrollback`, shared web core matches, and iOS is unchanged. A source check establishes wiring only; native retention remains A1/A2. | Diff/source audit; NOT an Android behavioral test | PASS by source inspection. No native test added in this PR. |
| W1 | Yes — locally served web, live mounted terminal | 1440 × 900; dark | Seed 12,000 lines; wheel/trackpad and scrollbar drag through middle to earliest retained marker, then back to T3-END. Reach materially beyond old ~600-row limit; for this short-line fixture target T3-SCROLLBACK 00001. Record actual earliest marker and cols. | **Recording**, paired base/candidate | PENDING; Linux browser possible, not used. |
| W2 | Yes — hosted web connected to remote environment | 800 × 900; light | Same live seed + scroll traversal at narrow width over normal remote/relay or tunnel connection; record which mode. Confirm readable continuous markers, no premature clamp, and return to T3-END. | **Recording**, paired base/candidate | PENDING; Linux browser and authorized remote environment required. |
| D1 | Yes — actual Electron desktop, live terminal | 1440 × 900; light | Same seed and traversal using wheel/trackpad plus scrollbar. Shared core alone does not close Electron integration. | **Recording**, paired base/candidate | PENDING; Linux Electron possible if display/runtime exists, not established here. |
| D2 | Yes — actual Electron desktop narrow window | 800 × 900; dark | Same narrow-width traversal; resize wide → narrow → wide while scrolled in history; retain readable ordered markers and return to end without losing the surface. | **Recording**, paired base/candidate | PENDING; Linux Electron possible with display/runtime. macOS/Windows remain unverified. |
| A1 | Yes — native Android, live terminal | 390 × 844 dp; dark | Fresh native build containing changed JNI; seed while attached, touch-drag/fling far above recent rows, identify earliest marker and return to end. Validate retention beyond old limit, native gesture and rendering together. | **Recording**, paired base/candidate | PENDING; Android emulator/device possible on Linux if available; none verified. |
| A2 | Yes — native Android wide orientation | 844 × 390 dp; light | Same seed and traversal; rotate portrait → landscape → portrait while in history and verify ordered content and usable return to end. Reseed if rotation recreates surface before live-capacity comparison. | **Recording**, paired base/candidate | PENDING; requires rebuilt Android client and emulator/device. |
| I1 | Yes — unchanged native iOS regression control | 390 × 844 pt; dark | Seed and touch-scroll to older numbered content and back to end; preserve existing iOS behavior. Do not call this evidence of a changed iOS limit. | **Recording**; current control, paired comparison if behavior differs | PENDING; no local iOS simulator on Linux. Requires separately authorized macOS/device/cloud access; web mobile emulation is insufficient. |
| R1 | Yes — replay/remount boundary on one web client | 1440 × 900; dark | After W1 live proof, recreate terminal surface, then separately reconnect/reload same PTY without rerunning seed. Record earliest marker and T3-END for each. Validate bounded replay and usable scrolling, not survival of all 12,000 lines. | **Recording**, labeled remount vs reconnect | PENDING; Linux web possible. This diagnoses limits, not enlarged live engine capacity. |

W1/W2 cover local and remote paths without duplicating every transport. Desktop OS variants, additional browser engines, Android ABIs/devices, iOS wide/light, and every relay/tunnel combination are unverified beyond recorded fixtures; list the actual coverage rather than implying universal proof. The mandatory rows are the minimum approval evidence for this audit, not a claim they were executed.

## Real-app seeding procedure (future authorized verification)

1. Use an isolated development home with a copy of real stopped-thread data; never run against live userdata. Use the normal terminal drawer/screen of a real thread. Keep a unique fixture label and record the candidate/base SHA. Build Android's native client so the JNI change is present; a JS refresh alone cannot apply it.
2. Attach the actual terminal surface BEFORE producing output. Use an otherwise idle shell in the main screen, not an alternate-screen application. Set the target viewport/theme and panel size first. Do not close, switch away from, or remount the terminal until live proof is captured.
3. Run this shell command in the app terminal on each compared build. It sends ESC c (RIS) first, emits 12,000 short numbered lines, then reports the PTY columns. It resets terminal presentation/history; it does not clear the server transcript store.

```sh
printf '\033c'
i=1
while [ "$i" -le 12000 ]; do
  printf 'T3-SCROLLBACK %05d\n' "$i"
  i=$((i + 1))
done
printf 'T3-END cols=%s\n' "$(tput cols)"
```

4. Wait for visible T3-END, then capture an uninterrupted recording showing the end, the input gesture/scrollbar traversal, older and earliest reachable numbered markers, and the return to end. Record measured rows as well as the T3-END cols value. At these viewports keep at least 24 terminal columns so the short markers do not wrap. Narrow/wide pixel dimensions alone do not establish terminal geometry. Compare base/candidate under the same geometry and output.
5. For resize/rotation, capture the transition and resulting content in the same recording. If a surface is recreated, label it and reseed before claiming live-capacity proof. Keep reconnect/remount proof separate as R1. A static screenshot of a large scrollbar or marker is insufficient to prove interaction or continuity.

## Retention limits and interpretation

The native/WASM scrollback budget is distinct from the client transcript and server replay. The shared client transcript defaults to 512 KiB (524,288 bytes). These 12,000 ASCII marker lines are approximately 240 KiB (plus reset, line-ending translation and end marker), deliberately below that limit; do not add verbose padding. This controls transcript trimming as a confounder but does not promise native engine retention at every geometry. Terminal scrollback bytes include implementation storage costs and width-dependent cell storage: 10,000,000 bytes is neither 10,000 rows nor a general guarantee of 12,000 rows. Record the actual oldest row; if the first short marker is missing at representative widths, investigate before declaring success.

The server independently defaults to 5,000 history lines and an 8 MiB byte ceiling. A fresh attachment/reconnection can therefore replay only a recent tail, roughly the last 5,000 lines including T3-END and shell/prompt output, even when the mounted live engine previously held the full run. Do not assert an exact oldest numbered line such as 07001: newline accounting, prompts and output after the marker affect it. A surface rebuilt from the client transcript is bounded by that transcript instead. ESC c resets the emulator, not those independent stores. Neither replay limit is changed by this PR, and replay-truncated history cannot be recovered by increasing engine scrollback.

Sources: [client buffer default](https://git.cafe/pingdotgg/t3code/blob/3b95e7831b11b17627dff16843bc087bfca397ec/packages/contracts/src/terminal.ts), [client session handling](https://git.cafe/pingdotgg/t3code/blob/3b95e7831b11b17627dff16843bc087bfca397ec/packages/client-runtime/src/state/terminalSession.ts), [server history limits and snapshot](https://git.cafe/pingdotgg/t3code/blob/3b95e7831b11b17627dff16843bc087bfca397ec/apps/server/src/terminal/Manager.ts).

## FRICTION

- The ABI header says lines while the fix relies on byte semantics. Source wiring is verified; the candidate regression still needs execution against both revisions, and Android needs native proof.
- The checkout is main, not the candidate. Historical images and inspection cannot close current proof rows. No tests or real-client actions were authorized in this read-only pass.
- Linux can potentially cover web, Linux Electron and Android; availability was not tested. Native iOS and macOS/Windows desktop remain unverified without other hosts/devices.
- The 512 KiB client transcript and 5,000-line server replay are independent constraints; reconnect evidence must not be presented as live scrollback capacity.

Verdict: configuration scope is coherent; current scrolling proof remains pending.
