# Proof audit, pass 1 — [#29141](https://git.cafe/pingdotgg/t3code/pull/29141)

PR: [#29141](https://git.cafe/pingdotgg/t3code/pull/29141) ([GitHub mirror](https://github.com/pingdotgg/t3code/pull/29141)). Reviewed head [a5c3df8b21](https://git.cafe/pingdotgg/t3code/commit/a5c3df8b2124ce221e4162332eaffa6090779812) against [30654b3e91](https://git.cafe/pingdotgg/t3code/commit/30654b3e91a51937d1dba18fc83af19d8003f5ca). Read-only source/evidence planning; no browser, server, device, tests, edits, pushes, or PR comments were run. No capture has been accepted in this pass. The PR body's descriptions of prior captures are claims, not inspected media.

## Findings first

1. **All affected fixture captures are pending.** A successful tool reply or stored toolName alone cannot prove that the reader sees the page. The core proof needs the same real-Hermes prompt at base and head, with the actual inline panel visible at head and absent at base. Activity disclosure is an interaction and needs a recording.
2. **The affected consumer set is larger than the inline page.** Canonical identity removes foreign-integration presentation, enables T3 labels/icons/group summaries, enables blocked-on-thread-wait status, and can suppress a redundant delegate row when a matching app-owned child card exists. Web/desktop, mobile, and guest shares consume the resulting stored item. The matrix includes these paths; it does not require a screenshot for every tool in the unchanged inventory.
3. **There are two production parser callers with different output handling.** Root `emitTool` performs HTML recovery; native ACP `projectSubagentTool` does not call `takeHtmlRender` or rewrite text-only output. For a native ACP child with Hermes-style text-only HTML output, this diff canonicalizes its identity but does not establish the same fallback panel behavior as the root path. This is a proof limitation, not a newly established code regression. Label this caller's synthetic fixture explicitly; do not claim a root capture covers it.
4. **This is prospective normalization, not a migration.** Previously persisted `editor_t3_code_<hash>.html_render` items retain their old presentation. A pre-existing missing panel is not evidence that the new-call fix failed.

## Source and render-path inventory

No React component, prop type, CSS, RPC schema, or mobile component changes in the diff. The only production change is the new guarded return in [runtimeModel.ts](https://git.cafe/pingdotgg/t3code/blob/a5c3df8b2124ce221e4162332eaffa6090779812/packages/provider-acp/src/server/runtimeModel.ts#L1139). The test addition covers one positive title and two negative server names. It does not itself exercise adapter recovery or any client.

| Entry / caller | Data and branch that can affect the reader | Downstream render path / fixtures |
|---|---|---|
| `extractMcpToolCallIdentity` | Candidates, in order: `_meta.toolName`, `_meta.claudeCode.toolName`, `_meta.goose.toolCall.toolName`, raw ACP `title`, each trimmed. New branch needs qualified `mcp__server__tool`, exact lowercase `editor_t3_code_<8 lowercase hex>` OR `editor_t3code_<8 lowercase hex>`, and membership in `T3_MCP_TOOL_NAMES`. Earlier explicit MCP/server provenance can return first. | Positive class P01–P10; rejected/precedence class C01. These candidate locations and equivalent hash variants have the same final visual, so they do not multiply screenshots. |
| Root ACP `emitTool` ([adapter.ts](https://git.cafe/pingdotgg/t3code/blob/a5c3df8b2124ce221e4162332eaffa6090779812/packages/provider-acp/src/server/adapter.ts#L3807)) | `toolName` becomes `t3-code.<tool>`, `title` is null and foreign `toolSource` is omitted. Input resolves identity input → rawInput.arguments → rawInput → `{}`. Output is rawOutput or ACP content. Only completed, non-error `html_render` can recover a page. Explicit reported reference, previous reference on repeated completion, unique retained-input match, and no match are separate data routes. Failed/completed-error calls consume retained pages without attaching them. Compaction is handled before the MCP branch. | P01, P02, P03. Exact recovered output is `{htmlRender: reference}` when Hermes supplies only a textual result. Other T3 tools use the normal dynamic-tool path. |
| Native ACP `projectSubagentTool` ([adapter.ts](https://git.cafe/pingdotgg/t3code/blob/a5c3df8b2124ce221e4162332eaffa6090779812/packages/provider-acp/src/server/adapter.ts#L2786)) | Same canonical identity and foreign-source removal, but retains merged title, rawInput and rawOutput/content; no root HTML fallback. | Native child thread work rows: P06. Native provider child and app-owned `delegate_task` child are different fixtures. |
| Page matching ([McpProviderSessions.ts](https://git.cafe/pingdotgg/t3code/blob/a5c3df8b2124ce221e4162332eaffa6090779812/packages/provider-core/src/server/McpProviderSessions.ts#L46)) | Match is scoped to thread and requires unique HTML hash (or supported truncated tail), title, and numeric height; a reported attachment consumes that exact page. Empty/missing HTML, changed title/height, no retained page, and duplicate ambiguous inputs return no page. | P03 covers recovered/previous/no-match classes. Hash spelling and number-of-retained-pages are protocol cases, not separate layouts. |
| Web + desktop root thread | `session-logic.ts` converts only completed dynamic tools with a valid non-error HTML reference into `html-render` entries. Otherwise work rows remain. `MessagesTimeline.logic.ts` groups tools; pending/running/waiting, completed, failed, interrupted and result-level failure select live/completed/failed/stopped wording and icons. One/multiple summaries, mixed sources, long labels, group disclosure, individual inspector disclosure, and task-id child suppression change layout. A successful page replaces the HTML tool work entry; do not require a separate visible completed render row. | `HtmlRenderTimelineRow` → `HtmlRenderFrame` → `HtmlRenderDocument`; activity → `TimelineWorkGroupRows` / `TimelineWorkEntryRow` / inspector. P01–P05, P07. Desktop wraps the same web components; no new shell/IPC branch in this diff. |
| Web HTML frame and dialog | Reference attachmentId/title/height/optional measured heights, environment asset availability and threadRef; asset pending = reserved blank area, success = iframe, failure = `Unable to load <title>`. Live content height overrides measured/reference height. Hover/focus/coarse pointer reveals fullscreen control. Fullscreen URL reuse vs refresh success/failure; dialog close/Escape, zoom 50–200%, reset; content links, images, keyboard shortcuts and native fullscreen; theme at first paint and subsequent changes. | P01/P07 prove the newly attached panel. Q01–Q03 list unchanged viewer branches without treating this nine-line parser change as a redesign of the viewer. `HtmlRenderDocument` is also reachable via `BrowserDocumentFrame` → attachment/file preview and the fullscreen dialog. |
| Native mobile feed | `threadActivity.ts` has the same completed-reference gate; `ThreadFeed` → `ThreadHtmlRender` → `HtmlRenderWebView(nested=true)`. Reference + frameWidth sets row height. Asset/WebView loading shows a spinner; failure retries once then offers `Page unavailable`/reload. Overflow x/y changes scrolling, especially patched Android edge behavior. Open navigates to `ThreadAttachment` (`htmlRender=1`) → `AttachmentDocumentBody` → `HtmlRenderWebView(nested=false)` with fullscreen gutters. WebView process death restarts once, then reports failure. | P08 iOS and P09 Android; Q01/Q03 for inherited failure/interaction branches. Theme comes from appearance preferences and platform colors. |
| Guest share | Same completed-reference timeline derivation, overridden renderer `GuestHtmlRender`, with guest-scoped render HTTP route. Responsive height/theme propagation and focused user link opening, but no owner's fullscreen button. | P10. `GuestShareService.render` also checks the canonical tool reference and actual attachment ownership. |
| Wait state | `deriveBlockedOnThreadWait`: active run must be running; at least one running/waiting `t3_thread_wait` OR `delegate_task` with `input.mode === "wait"`; ignores app-owned child tracking rows; any other active execution tool prevents wait-only status. Web `resolveSidebarThreadStatus` and mobile `resolveThreadListV2Status` display waiting before ordinary working, after approval/input/guest-request priority. | P04; nonwaiting/concurrent/terminal controls in the same recording. No new Settings or command-palette entry point. |
| Delegation deduplication | Canonical `delegate_task`, running/completed, non-error and output `taskId` matching an app-owned child in the same run hides redundant work activity. Failure, missing/mismatched taskId or absent child retains it. | P05, both web and mobile logic. |

Shared presentation sources: [tool identity inventory](https://git.cafe/pingdotgg/t3code/blob/a5c3df8b2124ce221e4162332eaffa6090779812/packages/shared/src/t3McpToolPresentation.ts#L377), [work-log presentation](https://git.cafe/pingdotgg/t3code/blob/a5c3df8b2124ce221e4162332eaffa6090779812/packages/client-runtime/src/work-log/presentation.ts#L226), [wait derivation](https://git.cafe/pingdotgg/t3code/blob/a5c3df8b2124ce221e4162332eaffa6090779812/packages/shared/src/orchestrationV2PendingBackgroundWork.ts#L56), [web feed gate](https://git.cafe/pingdotgg/t3code/blob/a5c3df8b2124ce221e4162332eaffa6090779812/apps/web/src/session-logic.ts#L698), [mobile feed gate](https://git.cafe/pingdotgg/t3code/blob/a5c3df8b2124ce221e4162332eaffa6090779812/apps/mobile/src/lib/threadActivity.ts#L2087).

## Seeding contract

**Real-app seed R.** Use isolated development state, never the live userdata database. Settings → Providers → Add provider → Local ACP command; executable `hermes-acp` from hermes-agent, configured using existing credentials. Label the instance distinctly. Create a thread with that instance, then visit `/<environmentId>/<threadId>` (web route `/_chat/$environmentId/$threadId`). Send: “Call html_preview, then html_render, using title ACP render test, height 200, and exactly <!doctype html><html><body><p>ACP render test</p></body></html>. Return a brief final reply.” Preview success is not required for recovery proof, but capture and label any preview failure. The external Hermes renaming implementation was not independently inspected in this pass; the actual ACP trace is required to verify that premise. Verify raw ACP title has the Hermes prefix, completed result has text content and **no rawOutput**, then verify the persisted item and actual attachment. Capture base and head with the same prompt and client configuration; use fresh calls, not historical rows.

**App/RPC driver D.** A deterministic script may register a local ACP shim in the same provider settings and drive the actual app via `orchestration.dispatchCommand` (`thread.create`, then `message.dispatch` with `dispatchMode.type=start_immediately`). The shim must implement normal initialize/session/new/session/prompt and emit real ACP `session/update` messages. On session/new it receives T3 MCP access; it must call the actual thread-scoped T3 `html_render` through that supplied transport to create and record the attachment, then emit Hermes-shaped text-only result updates. Do not invent an attachmentId, inject DOM, or insert a tool projection directly and call that real-Hermes proof. Log ids/raw notifications and output-only run summaries, with credentials redacted. A shim screenshot is **labelled harness capture**, even though the unmodified app renders it. No ready-made driver was created in this read-only pass. The repository's `packages/effect-acp/test/fixtures/acp-mock-peer.ts` is a protocol example, not an existing implementation of these cases.

**Projection harness H.** Where a provider cannot generate a precise state reliably, use a labelled web/native component harness supplied with valid projected turn items, rather than claiming real-provider coverage. It can prove downstream appearance, not the ACP recovery path. The native ACP child caller requires a protocol driver that emits the adapter's native subagent/session associations, or this explicitly limited harness. A real app-owned delegate alone does not hit `projectSubagentTool`.

**Connection/surface seed.** Open the same isolated server/thread from desktop or a remote web client; no extra origin settings. Pair native clients with that isolated server. Guest fixture: use the thread's Share with guest action (header/sidebar/command-palette entry points reach the same dialog), open the generated guest URL and capture only the isolated test content. Do not upload reusable auth or pairing URLs.

## Fixture matrix and minimum capture set

W = web 1280×800, light. N = web 390×844, dark. I = iOS native 393×852 logical viewport, dark. A = Android native 412×915 logical viewport, light. G = guest web 1280×800, light. Capture actual viewport when device dimensions differ. Desktop uses W's shared renderer; a distinct desktop capture is not a minimum requirement for this server-only diff. “Pair” means base and head media, clearly labelled and tied to their commits. Recording rows must show the transition/interaction; stills do not satisfy them. Multiple rows may share one continuous, labelled recording.

| ID / named fixture | Affected? / minimum viewport × theme | Required data and expected visible outcome | Media | How to seed / provenance | Status |
|---|---|---|---|---|---|
| P01 — real Hermes, one completed render | Yes; **pair W/light** | R's exact HTML/title/height. Base: success reply but no page, generic integration presentation. Head: actual ACP render test page inline above final reply; canonical stored `t3-code.html_render`, valid attachment. Keep activity collapsed for this stable comparison. | still pair | **R, actual Hermes**; mandatory real-provider anchor, cannot substitute H or D. | CAPTURES PENDING |
| P02 — live tools, labels and disclosure | Yes; **pair W/light** | Hermes-shaped capabilities success and html_preview failure, plus html_render pending/running → completion. Show one-tool and multi-tool group (two calls of one action, then three distinct T3 summary actions and one foreign integration to cover the two-selected-actions plus remainder/source summary), collapsed → expanded → individual inspector → collapsed. The added summary-action variants may use safe read tools in D/H. Include a failed `html_render`, completed `isError:true`, and interrupted active call as terminal no-panel cases (fresh ids). T3 names/icons and failed/stopped wording replace generic labels; failures never display a recovered page. Root completed HTML moves out of the work group into P01's page row. Use a long title/input once to check truncation/details. | **recording** | R for naturally produced capabilities/preview/render, **D labelled harness** for controlled terminal/error/interruption branches. Record which phases are synthetic. | CAPTURES PENDING |
| P03 — page-result recovery classes | Yes; **W/light**, head; base can share P01 | Same `html_render` input with text-only result and retained page → panel; repeated completed/titleless update → same one panel, not a duplicate/disappearance. Structured reported reference → same panel class. Completed call without matching retained/reference output → tool row, no fabricated/neighbor's page. Two distinct published inputs identify distinct attachments; ambiguous identical retained inputs must not attach arbitrarily. | **recording** | **D labelled harness** through actual MCP publish; no-match/ambiguity are deliberately synthetic. Page matching uses html/title/height and thread scope. Covers input-unwrapped `rawInput.arguments` as an equivalent protocol variant if that is used by the driver. | CAPTURES PENDING |
| P04 — wait-only run and exit | Yes; **W/light + I/dark**, head | Active run running, one running/waiting prefixed `t3_thread_wait`; separately prefixed `delegate_task` input `{mode:"wait"}`. Show working → waiting → completion/working release. Add another active ordinary tool: working, not wait-only. Async delegate/missing mode is also not wait-only. App-owned child tracking row alone must not defeat wait-only status; pending approval/input takes precedence. Sidebar/thread-list status and live tool label visible together. | **recording** | R if real wait can be held and released; otherwise **D/H labelled harness** with controlled active-run/item and priority states. One sequence per client can cover both wait names and the nonwaiting controls. | CAPTURES PENDING |
| P05 — matching app-owned delegate card | Yes; **W/light + I/dark**, head | Running/completed canonical delegate call, output `{taskId:"child-1"}`, same-run app-owned subagent id `child-1`: child card remains, redundant dynamic-tool activity disappears. Missing/mismatched id or failure: activity remains. Show expansion/opening child and return. | **recording** | Real app delegate via R if supported; else **D/H labelled harness** with same-run child/item associations. Does not test native ACP child parser caller. | CAPTURES PENDING |
| P06 — native ACP child caller | Yes; **W/light**, head | Native ACP subagent/session emits renamed capabilities/html_preview work calls and a text-only html_render completion. Canonical labels/foreign-source removal appear inside the actual child conversation. With text-only output and no parsed reference, do **not** assert root-style recovered panel: this caller has no takeHtmlRender fallback. A genuinely structured valid reference may take the ordinary client HTML path. | **recording** | **D native-subagent protocol driver**, or **H labelled harness** showing its emitted shape with explicit “downstream only” limitation. Open child thread and return; no reliable actual-Hermes native-subagent seeding established in this pass. | CAPTURES PENDING |
| P07 — narrow, dark, responsive reference | Yes, inherited rendering of newly recovered data; **N/dark**, head | Valid recovered page with long title (up to 200 chars), wrapping body, supplied height plus measured heights covering narrow/wide. Inline page fits the narrow column; adjacent reply/work labels remain readable. Switch from light/wide to dark/narrow with no tool rerun and page retained. Empty/whitespace title normalizes to HTML; that is a data boundary, not an extra style. | **recording** | R with a second responsive test page; resize/theme through app settings. D/H may provide measured-height boundaries. Use app theme variables, not a hard-coded light page that cannot reveal the theme. | CAPTURES PENDING |
| P08 — iOS native recovered page | Yes; **I/dark**, head | Connect to P01's post-fix thread: inline page, native work labels, correct order above reply. Tap page open control → ThreadAttachment HTML viewer → back; page remains and feed position is usable. | **recording** | Same actual-R stored thread on isolated server; mobile navigation, no synthetic substitute for cross-client integration. | CAPTURES PENDING |
| P09 — Android native recovered page | Yes; **A/light**, head | Same P01 reference, inline WebView and canonical labels. A stable fit-without-overflow page is sufficient for this parser change; platform-specific nested scroll stress is Q03. | still | Same actual-R stored thread on isolated server; native Android client. | CAPTURES PENDING |
| P10 — guest-shared recovered page | Yes; **G/light**, head | Share P01's isolated thread. Guest sees the same inline page above reply using guest renderer/asset route; no owner fullscreen control is expected. | still | Real app Share with guest flow, generated guest URL; capture only the resulting settled view. If the flow itself is the claim, record creation/opening instead. | CAPTURES PENDING |
| C01 — foreign, unknown and metadata precedence | No changed expected visual; no separate minimum capture | `editor_github_1234abcd` + html_render; `not_t3_code` + html_render; matching renamed T3 server + unknown tool; malformed/uppercase/7-or-9-char hash; earlier explicit foreign provenance. Retain foreign identity/source and no T3-only fallback. Matching no-underscore `editor_t3code_<hash>` is a positive equivalent to P01. Metadata candidates have the same final visual as title. | protocol assertions; if shown interactively, **recording** | Runtime-model assertions or D. Existing added test covers first two negative names, not the whole boundary list. Do not require captures for lexically different inputs with the same foreign layout. | CONTROL — NOT CAPTURED |
| C02 — historical items | No; no separate minimum capture | Old stored `editor_t3_code_<hash>.html_render` remains generic and panel-less. Fresh canonical calls show P01 behavior. No migration is claimed. | still if desired | Keep a base-created test thread in isolated state, reconnect head; can appear beside P01. | CONTROL — NOT CAPTURED |
| C03 — ordinary canonical/other providers | No; no separate minimum capture | Existing `t3-code.html_render` or recognized normal title still shows page; other provider adapters and scheduled-agent transport are unmodified. Same valid reference through local/remote/relay has same UI, but attachment must belong to destination environment. | still if desired | Existing isolated test thread or another normally configured provider; no new provider installation required for this pass. | CONTROL — NOT CAPTURED |
| Q01 — asset loading/unavailability/recovery | Inherited viewer behavior; outside minimum parser proof | Web reserved blank → iframe or Unable to load title; fullscreen refresh failure toast Page unavailable. Mobile asset/WebView spinner → retry → Page unavailable → reload, URL refresh without state reset; process crash once/repeated. | **recording** | Requires controllable asset/RPC/network failure driver or labelled viewer H. Do not remove live attachments or change live auth to provoke it. | OPTIONAL — NOT CAPTURED |
| Q02 — fullscreen, document actions, old file-view path | Inherited viewer behavior; outside minimum parser proof | Owner web hover/focus/coarse-pointer fullscreen; expand/close/Escape; zoom min/default/max/reset; long title; inline image expand/close, focused link/modified external link, allowed/blocked shortcut, no-threadRef file viewer. Desktop external-link shell is distinct but unmodified. Guest only supports its own link path. | **recording** | Real isolated page with image/link/input plus native app actions; H for URL-refresh failures/no-threadRef. Generic attachment preview via BrowserDocumentFrame is not a new ACP entry point. | OPTIONAL — NOT CAPTURED |
| Q03 — sizing and native overflow stress | Inherited viewer behavior; outside minimum parser proof | No measured heights vs table, narrow/exact/between/outside measured widths, reference clamp 80–2000, live-content fit up to 20000 on web, empty body vs long body. Native no overflow/x/y/both; Android edge handoff, fullscreen vs nested; live theme changes during load. | **recording** | R with deliberately small/long/wide pages, native Android scrolling and theme controls; H for exact numeric bounds. P07 covers the representative width/theme case, P08 native navigation. | OPTIONAL — NOT CAPTURED |

This is a reduced equivalence matrix, not a full Cartesian product. Ten affected fixture rows are the minimum consumer/path set; combine recordings where practical. Mandatory before/after media are P01 and P02. Other rows need head media because the affected consumer is distinct; an unchanged branch need not be reimplemented or fully retested to satisfy this proof round. Retain a short sanitized trace for P01/P03 that connects ACP call id → stored turn item → attachment; a client-only harness cannot prove fallback recovery.

## Capture handoff and acceptance

- Label each file by fixture, commit, client, viewport and theme; record actual dimensions. Use separate fresh timestamped run directories beneath the capturer's own scratch subdirectory.
- Real-Hermes anchor P01 must retain the original text-only completion shape. Structured rawOutput injection can bypass the very defect being fixed.
- For the label pair, preserve both collapsed summary and expanded activity details in video. A screenshot of raw JSON alone does not prove labels, grouping or inline-page placement.
- For P04 and P05, use controlled milestones; do not infer a state transition from two unrelated screenshots. A harness must say it is a harness in the visible caption and report.
- Embed the uploaded evidence in the PR/report when judging it. This pass only uploads the matrix; it does not post comments or alter the PR.
- This audit did not establish real-Hermes generation of native ACP subagents, controlled errors or repeated terminal updates. Those remain explicitly labelled driver/harness fixtures rather than silently assumed capabilities.

FRICTION: none

VERDICT: INSUFFICIENT <all affected rows: captures pending>
