# Composer list proof matrix — pass 1

For [#17138](https://github.com/pingdotgg/t3code/pull/17138) and [#17124](https://git.cafe/pingdotgg/t3code/issues/17124).

Before: [044f07257c](https://github.com/pingdotgg/t3code/commit/044f07257c9684ae4543bd1e1ebf9c394e9e3830). After: [903b0e3070](https://github.com/pingdotgg/t3code/commit/903b0e3070d85aa093916534d5972b582d9419f0), local adapted head in `/home/ubuntu/wt/issue-17124`. After SHA may change solely for a fixture script; record the actual SHA and confirm the helper diff is identical. Remote PR head differs; its existing checks and approvals do not prove this adapted head.

Read-only inspection covered the complete two-file diff: `apps/web/src/composer-list-continuation.ts` and its test. Only the helper changes production behavior. Main already has required `cursorAfter` and caller support. No Shift+Tab change is in scope.

## Environment and seeding

Primary performs the integrated check; evidence agent captures later. This pass did not open a browser, access the running app, run tests or a server, edit the worktree, push, or comment. The existing isolated app at `http://localhost:6215` is reserved for that later work; do not assume its served revision from its port.

Use a real ChatComposer in a stopped synthetic thread within isolated T3 state, alongside representative recent project/thread history copied read-only into the sandbox using the documented dev migration procedure. Never run against or write to live `~/.t3/userdata`. Do not expose real conversations or credentials in evidence. Use the same synthetic draft and history on both revisions. A standalone helper page, fabricated DOM, or editor-only test is insufficient.

Seed draft state through the app's normal draft/editor path; rich fixtures must use production `buildDocJson` and `serializeEditorDoc`, not hand-built list DOM. Confirm the canonical serialized starting text before each operation. Canonicalization differences must be recorded, not silently treated as exact fixtures. Use actual keyboard events through ChatComposer. Keep autocomplete closed, IME inactive, selection collapsed unless explicitly testing the guard, no literal pending answer, and Shift+Enter configured to insert a newline rather than submit. Use a harmless fixture-only send/queue path for rendered-message proof, without starting a real provider turn.

Capture both revisions at **1440×900 and 390×844 CSS pixels**, each in **light and dark**, at the same zoom, font, DPR, app layout and browser. The 390-width run is responsive web, not native mobile verification. Record browser/OS and actual revision. No tokenized URL may appear in a capture. Show the real composer, keyboard operation, caret result through typing, and relevant rendered message. Pair and label Before/After with matching chapter timestamps; supplement recordings with exact serialized draft observations because rich indentation alone can conceal flat Markdown.

## Minimal fixture matrix

Notation: `\n` is a newline; spaces are literal; `|` denotes the caret and is not fixture text. Reset to each stated fixture between chapters. Every affected row requires both composer modes and all four viewport/theme combinations on both revisions. All rows have **interaction=recording**.

| Row / chapter | Fixture and actions | After acceptance / before discriminator | Coverage and capture |
| --- | --- | --- | --- |
| A — parent widths, fresh child, caret, rendered result | `1. parent\n2. ch|ild`; Tab, type X. Reset to `10. parent\n11. ch|ild`; Tab, type X; send/queue the synthetic result and show its rendered message. | After: `1. parent\n   1. chXild` and `10. parent\n    1. chXild`. Before: two spaces with original 2/11. After message is a genuinely nested list; serialized content agrees, not merely rich visual indentation. | Ordered content columns 3/4, reset numbering, mid-word caret after replacement, draft-to-message path. interaction=recording; rich/plain × 1440/390 × light/dark × before/after. |
| B — existing children and deeper descendants | `2. parent\n   1. sibling\n      1. grandchild\n3. ch|ild`; Tab, type X. | After last line: `   2. chXild`; existing lines unchanged. Before last line: `  3. chXild`. Continue at the nearest child level, ignoring the deeper grandchild. | Existing child indentation and numbering; backward scan across descendants. interaction=recording; rich/plain × 1440/390 × light/dark × before/after. |
| C — reverse through empty-item lift and exit | `1. parent\n   1. child\n      1. |`; Shift+Enter three times, pausing after each. After each pause, type X to prove the caret, then delete only X before proceeding. | After last line successively `   2. `, `2. `, then empty; X appears after each marker, finally at column zero. Plain before removes the nested marker immediately. Rich before already lifts: capture it as parity/control, not a new rich Enter fix. | One-level reverse operation, parent numbering, repeated lift, top-level exit, continuation caret. interaction=recording; rich/plain × 1440/390 × light/dark × before/after. |
| D — fallback, scan boundary, prefix caret | `7. solo|`: Tab then X; reset `1. parent\n\n2. ch|ild`: Tab then X; reset `10. parent\n|11. child`: Tab then X in plain mode. | First two retain numbers and add exactly two leading spaces, with X at the original text position. Blank line prevents reaching an earlier parent. Prefix case after: `10. parent\n    X1. child`; no caret jump to the end. Rich markers are structural: test its reachable start-of-content caret instead and record the normalization, rather than claiming an inaccessible prefix offset. | No-parent fallback, hard scan stop, caret before replaced numeric prefix versus content caret. interaction=recording; rich/plain × 1440/390 × light/dark × before/after, prefix offset plain-only. |
| E — shared-helper compatibility | Independently seed `- parent\n- ch|ild` and `- [x] parent\n- [ ] ch|ild`; Tab then X. Seed `1. parent\n   09) sibling\n2) ch|ild`; Tab then X. Repeat with current `2. ch|ild` instead. | Bullet/task children retain their markers and use two spaces (checkbox is content). Matching `)` child becomes `   10) chXild`; mismatched `.` child starts `   1. chXild`, reusing the child indent without continuing incompatible numbering. | Non-ordered callers affected by generalized helper, task width, padded increment, delimiter compatibility. interaction=recording; rich/plain × 1440/390 × light/dark × before/after. |

## Fewest recording sequences

Use one chaptered sequence **A→B→C→D→E per composer mode**, resetting fixtures between chapters. All rows share these recordings; do not create a separate clip for every fixture. This is **2 sequences × 4 viewport/theme combinations × 2 revisions = 16 recordings total**, with chapter indexes for each row. Width and theme changes are separate runs to keep matched evidence readable. Operations within each chapter may share one clip; repeat a failed chapter after a clean reset rather than concealing it with a successful endpoint screenshot.

## Caller and boundary proof

The only production direct callers of `listIndentForTab` and `listContinuationForEnter` are in `useComposerCommandKey`, used by ChatComposer. Tab reaches the helper in rich and plain modes. Rich list/task Enter bypasses the helper and reaches `splitOrLiftListItem` in ComposerPromptEditorTiptap; plain Enter reaches the helper. `nextOrderedMarkerText` is also used by rich sibling alignment/renumbering and remains unchanged. `applyPromptReplacement` already honors the expanded caret offset, maps it through chip offsets, and updates the actual editor.

Primary should also confirm selection/IME/literal-answer guards, autocomplete precedence and code-block handling from existing focused coverage; these unchanged paths do not need duplicate themed clips. Parent whitespace outside the ordinary one-space case, tabs, malformed lists and multiline chip boundaries remain targeted logic-test concerns, not additional visual sequences. The new backward scan stops on any non-list line; blank-line fallback in D is the minimal observable control. Do not claim this pass exhaustively verifies every whitespace form.

Web local/hosted and desktop share the changed editor; this matrix proves the real web route only. Native mobile has a separate composer and no changed caller. No provider, service, wire contract, Settings action, command-palette action or remote-specific implementation changes. Rich-text preference selects both relevant modes. Shift+Tab retains its existing plan-mode/focus behavior; do not use it as the reverse proof. Following-item renumbering on Tab is outside scope.

No merge-readiness verdict or visual pass is asserted: all recordings and the integrated check remain pending.

FRICTION category | what | avoidance
--- | --- | ---
Evidence provenance | Remote PR evidence belongs to another head, and rich display can hide flat serialized Markdown | Pin actual before/after revisions, seed canonical real-app drafts, and pair keyboard recordings with serialized observations.

Status: proof plan complete; integrated verification and capture pending.
