# Proof audit, pass 1 — fixture matrix

PR [#24611](https://git.cafe/pingdotgg/t3code/pull/24611), branch `takeover/pr-13413`. Reviewed [43cb239](https://git.cafe/pingdotgg/t3code/commit/43cb23920cf601caaf146c75477abc5dfee95cb2) against merge-base [843f4e3](https://git.cafe/pingdotgg/t3code/commit/843f4e35292391897089535e8f1af59b322e70f8). Both were verified locally; forge metadata still names this head. Read-only audit in `/home/ubuntu/wt/pr-24611`.

## Findings first

1. No implementation defect found in the changed boundary check for its actual callers' positive integer limits. The diff changes a helper and adds its test; no component, style, contract or transport changed. Scope is valid UTF-16 surrogate pairs, not grapheme clusters or repair of already malformed inputs.
2. Seven direct calls exist in three production files. Four indirect title-entry functions exist, with two production uses of mobile's bootstrap builder. There is no shared-helper caller in server or desktop shell code. Web/desktop use the web title paths; native mobile also has affected 200-unit activity labels.
3. Earlier evidence is insufficient for this matrix: missing 390-wide/dark real-app pairs and native mobile. Earlier helper-output substitution cannot stand in for actual base/head application execution. This pass creates no screenshots and grants no visual-proof verdict.
4. A normal 199-letter mobile prefix hides the changed glyph behind native one-line tail ellipsis. Exact zero-width padding below makes the boundary visible while preserving the unmodified label renderer. The fixture JSON exposes every code unit; capturers must confirm the final glyph/dots are visible on the pooled device. An invisible or CSS-clipped difference is not proof.
5. Two important exclusions: mobile pending-task/draft titles use a separate 72-unit truncator; live mobile group summaries can bypass this helper. Capturing either does not prove the intended 50/200-unit paths.

## Exact fixture strings

`Z(n)` means the exact JavaScript expression `"\u200b".repeat(n)`, U+200B ZERO WIDTH SPACE. `+` below is string concatenation, not literal text. These are complete, deterministic strings, not approximate examples. Padding is intentional fixture data and survives the callers' trim and whitespace normalization. Full ASCII-escaped values, lengths and actual caller outputs are in [fixtures.json](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/37f06e10375228c1/fixtures.json). The emoji is U+1F600 (`\ud83d\ude00`).

| ID | Exact input string | Input route / limit | Exact expected head result |
|---|---|---|---|
| T50 | `"Text " + Z(44) + "😀 tail"` | Prompt, 50 units | `"Text " + Z(44) + "..."` |
| E50 | `"Keep " + Z(43) + "😀 tail"` | Prompt, 50 units; pair ends exactly at cut | `"Keep " + Z(43) + "😀..."`, unchanged |
| F50 | Filename `"file-" + Z(38) + "😀.txt"`; text `""`; one non-image file | Caller prepends `"File: "`; 50 units | `"File: file-" + Z(38) + "..."` |
| L50 | `"Term " + Z(44) + "😀 line 1"`; text `""`; attachments `[]`; fallback labels `[null," ",L50,"ignored"]` | First nonempty normalized fallback, 50 units | `"Term " + Z(44) + "..."` |
| P50 | Markdown `"# Plan " + Z(34) + "😀 tail\n\nReply OK."` | Heading produces `"Implement Plan " + Z(34) + "😀 tail"`; 50 units | `"Implement Plan " + Z(34) + "..."` |
| M200 | `"Note " + Z(194) + "😀 tail"` | Notice detail/message or reasoning text; 200 units | `"Note " + Z(194) + "..."` |
| C200 | `"Cmd " + Z(195) + "😀 tail"` | Synthetic command `input`; 200 units | `"Cmd " + Z(195) + "..."` |
| E200 | `"Keep " + Z(193) + "😀 tail"` | Notice detail/message; 200 units; pair fits | `"Keep " + Z(193) + "😀..."`, unchanged |
| A50 / B50 | `"a".repeat(51)` / `"日".repeat(51)` | ASCII/BMP length controls, 50 units | 50 copies plus `"..."`, unchanged |
| SHORT / EMPTY | `"  short 😀  "` / `"   "` | Short/empty prompt | `"short 😀"` / `"New thread"`, unchanged |
| IMAGE1 / IMAGE2 | text `""`; one / two image attachments named `"image.png"` | Image count branches | `"Attached image"` / `"Attached images"`, unchanged |
| NOHEADING | Markdown `"Reply OK."` | Plan with no heading | `"Implement plan"`, unchanged |

For every affected row above, base returns exactly the listed head output with `"\ud83d"` inserted immediately before `"..."`. Capture should show its replacement glyph on base and its removal on head. SQLite or native string transport may replace the lone surrogate with U+FFFD; disclose the actual persisted/native value rather than assuming it retained U+D83D.

## Caller and visual fixture matrix

Status is source/probe status in pass 1, not captured evidence. **Required** rows must appear in the combined scenes below. **Shared** rows are proved by the named mechanism plus caller provenance, without multiplying viewport/theme captures. If a shared route cannot be exercised, label its provenance source-only; do not call it an observed send.

| Row | Callers and data/conditional shape | Affected? | Real app seeding / destination | Minimum capture and status |
|---|---|---|---|---|
| T — ordinary persisted title | `deriveThreadTitleSeed` text branch; web `useComposerSend`; native `buildProjectThreadStartTurnInput` via new-task outbox; T50 and E50 in otherwise idle, unarchived threads | T50 yes; E50 no | New project task, exact prompt, no attachments/context, working selected provider. Web route `/$environmentId/$threadId`; native Thread route and thread list. Wait for acknowledged bootstrap and persisted title, not mobile Pending tasks. | **Required W and M**, still. Actual head helper/title caller probed; captures pending. |
| F — file-only title | Same seed function's attachment-name branch; web ordinary and multiple-model send; native new-task/outbox. F50; first file wins; any image takes image branch first; prose takes precedence over all attachments | Yes | Attach a small synthetic file with exact F50 filename, empty prose, provider supporting files. Inspect `bootstrap.createThread.title`/`titleSeed`. If product send guards prevent file-only launch, this is a **labelled caller harness**, not a claimed real send. | **Shared T's 50-unit mechanism**; exact actual caller output probed; no additional screenshots. |
| L — context-only fallback | Web ordinary composer only; nonempty normalized text/file/image branches absent; fallback order terminal → review → browser annotation; first nonempty wins | Yes | Terminal selection with `terminalLabel = "Term "+Z(44)+"😀"`, `lineStart=lineEnd=1`, selected text `"ok"`, empty prose, no attachments; add context and send. Existing terminal rename/context UI or driver using actual composer draft state. L50 is the computed label. Review equivalent: filePath `"r".repeat(41)+"😀.ts"`, rangeLabel `"+1"` yields `"Review: "+basename+" L1"`; annotation equivalent: empty comment and pageTitle=T50. If exact context-only send cannot be acquired, label caller harness. | **Shared T mechanism**; L50 actual seed branch probed. Other producer shapes source-traced; no extra viewport/theme captures. |
| P — plan implementation title | Web `useImplementPlanInNewThread` calls helper after `buildPlanImplementationThreadTitle`; heading / no heading. Existing-thread plan follow-up does not compute this title | P50 yes; NOHEADING no | Source thread contains actual proposed plan with exact P50 Markdown. Use **Implement in a new thread**, not ordinary follow-up. Select available provider and enabled interaction mode; inspect created title and source-plan reference. If only synthetic plan projection can be produced, label the plan-source setup as harness; use real implementation action. | **Required W**, still of settled destination, co-visible with T. Prefix transformation probed; captures pending. |
| M-C — compact activity label | Native `workEntryRowLabel(...,false)` → `compactWorkEntryText` → truncate(200). Sources: command after shell-wrapper/control display conversion; generic detail; changedFiles first path or first path + count; collapsed reasoning. M200 notice is minimal stable representative | Yes at split | **Synthetic projection harness in real native app**, per procedure below: completed `system_notice.message=M200`, no command/tool presentation, no groupStandIn. Visible ThreadWorkLogRow header. This avoids requesting a provider to emit a chosen system notice. | **Required M**, still; real notice→workEntry→label output probed; native capture pending. |
| M-X — expanded generic label | Native `workEntryRowLabel(...,true)` reaches direct truncate(200) when preview is nonblank. Reasoning expansion instead returns Thinking/Thought; a command returns earlier; blank preview returns entry.label | Yes | Second completed `system_notice.message=M200`, distinct ID; open its disclosure before capture. Keep first notice collapsed. Same synthetic projection harness; UI must render actual source module, not supplied precomputed label. | **Required M**, settled still after disclosure. Actual expanded notice path probed; capture pending. Expansion timing is not claimed. |
| M-S — single command / thought summary | Native `singleToolCallLabel`: settled, single, tool-like, not edit; reasoning collapsed uses compact helper, reasoning expanded is Thought; resolved T3 MCP presentation returns early; command C200 otherwise directly truncate(200) | Yes | Synthetic completed command with C200 input, output=`"ok"`, exitCode=0, no recognized MCP name. Alone in its group: produces groupStandIn label in same actual WorkLogLabel. Separate it from other commands with prominent notices. Alternative single reasoning text=M200 produces ThreadWorkGroupToggle. | **Required M command representative**; single-thought summary shares compact mechanism and WorkLogLabel. Both actual feed-building routes probed; captures pending. |
| C — unchanged truncation controls | Pair fits (E50/E200); below/equal limit; long ASCII/BMP; trim; empty; image count 0/1/2; first blank/nonblank fallback; no plan heading | No | E50 and SHORT as actual threads beside T/P. E200 notice beside native notices. Remaining exact inputs via caller probe/harness; no reason to create unavailable empty-content sends. | **W: E50+SHORT; M: E200** co-visible when feasible. Remaining controls source/probe, no extra pairs. |
| V — grouped/detail/status variants | Native expanded multi-tool group calls workEntryRowLabel for child rows; single groupStandIn bypasses it. Search with resolved search label, read-tool first path/count, recognized T3 MCP presentation, expanded reasoning, absent/blank preview and entry-label fallback bypass affected cuts. Failed provider-error/usage-limit row has separate early renderer | Only branches that reach truncation | Existing static seed fields can exercise nonempty detail/changedFiles, or raw command without shell wrapper. One/many changedFiles only changes input string before same helper. Success/failed/stopped tones, copied badge, icons, answer preview do not change boundary selection. | **Shared M** for truncate-reaching branches; bypass branches source-excluded. No independent status/count capture multiplication. |
| LIVE — live/loading/interaction transitions | Native live group uses `liveToolActivitySummary`/liveThoughtLine, shimmer; streaming detail changes over time. Title regeneration may replace a seed. Detail disclosure/loading/error, copy feedback, hover tooltip, rename, drawer/menu opening are separate interactions | Only a live row that still reaches same cut; most summaries bypass | Not needed for static surrogate-boundary claim. Choose historical settled rows, title generation completed, all detail fetched, no copied badge/hover/focus. | **recording if such transition is claimed**; excluded from minimum. Stills cannot substantiate streaming, reveal timing, persistence transitions or animation claims. Static expanded/collapsed end states use stills. |
| OUT — separate truncators/surfaces | Server CLI title/Git toast/watch snippet/restart note; native pending/new-task draft title limit 72; web work-log labels use other helpers. No desktop-shell shared-helper caller | No | These paths do not call changed helper. Do not substitute a pending native title or web activity label for M. | Source-excluded; no capture obligation for this PR. |

### Complete direct and indirect source inventory

All seven production calls:

- [threadTitle.ts](https://git.cafe/pingdotgg/t3code/blob/43cb23920cf601caaf146c75477abc5dfee95cb2/packages/client-runtime/src/operations/threadTitle.ts): lines 34 (prompt), 44 (file), 50 (fallback), all default 50.
- [useProposedPlanSend.ts](https://git.cafe/pingdotgg/t3code/blob/43cb23920cf601caaf146c75477abc5dfee95cb2/apps/web/src/components/chat/useProposedPlanSend.ts):304, default 50.
- [threadActivity.ts](https://git.cafe/pingdotgg/t3code/blob/43cb23920cf601caaf146c75477abc5dfee95cb2/apps/mobile/src/lib/threadActivity.ts):278 compact, 317 expanded preview, 938 single command, all 200. Single reasoning at 934 goes through compact at 278.

Every indirect production seed caller: web `useComposerSend.ts:1157`, `useMultipleModelSend.ts:132`; mobile `projectThreadStartTurn.ts:41` and `use-thread-outbox-drain.ts:865`. Mobile builder is used by new-creation outbox at 1021 and work-locally recovery at `ThreadRouteScreen.tsx:831`. These wrappers do not change the cut, so fanout model counts, provider adapters, local/worktree creation, existing-thread queued delivery, work-locally recovery, local/remote/relay/tunnel and destination environment do not require additional pixel matrices. Existing-thread titleSeed updates title only for first user/maintenance-only history; an ordinary existing-thread send does not rename arbitrary history. Source/RPC provenance must distinguish these paths from visually similar title assignment.

Prompt normalization branches replace context tokens with their labels, remove image tokens, flatten whitespace/citations, then prefer prose. Image-only token counts produce Attached image(s). Attachment branch prefers any image over first file. No provider-specific boundary implementation exists. All providers receive the already computed seed. Server title generation is a separate transformation and may hide the seed; a successful generated title is not proof of this helper.

Downstream title render families all read the stored string without calling a new shared truncation: web current/card/legacy sidebar rows and tooltips, chat breadcrumb and rename/menu label, command-palette thread hits, archived lists, thread relationships/Markdown thread links/PR thread links, background announcements, in-app/desktop notifications, composer accessible label. Native families: thread list (list/card/sidebar/search), AppBar/native header, archived and arrangement lists, command-palette and thread detail/menu/title actions, composer accessible label and shortcuts; widget activity uses the resulting title data. Their badges, unread/status colours, pin/archive/settle state, selection and counts are existing layout and CSS/native ellipsis variants, not new helper branches. W proves actual persisted web string rendering, M proves native header and actual WorkLogLabel rendering. No independent Electron, iOS, widget or OS-notification screenshot is claimed; their shared title-data mechanism is source-covered only.

## Minimum capture set — six base/head still pairs

One pair means two original full-surface files, one running actual base and one running actual head, with identical fixture inputs/settings/fonts/device. A combined scene can cover multiple logical rows. Do not multiply equivalent calls into separate 1440 × 390 × theme captures.

| Scene | Surface × viewport × theme | Co-visible rows | Status |
|---|---|---|---|
| W-1440-L | Real web, 1440×1000 CSS px, DPR fixed, light | T50, E50, SHORT and P50 titles in same sidebar; selected P50 in breadcrumb | Required; not captured |
| W-1440-D | Same, dark | Same | Required; not captured |
| W-390-L | Real web, 390×844 CSS px (increase only height if needed), same DPR, light | Open ordinary sidebar drawer before capture; same four title rows visible; no resize/crop of a 1440 image | Required; not captured |
| W-390-D | Same, dark | Same | Required; not captured |
| M-Android-L | Real native client on **pooled Android emulator**, portrait full device surface, normal text scale, light; record pool lease/device name, OS, logical viewport and screenshot pixels | Header with native-created persisted T50; compact notice M200; expanded notice M200; isolated C200 command label; E200 notice if it fits | Required; not captured; activity portion explicitly **synthetic projection harness** |
| M-Android-D | Same pooled Android device, dark | Same | Required; not captured; same harness label |

Six pairs = twelve original stills. Android is the chosen native platform; iOS uses the same truncation logic and React Native WorkLogLabel, but its platform renderer is unobserved. Do not claim iOS verified. If all native labels cannot fit one full surface, add the minimum second native scene per theme rather than shrinking fonts, changing production layout or hiding this limitation. T50's 50-unit mechanism also covers F/L and fanout/recovery wrappers; P50 is still co-visible because its title prefix must place the cut correctly. The 200-unit native compact and expanded branches share the boundary mechanism but retain separate co-visible notices because they select text differently. M-S's command reaches the third direct native call; do not replace it with a live Running command summary.

No required row is time-dependent. Sending, opening a drawer and selecting an expanded state acquire a static fixture; those operations are not timing/interaction claims. If the capturer elects to claim the interaction itself, label that evidence **recording** and supply video. Static title/glyph evidence needs stills only.

## Seeding and provenance instructions for the capturer

**Web W:** run base and head applications with separate isolated state and identical source inputs. Create T50, E50 and SHORT via the real composer; create P50 through a real proposed-plan implementation action. Preserve the computed seeds by selecting a deliberately unavailable custom **Text generation model** in isolated Settings → Text generation, while the composer/implementation model remains available. Wait until titleRegeneration clears after failure, then confirm persisted titles. This intentionally exercises fallback retention; it does not prove successful automatic title generation. Alternative: a labelled deterministic text-generation fixture that leaves the seed unchanged. Record which route was used. Do not hand-rename titles, insert precomputed titles, patch helpers or swap output strings as the primary W evidence. At narrow width, put the four threads near top and open the real sidebar drawer; no interaction proof claim is needed for the prepared static state.

**Native title:** pair native client with the isolated environment, use New task with T50, wait for outbox delivery and title-regeneration completion. The pending row is out of scope. Use that persisted thread as the native activity fixture destination. Run base JS against base server and head JS against head server; a shared native dev-client binary is fine when native fingerprint is identical, but Metro source revision must be recorded.

**Native activity deterministic fixture:** no general client RPC lets a client author arbitrary provider turn-item/system-notice events. This matrix therefore explicitly permits a **labelled synthetic projection harness rendered by the real native app**, not a claimed real provider run. Start isolated environment once for migrations and a normal thread; stop its owned server. Inspect current schema and clone app-written rows. With the documented `apps/server/scripts/t3-sqlite-state.ts` helper, insert only into isolated `orchestration_v2_projection_turn_items` (and coherent synthetic run/message projection rows as needed), using unique IDs, fixed old ISO timestamps and identical raw inputs in base/head. Never seed pre-truncated labels. Leave event history unchanged and claim rendering only. Reference [SQLite fixture procedure](https://git.cafe/pingdotgg/t3code/blob/43cb23920cf601caaf146c75477abc5dfee95cb2/.agents/skills/test-t3-app/references/sqlite-fixtures.md); schema/seed pattern also exists in [seed.py](https://git.cafe/pingdotgg/t3code/blob/43cb23920cf601caaf146c75477abc5dfee95cb2/scripts/fixtures/issue-16534/seed.py).

Recommended turn-item order: a single completed command C200; prominent system notice M200 (collapsed); second prominent system notice M200 (expanded); prominent system notice E200. A system notice breaks command grouping and stays visible. All use a completed historical run, status=completed, streaming=false where the schema requires it, no recognized T3 MCP tool fields, no activeTail/shimmer. For C200 the synthetic command reports output="ok", exitCode=0; this is deliberately fabricated presentation data, not an executed command. After restart, open the existing run fold if needed and the second notice disclosure through UI, wait for detail loading to settle, then capture. If the seed does not decode or an item becomes invisible, fix the seed or report the row missing; do not silently switch to a string gallery. The raw event shapes can follow `base`/`projected` in the probe and existing `threadActivity.test.ts` fixtures. Capture base and head from the actual native renderer; a browser at mobile width is not M.

If exact F/L producers or proposed-plan source cannot be driven through existing UI, a caller/component harness is allowed only with that row explicitly labelled. It proves that caller/render path, not sending/persistence. It must use actual base/head modules, with raw fixture fields, and never supplied helper outputs.

**Both surfaces:** light/dark is a prepared setting, not system auto-theme timing. Use identical normal text scale, locale, clock, fonts, order and scroll positions. Keep loading/errors/copy feedback/active runs absent from the selected static scene. Preserve full viewport/device originals; crops may supplement them. Record revision, raw input UTF-16 counts, persisted/native label values and source paths. Diffs should disclose age/status/focus/chrome differences. No video is needed unless extending the claim to LIVE/interaction timing. Release only leases/processes owned by the capture run.

## Executed checks and audit limits

Repository-wide tracked-import search independently confirms exactly three production import sites, no aliases/reexports or server shared String imports. Complete PR diff has only String.ts and String.test.ts. Actual head modules were imported under Bun without app initialization; head caller outputs and the actual `git show` base helper were compared for 16 checks, including native feed grouping and system-notice conversion. All affected checks change only the broken boundary and all probed results are well-formed. The exact-pair, ASCII/BMP and short controls are unchanged. EMPTY fixture uses New thread at the seed level; its helper-only result is empty. This probe is not a client render test, full test-suite run, native/Hermes execution or live persistence proof.

Public probe [source](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/a5d811303ef764e1/probe.mjs) and [transcript](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/12c66cdb3594bc65/probe.log). Earlier inventory was read only after independent import/call discovery and checked against code; it is not the authority for this matrix. No browser/device/server started, no source edited, no push or comment. No real userdata accessed. This pass completes the fixture-planning gate only; every required capture is still pending.

FRICTION:
- tool | First ghdrop matrix upload failed with a POST transport error; artifact uploads had succeeded. Retried the matrix upload. | Automatic retry for transient upload transport failures.

VERDICT: SUFFICIENT
