# PR #23573 proof-audit pass 1: surface × case matrix

Head 439fbdfda0, base 8d3b061121. Diff: shared `:is(.chat-markdown,.composer-tiptap) ol { --list-gutter: max(1.25rem, 3ch) }` (was 1.25rem); composer-only duplicate removed.

## Geometry (why some surfaces clip and others don't)

`list-style-position: outside` paints the marker left of the `li`. Monocraft 14px `1. ` ≈ 27px vs old 20px gutter, so the marker sticks out ≈7px past the `ol` border box. That's only visible as clipping when an ancestor with `overflow: hidden/auto/clip` has less than about 7px of padding between its clip edge and the `ol`. New gutter 28.5px (Monocraft) / 23.36px (Liberation Sans) means it never overflows for single-digit lists.

Theme (light/dark) can't change gutter geometry. It only changes colours. Captures exist for dark (F1, F3–F7, F9) and light (F2). One theme per row is enough.

## 1. Surfaces rendering `.chat-markdown` / `.composer-tiptap`

Every `.chat-markdown` comes from `ChatMarkdown` root: `apps/web/src/components/ChatMarkdown.tsx:1601`. Composer: `apps/web/src/components/ComposerPromptEditorTiptap.tsx:1129` (`overflow-y-auto p-1 -m-1`).

| # | Surface | Call site | Clipping ancestor at the ol's left edge | Old gutter clipped (Monocraft)? |
|---|---|---|---|---|
| S1 | Assistant message | `chat/TimelineAssistantRows.tsx:42` | chat lane `overflow-x-clip`, `chat/MessagesTimeline.tsx:1024` | **YES** (the bug) |
| S2 | User bubble, short | `chat/TimelineUserMessageRow.tsx:713` (UserMessageBody); bubble `:409` `p-3` | none flush (12px padding) | no |
| S2c | User bubble, **collapsed** (>8 lines or >600 chars) | wrapper `chat/TimelineUserMessageRow.tsx:648` `max-h-44 overflow-hidden` + mask, flush around ChatMarkdown | `overflow-hidden`, 0 padding | **YES**. The PR fixes it, but no capture shows it |
| S2a | Automation/folded user prompt | `chat/TimelineUserMessageRow.tsx:187` `px-3 pb-3 max-h-96 overflow-auto` | overflow-auto, 12px padding | no |
| S3 | Proposed plan card, expanded / short | `chat/ProposedPlanCard.tsx:209`; card `:163` `p-4 sm:p-5` | none | no (indent only) |
| S3c | Proposed plan card, **collapsed** (>900 chars or >20 lines, `:79`) | `chat/ProposedPlanCard.tsx:195` `max-h-104 overflow-hidden`, flush; preview ChatMarkdown `:200` | `overflow-hidden`, 0 padding | **YES**. The body says "never clipped there", which is wrong for the collapsed state |
| S4 | `.md` file preview (workspace file) | `files/FileMarkdownPreview.tsx:26` (`px-6 py-5`), mounted at `files/FilePreviewPanel.tsx:949` | ScrollArea, 24px padding | no (indent only) |
| S4b | `.md` attachment preview | `files/AttachmentFilePreview.tsx:217` (`px-6`) | ScrollArea, 24px padding | no |
| S5 | Reasoning trace ("Thought" row expanded) | `chat/TimelineWorkEntryRow.tsx:125` inside `WorkLogDetails` kind=text, `chat/WorkLog.tsx:160` `ms-7 max-h-96 overflow-auto px-0.5` | overflow-auto, **2px** padding | **YES** (~5px cut). Not mentioned in body |
| S6 | Pending user-input question (composer banner) | `chat/ComposerPendingUserInputPanel.tsx:237`; Body `chat/ComposerBanner.tsx:310` `ps-8 sm:ps-7` inside ScrollArea | 28–32px padding | no |
| S7 | V2 event row detail | `chat/TimelineV2EventRow.tsx:105` (`ps-8`), `:180` | none flush | no |
| S8 | V2 item inspector reasoning | `chat/V2ItemInspector.tsx:254` `p-2` box | 8px padding ≥ 7px | marginal, no |
| S9 | Review-comment context chip | `chat/UserMessageContextChips.tsx:551/561` (`p-3`); `composerContextPresentation.tsx:287` (`p-3`, overflow-hidden) | 12px padding | no |
| S10 | Workflow script | `chat/WorkflowOpeningBlock.tsx:224` (folded overflow-hidden) | n/a: content is a code fence, no lists | n/a |
| S11 | PR body / review guide prose | `pullRequest/PullRequestMarkdown.tsx:80`, `pullRequest/guide/GuideProse.tsx:25` | panel padding (not traced in depth) | unlikely |
| S12 | Inline ChatMarkdown (thought previews) | `chat/TimelineWorkGroupRows.tsx:531,559`, `chat/TimelineWorkEntryRow.tsx:412` | `inline` + `line-clamp`/truncate, no block lists | n/a |
| C | Composer | `ComposerPromptEditorTiptap.tsx:1129` | already 3ch before the PR | no change |

## 2. Matrix

Fonts: W = wide/monospace (Monocraft), P = proportional (Liberation Sans stand-in). Cases: 1 = single-digit, M = multi-digit (inline `${n+2}ch` wins, unchanged by the diff), N = nested ol>ol / ol>ul. Theme collapsed (see above).

| Surface | Case | Font | 1440 | 390 |
|---|---|---|---|---|
| S1 assistant | 1 | W | COVERED F1, F1 crop (dark) | COVERED F1 390 + crop (dark) |
| S1 assistant | 1 | P | COVERED F2 (light) | UNCOVERED (low value: no clip at P) |
| S1 assistant | M | W | COVERED F3 (AE 0) | UNCOVERED (geometry-identical, low value) |
| S1 assistant | M | P | UNCOVERED (unchanged by diff, low value) | UNCOVERED |
| S1 assistant | N | W | COVERED F4 | UNCOVERED |
| S1 assistant | N | P | UNCOVERED | UNCOVERED |
| S1 assistant | task list | W | COVERED F5 | UNCOVERED |
| S2 user bubble short | 1 | W | COVERED F9 (dark) | **NOT CHECKED** (body says so) |
| S2 user bubble short | 1 | P | UNCOVERED | UNCOVERED |
| S2c user bubble collapsed | 1 | W/P | UNCOVERED (real clip surface) | UNCOVERED |
| S3 plan card expanded | 1 | W/P | **NOT CHECKED** | NOT CHECKED |
| S3c plan card collapsed | 1 | W/P | **NOT CHECKED**, and the body's "never clipped" claim is false here | NOT CHECKED |
| S4 .md file preview | 1 | W/P | **NOT CHECKED** | NOT CHECKED |
| S4b .md attachment preview | 1 | W/P | UNCOVERED (same geometry as S4) | UNCOVERED |
| S5 reasoning trace | 1 | W | UNCOVERED (real clip surface) | UNCOVERED |
| S6–S9, S11 | 1 | W/P | UNCOVERED (padded, indent-only) | UNCOVERED |
| C composer | 1, M | W | COVERED F6/F7 + recording | UNCOVERED (unchanged rule) |
| C composer | 1 | P | UNCOVERED (unchanged rule, low value) | UNCOVERED |
| Safari / Firefox | all | all | NOT CHECKED | NOT CHECKED |

## 3. Minimum captures to add (each before = 8d3b061121, after = 439fbdfda0; Monocraft + Liberation Sans)

1. **S2 user bubble @390**, single-digit `1. one / 2. two`, W and P.
2. **S3 plan card, single-digit list**, at 1440 W and P, in both states. Collapsed (>20 lines) is a predicted clip that the fix should resolve. Expanded/short is indent only.
3. **S4 `.md` file preview, single-digit list**, at 1440 W and P (indent only, expected no clip either side).
4. **S2c collapsed user bubble** (≥9 lines, starts with `1.`) at 1440 W. Predicted clip before, fixed after.
5. **S5 reasoning trace** with a `1.–3.` list, expanded, at 1440 W. Predicted ~5px clip before, fixed after.

Skip S6–S11: padded ≥8px, so the change is indent only.

## 4. How to reach each surface (seeded dev DB, as in the body's seed.py)

The seed approach: `vp run migrate-dev-db` → patch `payload_json` strings in `<wt>/.t3/userdata/statev2.sqlite` with the dev server stopped, then reload.

- **S2/S2c user bubble**: patch a `user_message` row in `orchestration_v2_projection_messages` **and** `orchestration_v2_projection_turn_items` (type `user_message`), replacing its text field (same recursive `patch()` from seed.py, marker on an existing user prompt). For S2c, use ≥9 lines (`MAX_COLLAPSED_USER_MESSAGE_LINES = 8`, `MessagesTimeline.logic.ts:2352`). 390: `preview_resize` freeform 390×844.
- **S3 plan card**: the client renders `ProposedPlanTimelineRow` (`TimelineAssistantRows.tsx:217`) from a turn item `type: "proposed_plan"` with fields `planId, markdown, streaming` (`packages/contracts/src/orchestrationV2.ts:1374`). History loads it from `orchestration_v2_projection_turn_items` (`ProjectionStore.ts:4289-4292`). The plan itself is in `orchestration_v2_projection_plans` (`kind='proposed_plan'`, payload `markdown`, contracts `:1162`). Fastest route: `SELECT thread_id FROM orchestration_v2_projection_turn_items WHERE type='proposed_plan'` in the seeded DB (real plan-mode runs exist in typical data). Rewrite `markdown` in both that turn item and the matching plans row: short (<20 lines, <900 chars) for expanded, ≥21 lines starting `1. …` for collapsed. If no plan exists, change an `assistant_message` turn item to the `proposed_plan` shape with a fresh `planId` and insert a plans row (`status 'active'`).
- **S4 `.md` file preview**: open the right panel's Files surface (`RightPanelSurfaceContent.tsx:186` → `FilePreviewPanel`), or click a file link to a `.md` in any assistant message. Put a fixture `.md` with `1. First…` in the project's workspace root (the seeded project's cwd, or a scratch project registered on the dev server; don't write into a real repo). Rendering is off by default: click **"Show rendered markdown"** (`FilePreviewPanel.tsx:974`), or set localStorage `t3code.renderMarkdown=true` (`:146`) before load.
- **S5 reasoning trace**: patch a `reasoning` turn item's `text` (contracts `:1368`) to include `1. First\n2. Second\n3. Third`, then click the "Thought" row to expand it (renders `ReasoningTraceContent`, `TimelineWorkEntryRow.tsx:117`).
- Font: Settings → Appearance → Interface font = Monocraft (as in the body's FontFace setup), then Liberation Sans. Same scroll anchor (`scrollIntoView({block:'start'})`) per pair.
