# Proof audit pass 1: fixture matrix

PR [#25828](https://git.cafe/pingdotgg/t3code/pull/25828), branch `t3/mobile-terminal-copy-paste`.
After: [49f116d](https://git.cafe/pingdotgg/t3code/commit/49f116db6e3cd1c4b3c2974c017ed1d5e3280609).
Before / actual merge-base: [f120c51](https://git.cafe/pingdotgg/t3code/commit/f120c51134c6b5f20b42f51cda84e3e6da681a5f).
Read from `/home/ubuntu/wt/pr-25828`; 10 changed files. This is code-derived pass 1, not a review of old captures. Every capture status below is pending or optional/unverified. No app, browser, simulator, server, or device lease was started by this auditor; no tests were run and no repository files were changed.

## Findings first

1. **Proof is not sufficient yet: no current-round captures have been supplied.** The minimum below is 13 real-app fixture rows, with explicit device/theme variants. Most interaction sequences can share one recording and extracted checkpoints; the table does not demand one fresh device run per row. The remaining 6 real-app rows and 7 harness rows document the rest of the input space and are **not required** for the minimum visual claim set.
2. The feature spans **iOS native terminal**, **shared RN terminal controls on iOS and Android**, and the **shared iOS Sheet container**. Android native code and `Sheet.android.tsx` are unchanged, but the Android Paste icon and fixed button are affected. Web and desktop have no changed render paths. This is a terminal feature, independent of agent provider; it does not require separate Codex/Claude/etc. runs or real paid agent turns. The same typed terminal RPC drives direct/remote connections; use one isolated remote environment on all devices, not a separate connection-mode capture matrix.
3. Do not accept a Select Text screenshot as proof of links or Copy All. Record the external link opening and return, then Copy All closing the page and the copied text pasted into an unsent composer draft. Do not accept a toolbar screenshot as proof of Paste, modifier reset, newlines, or responsiveness.
4. Long-press with the keyboard dismissed is a separate real-app case. The menu reuses only a **suggested system Paste command**; `canPaste` alone does not insert one. Whether UIKit supplies that command without a first responder needs the actual device, not a mocked menu or a code assertion. Record both keyboard states.
5. The attach snapshot contains **visible terminal output**, not all scrollback. Populate enough visible short numbered rows that the resulting line-picker overflows; determine its real last line and record that line reached. Do not demand a fictional 300th captured line on a small phone. The iOS native selection page is UIKit `.pageSheet`; the attach line picker and composer context preview use the changed RN `Sheet`. They are different paths.
6. Freeze claims need completion and recovery checkpoints: many title/BEL messages, visible completion, another successful typed command, then Back to chat. Theme-switch **during replay** needs independent evidence of overlap (driver/native log milestones); switching just after opening is not proof that parsing was active. A single `tickApp()` before free cannot establish that every concurrent teardown race is fixed. Label that adversarial case optional/unverified, not covered by a settled-output run.
7. The native menu tests `!readOnly && hasStrings`, not session status. An exited but writable surface can still offer Paste; JS filters `onPaste` with `isRunning && !readOnly`. Do not assert that Paste disappears on exit. The focused JS regression test covers forwarding guards, but is not visual proof of native menu state.
8. The brief's system-appearance statement needs configuration: current appearance code also supports an explicit light/dark override. Set **Settings → Appearance → mode System** before changing device appearance. Keep palette, font scale, terminal font, orientation and keyboard configuration identical in each before/after pair.

## Exact minimum capture contract

- Devices named exactly: **iPhone 18 Pro**, **iPad Pro 13-inch (M5)**, **Pixel 9**. Use iOS/iPadOS 27.2; log the actual Android OS. Do not reuse an older device name for a new pooled lease.
- `P` below means full-device **portrait** at the device's actual native screenshot size; log pixels, logical viewport, DPR, OS, font scale, safe-area and app column width from the run. `W` means the iPad's full-width app column in portrait. Do not assume a viewport from the device name. A split-column/landscape variant is optional O06.
- Every required row is a **recording** because it depends on interaction or time. Capture before and after sequences with matched data and appearance; extract paired stills at the named stable checkpoints. A still alone cannot satisfy a recording row. Multiple rows may cite timestamp ranges in one recording. Before lacking a menu is a legitimate baseline; do not fabricate a before selection page.
- Record whole-device UI, including keyboard, sheet edges and home indicator. Preserve raw evidence. If producing pixel diffs, report full-frame versus app-region noise/masks honestly; status bar clock/battery noise is not a product change and is not evidence of exact pixel equality. No global zero-diff criterion is invented here.
- Required per-device/theme cells: iPhone dark **T01 N01 S01 A01 A02 C01 F01 F02 F03**; iPhone light **T01 N02 S01 A01 F02**; iPad dark **I01**; iPad light **A03**; Pixel dark and light **T02**. F02 is one dark→light→dark sequence, not two separate stress runs. A01 can be clipped from T01/N02; C01 follows A02. This is 17 row/device/theme cells when F02 counts once, with substantial clip reuse.
- Suggested capture order within ~120 minutes: iPhone normal dark/light flows (35 min), iPhone freeze/replay/font (20 min), iPad combined flows (20 min), Pixel both appearances (15 min); reserve 30 min for setup, device friction, uploads and retries. These are allocations, not measured runtimes. Start no optional harness work until the required actual-app clips are uploaded.
- After must use a native build of this head; iOS reports `hardwareKeyRevision: 4`, `outputRevision: 1`. Baseline reports hardware revision 3. An OTA/Metro reload alone cannot install the Swift changes. Record native build/fingerprint and JS SHA. Capturer uses the approved native-install/device workflow; this auditor did not install anything.

## Real-app seeding recipes used by the table

All seed data is synthetic, on the lead's isolated development state; never use live `~/.t3/userdata` or upload pairing credentials. This matrix is a recipe, not an executed seed script.

**BASE:** Create one project rooted at a scratch fixture workspace and a thread through the normal app or its existing orchestration RPC. Open Terminal from the thread header. The navigation screen is `ThreadTerminal` with `{environmentId, threadId, terminalId: "term-1"}`; linking path is `threads/:environmentId/:threadId/terminal` with `terminalId` as an optional query/route parameter. Reopening without a terminal id selects a running known session, or defaults to `term-1`; explicit ids, new-shell, project-script, hardware shortcut and showcase entry points converge on the same screen. Use a real writable running PTY and a small deterministic prompt. Record whether the server host OS is Linux/macOS/Windows: this changes CTRL/ALT versus CMD/CTRL key labels, not device platform.

An existing authenticated RPC driver may call `terminal.open({threadId,terminalId,cwd,cols,rows})`, `terminal.write({threadId,terminalId,data})`, `terminal.resize({threadId,terminalId,cols,rows})`, `terminal.clear({threadId,terminalId})` and `terminal.close({threadId,terminalId})`. `data` is terminal **input**, not injected output: send a command ending in CR that prints the fixture. `terminal.attach`/`terminal.observe` use the same target and deliver the real PTY output. Use the repository RPC client and configured session; do not guess a raw WebSocket envelope or serialize credentials into the evidence. Driver logs should identify fixtures, terminal target, sizes and observed milestones only.

**TEXT:** Print `COPY_BEGIN`, short Unicode text, blank line, a long single URL `https://example.org/?fixture=t3-terminal-copy-paste&padding=abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789`, then `COPY_END`. Put the URL within the current viewport, long enough to wrap on the phone; keep the entire short fixture visible. A normal `print()` of one long URL lets terminal soft wrapping be tested; explicit newlines inside the URL would be a different input. Use a public nonsensitive URL. Re-print after a resize so each device gets the intended visible text. For selection snapshot O06, print distinct `BEFORE_MENU` and then `AFTER_MENU` markers while the menu is open.

**PASTE:** Put the following exact two logical lines on the **device** clipboard, no newline at the end (literal LF between them):

```text
printf 'PASTE_ONE\n'
printf 'PASTE_TWO\n'
```

Use a plain non-bracketed shell line editor or a synthetic PTY input-echo process that reports CR/LF bytes. At the expected checkpoint, line one has run and printed `PASTE_ONE`; the second command is intact at the prompt. Send Enter and show `PASTE_TWO`. A payload that becomes literal `J`, a flattened line, or two concatenated commands fails. A direct shell output fixture which only prints those words without actual device Paste does not satisfy this recipe. For fixed-key modifier tests, activate CTRL (or CMD), tap fixed Paste, then type an ordinary `x`; show the modifier unhighlighted and `x` delivered normally. Clear the unsent input afterwards. iOS expo-clipboard fixed-button Paste may show the existing Allow Paste prompt; accept it explicitly. Do not confuse that with system-menu Paste, which is intended to avoid the prompt.

**LINES:** At the measured terminal grid, print short `LINE_001`…`LINE_NNN`, with `LAST_VISIBLE_<n>` at the bottom and one Unicode/blank row. Fill most rows without wrapping and keep a known last sentinel visible. Dismiss keyboard, remeasure/reprint if necessary, and request capture. The line-picker uses each captured row as a generously padded touch target, so a normal screenful should overflow. If it does not, use a smaller terminal font through the existing terminal menu and regenerate. Record the **actual captured** line count and final sentinel. Use a selection within the context limit for minimum proof; do not create huge hidden scrollback and assume it was captured.

**BURST:** In the real PTY run a Python/Node command that emits 2000 OSC title updates plus BEL (`ESC ] 0 ; title-i BEL` then another BEL), prints progress marks and a final `BURST_DONE`, then returns to the prompt. Use the same driver/input on before and after. Observe `BURST_DONE` in the terminal itself before the settled-output recovery test. Then type `printf 'TYPE_OK\n'`, show `TYPE_OK`, and press Back to the actual chat. Do not equate a server-side “bytes written” log with native parsing complete. If before hangs, capture the failed interaction; the lead owns recovery of its spawned app processes.

**REPLAY:** Seed a retained history containing many title/BEL messages and readable numbered lines, then detach/reopen the same terminal so that its initial streaming reset replays history. Coordinate system-appearance change while the replay is demonstrably active; retain logs/markers establishing start → appearance change → replay completion. An ongoing output stream can separately prove a live reset, but cannot substitute for a specifically claimed replay overlap. If native parse milestones are unavailable, label overlap unverified and report only the observed open/switch/type/Back sequence. F03's font/clear path can run during a logged ongoing output stream without claiming initial-replay overlap.

**CONTEXT:** From A02 attach a modest nonempty line range, return to the chat draft, tap the terminal context chip. This reaches `ComposerEditor → ComposerContextSheet`, with long terminal text and Remove from draft at its end. Drag the sheet from half to full height, scroll to the final sentinel/footer and close. It tests a second real caller of `Sheet` without an agent turn. Optionally open a synthetic existing message context chip for the `ThreadFeed → ComposerContextSheet` no-remove variant; never start a paid provider just to seed it.

## Required fixture table

Legend: **Y** = directly affected; **R** = regression through a changed container/lifetime. All rows here are real app, required, `recording`, and **PENDING — no captures yet**.

| ID / named fixture | Diff | Minimum viewport × system theme | Exact data / state and interaction | Required checkpoints / acceptance | Route / seed | Status |
|---|---|---|---|---|---|---|
| T01 iOS fixed Paste at strip edges | Y | iPhone 18 Pro P × dark, light | BASE + PASTE; keyboard up, writable/running; scroll key strip start→middle→end, pending modifier then fixed Paste, type x, dismiss and show keyboard | Paste and Dismiss remain fixed at every offset; correct scroller fades; old inline Paste absent after; paste runs exact two lines and clears modifier; no overlapping/truncated keys | BASE; native keyboard focus and real device clipboard | PENDING, required recording |
| T02 Android fixed Paste + clipboard glyph | Y | Pixel 9 P × dark, light | Same as T01; Linux host CTRL/ALT is sufficient; paste while CTRL active; keyboard hide/show | Clipboard glyph rendered (not missing fallback), fixed Paste and Dismiss stay visible while strip scrolls; complete PASTE + x checkpoint, modifier no longer active; Android bottom controls recover | BASE + PASTE through native Android terminal | PENDING, required recording |
| N01 iOS long-press with keyboard up | Y | iPhone 18 Pro P × dark | TEXT visible + text clipboard + focused keyboard; long-press at visible output, choose system Paste | System menu shows Paste and Select Text without hidden-field extra actions; paste result proves newlines; keyboard/layout remain usable | BASE + TEXT + PASTE | PENDING, required recording |
| N02 iOS long-press with keyboard down | Y | iPhone 18 Pro P × light | Same text/clipboard, dismiss keyboard first, long-press and choose Paste without tapping/focusing first | Menu is actually available; keyboard stays down when opening the menu; two-line paste result appears. If Paste missing, record it as failure, not a harness substitution | BASE + TEXT + PASTE; use Dismiss keyboard | PENDING, required recording |
| S01 iPhone Select Text / URL / Copy All | Y | iPhone 18 Pro P × dark, light | TEXT visible; Select Text; select a substring and Copy; tap long detected URL and return; reopen then Copy All; reopen then Done | Native page title and both buttons visible; monospaced selectable output with wrapped URL; external navigation to intended URL; copied substring and Copy All result pasted into an unsent draft; Copy All and Done each close page; terminal still responds on return | BASE + TEXT; paste copied data into normal chat composer, do not send | PENDING, required recording |
| I01 iPad wide terminal + native paste routes | Y | iPad Pro 13-inch (M5) W × dark | TEXT + PASTE; keyboard shown and dismissed; long-press Paste; show keyboard and invoke available native shortcut-bar Paste; Select Text, Copy All and verify copied result | Menu anchored and unclipped at tablet width; system Paste works in both keyboard states; shortcut bar routes through paste, preserving LF; selection page fills its page sheet and output/link/buttons remain readable. Do not call simulator text insertion hardware Cmd+V | BASE + TEXT + PASTE on actual iPad app | PENDING, required recording |
| A01 iPhone attach row / home indicator / keyboard | Y | iPhone 18 Pro P × dark, light | Running terminal; dismiss→show→dismiss keyboard, then tap Attach visible output | Row and text clear home indicator; no overlap with floating Show keyboard button; row is absent while keyboard is visible; returns on dismissal; tap reaches real line-picker | BASE + LINES; clips may be shared with T01/N02/A02 | PENDING, required recording |
| A02 iPhone bounded attach sheet / last line | Y | iPhone 18 Pro P × dark | LINES with overflowing real viewport snapshot, keyboard down; open half-height sheet, drag full, scroll to real last sentinel; select range in both orders and attach | Header/close/body/footer bounded; last row and Attach selected output reachable at half/full; line selection highlights correctly and footer remains usable; successful attach returns to chat with context chip | BASE + LINES; native captureRequest → TerminalContextSheet | PENDING, required recording |
| A03 iPad attach sheet / wider safe area | Y | iPad Pro 13-inch (M5) W × light | LINES regenerated for tablet grid; keyboard down; open/expand/scroll/select/attach and close/reopen | Tablet sheet not blank or collapsed; actual last line reachable, footer and home indicator clear; selected text is attached | BASE + LINES; capture actual viewport, not 300 asserted rows | PENDING, required recording |
| C01 second Sheet caller: draft context | R | iPhone 18 Pro P × dark | CONTEXT from A02, long terminal label/text, removable draft context; half→full, scroll bottom, horizontal text scroll, close/reopen | Body reaches actual last sentinel and Remove from draft; title/close visible; content not clipped below sheet; Remove changes draft when exercised | Chat draft → terminal context chip → ComposerContextSheet | PENDING, required recording |
| F01 titles/bells complete then type and Back | Y | iPhone 18 Pro P × dark | BURST, 2000 title/BEL messages; wait for actual visible completion then type TYPE_OK and Back | Before/after show same workload; after reaches BURST_DONE, TYPE_OK, and actual chat without a frozen UI; stills cannot establish this | Real PTY through terminal.write/input command; raw clip plus workload transcript | PENDING, required recording |
| F02 appearance reset during history replay | Y | iPhone 18 Pro P × dark→light→dark | REPLAY; system appearance change during verified native parsing, then normal input and Back | No blank/stale surface after reset; output retained and recolored, TYPE_OK and chat reached; overlap evidence required for “during replay” claim. Report partial/unverified if only opening/switching was shown | Retained terminal history + reopen; OS appearance set with mode System | PENDING, required recording |
| F03 font/stream reset/clear recovery | Y | iPhone 18 Pro P × dark | Ongoing numbered output; Terminal options A+/A−; toolbar Clear, print FONT_OK; switch to another terminal and back, then Back | Font visibly resizes and retained stream redraws; clear/reset removes old screen; FONT_OK appears; terminal-key switch/remount and Back remain responsive | BASE; terminal menu font controls, clear action, new/select existing terminal | PENDING, required recording |

There are **17 cells**: T01 2, T02 2, N01 1, N02 1, S01 2, I01 1, A01 2, A02 1, A03 1, C01 1, F01 1, F02 1, F03 1. Do not multiply the iPad flows by both themes or every phone flow by every width. Optional rows below do not block the minimum visual claim set; any observed failure still must be reported.

## Remaining real-app equivalence fixtures — optional, not required

Each row involving several equivalent subcases names its visually distinct checkpoints explicitly; this is not an unbounded Cartesian product.

| ID / fixture | Diff | Viewport × theme if captured | State / interaction and expected visual shape | Real-app seed / limit | Status / medium |
|---|---|---|---|---|---|
| O01 clipboard guard menu shapes | Y | iPhone 18 Pro P × dark | Nonblank TEXT + empty or image-only clipboard → Select Text only; blank/whitespace viewport + text clipboard → Paste only; blank viewport + empty/image clipboard → no menu. Attempt Paste with empty string → no input. Also empty native capture → existing No terminal output alert | Device clipboard setter can clear text/set an image; a PTY command can clear display and hide cursor/prompt using raw-mode fixture. A normal shell prompt is not an empty viewport. No native read-failure is implied | OPTIONAL/unverified real app, recording |
| O02 read-only observer | Y | iPhone 18 Pro P × dark | Read but no operate grant, nonempty output and text clipboard; Select Text allowed, Paste absent; no fixed accessory/show-keyboard action, attach row still available; no writes | Separate isolated connection with `terminal:read` and no `terminal:operate`, existing session, terminal.observe; do not approximate by disabling a UI button. If permission setup not feasible, JS guard can be checked in H04 but label that harness-only | OPTIONAL/unverified real app, recording |
| O03 exited/error/starting session | Y/R | iPhone 18 Pro P × dark | Starting shares isRunning guard with running. Exited/error writable native view may still offer Paste and toolbar if keyboard up, but sends no bytes. Existing automatic exit navigation may leave screen; record that rather than assuming a stable stopped view | `terminal.close` or shell exit/error in an isolated target and reopen explicit id with retained history if product allows. Use H04 for stable state injection if immediate navigation prevents it; starting normally needs driver coordination | OPTIONAL/unverified real app or explicitly labelled H04 harness, recording |
| O04 worktree-setup Sheet caller | R | iPhone 18 Pro P × light | Details sheet: short completed stage list; running list with elapsed/progress/output tail + Cancel and Work locally; failed list with error/output and no cancel. Expand, scroll last control, close | Real isolated worktree setup event sequence from project setup script, Details button in WorktreeSetupCard. No generic seed switch exists; can use H07 synthetic snapshot if running/failed stages cannot be seeded within budget | OPTIONAL/unverified real app when seeded, otherwise H07 harness recording |
| O05 short generic sheet and context alternatives | R | iPhone 18 Pro P × light | ComponentCatalog's Example sheet with Overview/Activity (short content, half/full, close). Composer context from feed lacks Remove footer; missing/unknown record shows only explanatory text; rich element/review/media content can overflow; file/image/PDF/video early returns do not use changed Sheet when a viewer source exists | Dev route `dev/components`, feedback page → New thread example; feed context requires a synthetic persisted message, not paid agent. Normal context recipes in inventory below; harness only if actual data cannot be seeded | OPTIONAL/unverified real app, recording |
| O06 width, snapshot, accessibility variants | Y/R | iPad Pro 13-inch (M5) narrow/split column × light; phone P × dark for others | Narrow column/rotation with keyboard/Sheet; largest/smallest terminal fonts; long Unicode/URL/very long single row; open menu while output changes then Select Text (frozen snapshot stays BEFORE_MENU); scrolled-back viewport snapshot; Reduce Motion suppresses page animation; normal tap focuses and pan scrolls without unintended paste | Native app resize/rotation, terminal menu font range 6–14 pt, public TEXT, app/system accessibility settings; streaming shell prints AFTER_MENU only after menu open. Width is measured app column, not full iPad screen. Use H05 only for unobservable presentation guards | OPTIONAL/unverified real app, recording |

## Harness-only equivalence fixtures — all not required

These are **labelled harness captures**, not proof that the production UI reaches the state. No harness exists or was created in this pass. A capturer may build one only outside the repository under its own authorized scratch; do not replace required real-app rows with it.

| ID / fixture | Diff | Viewport × theme if captured | Exact branches / data to force | Why harness / seed | Status / medium |
|---|---|---|---|---|---|
| H01 UIKit suggested menu trees | Y | iPhone 18 Pro P × dark | Suggested flat Paste command; nested submenu Paste; unrelated commands only; no suggestions. Cross with text/nontext snapshot and readOnly/hasStrings gates. Expected first matching system Paste retained, unrelated suggestions dropped, Select Text appended only for nonblank text, nil menu if no children | Native delegate harness calls menu construction with UIKit elements; normal app cannot control OS suggested-action tree. Missing system Paste with valid clipboard is especially relevant to N02; do not infer device behavior from harness | OPTIONAL/unverified harness, recording |
| H02 native unavailable / creation or capture failure | R | iPhone 18 Pro P × dark | Resolver missing/throw → text fallback (`Viewing terminal output`, running fallback notice, stopped Open terminal notice); empty buffer shows `$ `; running/writable input enabled versus disabled. Native Ghostty runtime/config/app/surface/read-text failure → blank surface or empty capture; zero-sized/unattached window defers creation; failed presentation stays closed | Inject resolver/Ghostty API failures in labelled native/RN harness; supported new native binary does not expose these toggles. Fixed accessory remains shared, native attach hidden in fallback; fallback gets no native onPaste | OPTIONAL/unverified harness, recording |
| H03 legacy and streaming buffer shapes | Y/R | iPhone 18 Pro P × dark | `outputRevision` absent/0 → initialBuffer; revision 1 → writer. Legacy empty/append/nonprefix buffers; stream reset/nonreset, null output/version 0, first resize not yet ready; font/appearance/themeConfig updates with same/different values | RN/native harness sets props/revision and feeds chunks. Current app's native module is revision 1; no setting switches a current build into legacy revision. Assert eventual retained text/size rather than a screenshot of prop plumbing | OPTIONAL/unverified harness, recording |
| H04 paste errors, limits and temporal guards | Y/R | iPhone 18 Pro P × dark; Pixel 9 P × dark if needed | Running+writable forwards onPaste; readOnly/stopped filters; starting treated live by route. Clipboard read rejects → logged error, no new alert. Empty clipboard no write; CRLF/LF/CR/tab/control/Unicode payload; >65536 UTF-16 units splits without surrogate damage; repeated paste, delayed read after exit/restart, failed write abort. mac CMD+V versus non-mac CTRL+V handler | Inject controlled reader/write promises/native event and status in existing harness; clipboard read error and lifecycle timing are not reliable device fixtures. Existing NativeTerminalSurface and terminalPaste/input tests are functional evidence, not native capture | OPTIONAL/unverified harness, recording plus event transcript |
| H05 presentation/lifecycle guards and input sources | Y/R | iPhone 18 Pro P × dark | No window, responder chain lacking presenter, other presented controller, being-presented/dismissed or transition active → Select Text does not present; weak callback after view deallocation no update; actual hardware Cmd+V, text-field shortcut paste, view Paste all converge on onPaste | Native harness for responder/transition/deallocation; hardware fixture only counts if real device input is supported. iPad shortcut bar is already required in I01; simulator text injection is not Cmd+V. Keyboard restoration after Select Text is not promised by code | OPTIONAL/unverified harness, recording plus lifecycle transcript |
| H06 adversarial teardown during unresolved mailbox parsing | Y | iPhone 18 Pro P × dark | One pending output chunk with >64 titles/bells, free/reset while parsing before completion; queued callbacks after disposal; compare input/destroy progress milestones | Deterministic native/Ghostty synchronization harness needed for the exact race. There is one pre-free tick, no fence guaranteeing parsing is done. F01 proves the settled case only; do not generalize it to this row | OPTIONAL/unverified harness, recording plus synchronized transcript |
| H07 arbitrary Sheet child data and attach limits | R | iPhone 18 Pro P × light | Worktree snapshot fixtures in O04 if unseedable; line-picker blank/one line/many lines, whitespace-only or over-limit selected text disabled with helper, at-limit enabled; context limit-exceeded insertion alert; missing/unknown context record and all rich content branches | Instantiate actual Sheet caller with synthetic contracts in a labelled RN native harness. Max selected chars is `COMPOSER_CONTEXT_TERMINAL_TEXT_MAX_CHARS` (64000 at this head), and composer capacity failure depends on existing draft contexts; do not inject via invented terminal output RPC | OPTIONAL/unverified harness, recording |

## Component/caller and branch inventory from the diff

Source links are pinned to the audited head. “Collapsed” means the branch does not justify multiplying the required device/theme matrix, not that it was runtime-verified.

| Changed component / boundary | Every production caller and branch that matters to rendering / interactions | Fixture mapping / collapse |
|---|---|---|
| [T3TerminalModule.swift](https://git.cafe/pingdotgg/t3code/blob/49f116db6e3cd1c4b3c2974c017ed1d5e3280609/apps/mobile/modules/t3-terminal/ios/T3TerminalModule.swift#L4) | Expo `T3TerminalSurface` view → T3TerminalView only. Props: terminalKey, initialBuffer, fontSize, focusRequest, readOnly, autoFocus, appearanceScheme, themeConfig, three colors, captureRequest. Registers onInput/onResize/onCapture/new onPaste and writeOutput. Revision values affect build/output pipeline, not a visual screen of their own | All real native rows; H03 for legacy; H04 for guarded paste. Build revision log required |
| [T3TerminalView.swift](https://git.cafe/pingdotgg/t3code/blob/49f116db6e3cd1c4b3c2974c017ed1d5e3280609/apps/mobile/modules/t3-terminal/ios/T3TerminalView.swift#L198) | Only Expo wrapper caller. Long-press begins only; empty/whitespace/nil viewport text collapses to no Select Text. Text+clipboard has both actions; text only, paste only, neither differ. ReadOnly blocks native menu Paste/focus; live status is JS-only. Suggested menu recursive Paste search. Frozen snapshot while streaming. Tap focuses; pan scrolls; long-press focus gesture waits. UITextField paste and UIView paste emit onPaste; typed Return uses separate CR path. Capture positive changed token, empty/error result, ASCII row-end trim, repeated requests. Surface creation failure/zero-size/window, append versus reset, initial legacy versus streamed output; changes to font/appearance/themeConfig/terminalKey reset; layout rebuild delayed; app tick before feed/free and async wakeup; weak callback lifetime | N01/N02/S01/I01, A01–A03, F01–F03; O01–O03/O06, H01–H06 |
| [TerminalTextSelectionViewController.swift](https://git.cafe/pingdotgg/t3code/blob/49f116db6e3cd1c4b3c2974c017ed1d5e3280609/apps/mobile/modules/t3-terminal/ios/TerminalTextSelectionViewController.swift#L3) | Sole caller presentTextSelection in T3TerminalView; requires eligible presenter/window. UINavigationController pageSheet; title, left Copy All, right Done, read-only selectable UITextView, safe-area bounds, inset, detected links. Font max(fontSize,14): normal terminal 6–14 means minimum 14 in selection page. System colors, portrait/tablet/wrapped/long/Unicode/blank lines; Reduce Motion changes presentation/dismiss animation | S01/I01; O06 and H05. No empty/loading/error UI inside this controller; it receives a nonblank frozen snapshot |
| [NativeTerminalSurface.tsx](https://git.cafe/pingdotgg/t3code/blob/49f116db6e3cd1c4b3c2974c017ed1d5e3280609/apps/mobile/src/features/terminal/NativeTerminalSurface.tsx#L44) and [nativeTerminalModule.ts](https://git.cafe/pingdotgg/t3code/blob/49f116db6e3cd1c4b3c2974c017ed1d5e3280609/apps/mobile/src/features/terminal/nativeTerminalModule.ts#L29) | Export TerminalSurface's only production caller is ThreadTerminal. ReadyTerminalSurface keyed by terminalKey; native resolved versus text fallback; streaming supported versus initialBuffer; writer readiness based on first resize; buffer null/version0 versus history; supplied font/theme or appearance-derived defaults; autoFocus/readOnly/focusRequest; onPaste forwarded only if running+writable. Optional onPaste does nothing when absent; no added visual loading/error component. Expo native binding/type change has no standalone render caller | All terminal rows; O02/O03, H02–H04. Existing test caller RawView mock is not a shipped screen |
| [ThreadTerminalRouteScreen.tsx](https://git.cafe/pingdotgg/t3code/blob/49f116db6e3cd1c4b3c2974c017ed1d5e3280609/apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.tsx#L272) | Stack's ThreadTerminal screen. Entry points: ThreadRouteScreen open existing/default/new terminal and project script, HardwareKeyboardCommandProvider, ShowcaseCaptureCoordinator; session select/new uses Stack replace, exit fallback uses useTerminalExitNavigation. All converge on private ThreadTerminal. Missing thread during connection → loading or unavailable; missing workspace → unavailable; environment not initialized → loading. Within body: disconnected notice/retry; checking session permissions; permission unavailable/error; launch/session loading/error; no read-only sessions; read-only observe error; otherwise terminal. These unchanged nonterminal branches never render changed controls. Surface status starting/running versus exited/error, operate/read grants, target/default/bootstrap, keyboard showing and accessory dismissed state. Host OS determines two modifier labels. Counts of sessions/long labels affect header menu only, unchanged. Native/Android/platform/keyboard branches govern bottom attach/show-keyboard/accessory. New fixed Paste clears pending modifier; existing modifier-V uses same paste session. capture nonblank opens TerminalContextSheet; empty shows alert; attach closes and navigates back | T01/T02, N01/N02, A01–A03, F01–F03; O01–O03. Unchanged loading/error/header-menu counts are inventoried but need no new proof-only fixtures; fallback H02 |
| [AppSymbol.tsx](https://git.cafe/pingdotgg/t3code/blob/49f116db6e3cd1c4b3c2974c017ed1d5e3280609/apps/mobile/src/components/ui/AppSymbol.tsx#L308) | Only changed mapping is `content_paste → IconClipboard`; sole production request of that material name is ThreadTerminal fixed Paste → ComposerToolbarButton → SymbolView. Android uses object android name then SF fallback; missing lookup returns fallback/null. iOS resolves AppSymbol.ios.tsx and uses SF `doc.on.clipboard`; all other SymbolView callers use unchanged keys | T02 for affected mapping; T01 for iOS icon. No repo-wide symbol capture matrix needed |
| [Sheet.tsx](https://git.cafe/pingdotgg/t3code/blob/49f116db6e3cd1c4b3c2974c017ed1d5e3280609/apps/mobile/src/components/ui/Sheet.tsx#L16) | Exactly four direct production source callers: TerminalContextSheet, ComposerContextSheet, WorktreeSetupSheet (non-Android), ComponentCatalogScreen dev-only. FormSheet initial .5/full 1 on iOS, grabber, title/close, native and JS dismiss paths; now absolute inset-0 View → ScreenStack → NativeScreen → children/header. Children short versus long, intrinsic versus flex ScrollView, separate footer versus in-scroll actions, safe area/keyboard, width/theme all share this bound. Android resolves unchanged Sheet.android.tsx with independent container and .5/.9 detents; most route components named “Sheet” use SheetHeader/native stack instead and are **not** callers of changed Sheet | A02/A03 cover flex body plus separate footer; C01 covers second caller, long in-scroll footer; O04/O05/H07 enumerate other callers/short and failure shapes. No blanket captures of all routed SheetHeader screens |
| TerminalContextSheet (unchanged child) | Sole caller ThreadTerminal after nonblank native capture. `lines = trim trailing newlines, split`; count 1/many, empty internal rows, long wrapped rows, selected start/end and anchor, selected text whitespace/over 64000 disables attach; over-limit helper, insertion capacity failure alert, successful attach draft and navigate. Body flex ScrollView plus fixed footer, native safe-area footer inset | A02/A03, O01, H07; no 300-line assumption |
| ComposerContextSheet (unchanged child) | Direct callers ComposerEditor (draft/skill preview, optional Remove and open callbacks) and ThreadFeed's rendered context links (no Remove). Data record missing; generic payload/unsupported; terminal with line metadata and nested horizontal text; review-comment with optional PR/title, review body and open link; preview-annotation/element optional fields plus source/code surfaces and many elements; image metadata/file attachments; mention path; thread with optional Open thread; environment; skill description/default + View instructions. Optional attachment/open-PR/open-attachment/remove actions change scroll length. File/image/PDF/video with preview source return other modal **before** Sheet; unresolved sources fall through to Sheet attachment content, which may load/error | C01 long removable terminal; O05/H07 alternatives collapsed into short, long text, rich nested scroll/media, missing/unsupported and footer/no-footer shapes. These unchanged internals need no Cartesian product with all new terminal states |
| WorktreeSetupSheet (unchanged child) | Only caller SetupDetailsSheet in WorktreeSetupCard; Details opens it. No Android use (Android file has independent host). Stages pending/running/done/skipped/failed/warning, optional details/progress/elapsed; setup-script running/failed/nonempty-tail output; failed phase error text; running before turn has Cancel and optional Work locally; handed-off/completed/cancelled hides those actions. Children ScrollView with automatic insets and bottom safe-area; height prop unused on iOS | O04 real setup optional, H07 otherwise. Collapse status icon/color permutations into short settled, running/actions, failed/error shapes; time → recording |
| ComponentCatalogScreen (unchanged child) | Example sheet opens from the controls-page Projects row and Open example sheet (+) icon button, or feedback-page Sheet button and New thread floating button (all set the same sheet state). Short ScreenScrollView with Overview/Activity tab state; close and half/full detent | O05, optional actual dev app route |
| NativeTerminalSurface.test.tsx and docs/user/terminal.md | Test adds writable/readOnly/stopped forwarding sequence; no production render path. Docs add iPhone/iPad long-press Select Text/paste/copy/link claim; no shipped screen changed here | H04 for guard evidence; N01/N02/S01/I01 prove documented behavior |

## Scope and handoff

No confirmed runtime defect is asserted by this pass: it derives proof requirements from code. Native suggested-menu availability without focus and replay overlap are explicit unresolved proof checkpoints, not passed findings. Existing PR images on older commits are background, not current-round coverage. CI was read once: at this head 28 successful, 3 pending, 0 failing; that is not a merge verdict and was not polled.

The capturer should report each required row PASS / FAIL / PARTIAL / CANNOT VERIFY with a recording URL, before/after SHA, viewport/theme/build metadata and checkpoint timestamps. Report optional rows as NOT RUN rather than counting them passed. Do not hide a real observed failure because a row was optional. No repo edits, pushes, comments, paid turns or additional delegate work are authorized by this matrix.

FRICTION: none
VERDICT: INSUFFICIENT no captures yet
