{
  "summary": "Source-grounded verdict\n\n1. HIGH — `ScreenGrid` has no wide-glyph continuation-cell model, and this is the strongest exact mechanism for the screenshot’s two-character left-margin scraps. `crates/spt-term/src/screen.rs:122-145` defines each cell as only `{ ch: char, pen: Pen }`. `GridState::put_char` at `screen.rs:330-349` advances exactly one grid column per Unicode scalar, regardless of terminal display width. The erase/edit paths at `screen.rs:359-443` likewise operate on those scalar cells. `crates/spt-term/Cargo.toml` depends on `vte` but not on a Unicode-width/grapheme implementation. `GridState::render_repaint` at `screen.rs:577-629` then emits the stored cells sequentially from an absolute `CUP row;1`. Consequently, a width-2 glyph occupies one authoritative grid cell but two cells in the real terminal; absolute cursor-addressed updates and synthesized repaint disagree by one column per wide glyph.\n\nDeterministic byte-level reproducer for the wrap/scrap class, using a 3-row × 10-column terminal:\n\n```text\nChild frame fed to ScreenGrid:\nESC [ 1 ; 1 H  界 界  ESC [ 1 ; 5 H  A B C D T o  ESC [ 3 ; 1 H  Search\n\nEscaped:\n\\x1b[1;1H界界\\x1b[1;5HABCDTo\\x1b[3;1HSearch\n\nHex for each 界: e7 95 8c\nCUP bytes: 1b 5b 31 3b 31 48 and 1b 5b 31 3b 35 48\n```\n\nA width-aware VT renders the child frame correctly: the two `界` glyphs occupy columns 1–4 and `ABCDTo` occupies columns 5–10; row 2 stays blank and `Search` begins on row 3. Current `ScreenGrid` instead stores the two glyphs in logical cells 1–2, leaves logical cells 3–4 blank, and stores `ABCDTo` in cells 5–10. The cold-attach repaint therefore emits the visible row as:\n\n```text\n界界··ABCDTo\n```\n\nwhere `··` are the two erroneous stored blank cells. That byte string has physical display width 12. On a 10-column terminal, `To` wraps to row 2, columns 1–2. `render_repaint` skips the authoritative grid’s default-blank row 2 (`screen.rs:598-607`), so nothing subsequently erases the newly wrapped `To`; the next emitted nonblank row is the absolute row-3 `Search`. The initial `ESC[2J` does not prevent the defect because the wrap scrap is created after the clear, during the synthesized repaint. Repeating the same valid construction on separate rows with suffixes `Wh` and `Es` deterministically produces the reported `To`, `Wh`, and `Es` fragments at column 1. This also explains why the fragments are exactly two cells when the row has accumulated two columns of width divergence.\n\nA second non-wrap kernel shows stale-prefix corruption directly:\n\n```text\n\\x1b[1;1H界界Today\\x1b[1;5HNEW\n```\n\nA real VT’s final row is `界界NEWay`: CUP column 5 overwrites `Tod`. Current `ScreenGrid` stores `界界Today` in seven scalar cells, so CUP column 5 overwrites scalar cells `day`; its authoritative state becomes `界界ToNEW`. A cold repaint therefore preserves the stale `To` prefix. Substitute source strings beginning `When…` or `Escape…` to obtain the `Wh`/`Es` family. Mapping those exact English prefixes to Claude’s captured frame is `[INFERENCE]` because the supplied child PTY byte capture is not in the repository, but the mechanism and byte reproducer are deterministic.\n\nThe production seam that exposes this state is `crates/spt-daemon/src/broker.rs:836-852`, `OutputLog::repaint_initial`: a cold attach with `from_seq == 0` returns `self.grid.render_repaint()` at pseudo-sequence `next_seq - 1`. `OutputLog::become_controller` at `broker.rs:1048-1072` sends that one synthesized repaint before subsequent raw live frames. Thus this is not terminal-emulator residue from an uncleared old screen: `render_repaint` explicitly switches the buffer, resets SGR, clears, and homes before painting, but paints the wrong authoritative cells.\n\nFix seam for hypothesis 1: `crates/spt-term/src/screen.rs`, centered on `Cell`, `GridState::put_char`, `render_repaint`, and every cell-edit/resize operation. Model terminal columns rather than Unicode scalars: a width-2 primary glyph must own its leading cell plus an explicit continuation cell; width-0 combining input must attach to the preceding grapheme rather than advance; overwriting either half of a wide glyph must clear the whole glyph; ICH/DCH/ECH/EL/ED, scrolling, and `resized_buffer` must preserve or deliberately clear continuation invariants; repaint must emit a wide primary once and emit no byte for its continuation cell. A mere `col += unicode_width(c)` without continuation cells is insufficient because absolute overwrites and edit operations would still leave torn glyph halves and phantom blanks.\n\nFocused regression: add a stateful width-aware repaint test beside `screen.rs:893+` that feeds the exact valid 3×10 frame above, obtains `render_repaint`, applies it to a tiny independent width-aware terminal oracle, and asserts: row 1 is exactly `界界ABCDTo`, row 2 is blank, row 3 begins `Search`, no printable byte wraps into row 2, and the final cursor matches the server state. The oracle must not reuse `ScreenGrid::put_char`, or the same bug will mask itself. Boundary cases should cover one wide glyph at the penultimate column, overwrite through either the primary or continuation cell, resize clipping a wide glyph, ICH/DCH across a continuation, and a combining mark that consumes zero columns. The exact `To` test fails current code and defends the observable screenshot contract.\n\n2. HIGH as a separate/compounding defect when enabled — rc’s client-side identity `StatusRow` is an unsafe out-of-band renderer, but current v0.38.x main intentionally disables it. `crates/spt/src/rc.rs:199-208` documents the known failure and sets `STATUS_ROW_ENABLED: bool = false`; `status_row_active` at `rc.rs:210-216` therefore returns false for controllers and viewers. `traceable-reqs.toml:1603-1606` records `REQ-RC-IDMARKER-DISABLE`: the one-shot absolute paint scrolls away and its DECSC/clear/SGR bytes splice into harness drawing. If the screenshot visibly contains this identity banner, then the running binary is older than this gate, locally modified, or otherwise not exercising current-main wiring `[INFERENCE]`. The banner is evidence that the hazardous path was active; it is not evidence against the ScreenGrid width defect.\n\nThe enabled overlay sequence is `StatusRow::assert_bytes` at `rc.rs:236-247` followed by `StatusRow::repaint_bytes` at `rc.rs:249-271`:\n\n```text\nESC [ 2 ; R r              DECSTBM reserve top row\nESC 7                      DECSC\nESC [ 1 ; 1 H\nESC [ 2 K\nESC [ 1 ; COL H\nESC [ 36 m  <identity>  ESC [ 0 m\nESC 8                      DECRC\n```\n\nThere are two cursor-state problems. First, DECSTBM is emitted before DECSC. DECSTBM changes the cursor position; therefore DECSC saves the post-margin cursor, not the child’s cursor that existed before the overlay. Second, DECSC/DECRC use the emulator’s one shared DEC saved-cursor slot, so an overlay between a child DECSC and its later DECRC overwrites the child’s saved state. The repository’s own terminal model demonstrates the relevant absolute semantics: CUP/HVP at `screen.rs:791-795` addresses absolute rows and columns, while DECSTBM at `screen.rs:808-817` changes margins and resets the cursor. A scroll margin is not a coordinate translation.\n\nThe exact interleaving seam is `rc.rs:2286-2303`: pump writes a complete child `AttachRecord::Output` chunk verbatim, then scans that same chunk, and if it saw alt-screen entry or DECSTBM reset, appends `status.assert_bytes()`. PTY/output-record chunk boundaries are not TUI-frame boundaries. A minimal cursor-corruption schedule is:\n\n```text\nchild chunk A:  \\x1b[?1049h\\x1b[4;3H\nrc injection:   \\x1b[2;6r\\x1b7\\x1b[1;1H\\x1b[2K...banner...\\x1b8\nchild chunk B:  To\n```\n\nWithout rc injection, chunk B begins at row 4, column 3. With the injection, DECSTBM resets the cursor before rc saves it; DECRC restores that reset position, so `To` lands at the left edge. This is deterministic out-of-band composition even though each individual `write_all` is not byte-torn.\n\nThe geometry contract is also unsound for an absolute-addressed TUI. `attach_viewport` at `rc.rs:1870-1882` reports PTY rows as physical rows minus one while preserving all columns. The physical terminal is only given scroll margins, not a vertical CUP translation. Claude may still issue `CUP 1;n` and touch physical row 1. Reserving a top row via DECSTBM therefore cannot transparently host a child that believes its own row 1 is the first row of an `(R-1)`-row screen. A bottom physical status row would align ordinary absolute child rows better, but child DECSTBM/alternate-screen transitions would still destroy the parent margin; raw pass-through cannot make two renderers safely share one VT state machine.\n\nResize makes the collision more likely. `attach_viewport` sends an initial resize at `rc.rs:1874-1882`. `pump` starts with `last_size = None` at `rc.rs:2190-2194`, so its first loop observes the same dimensions as a change and sends another resize; when status is enabled it immediately appends another `status.assert_bytes()` at `rc.rs:2210-2225`. The target forwards resize at `crates/spt-daemon/src/attach.rs:498-506`; the broker resizes the PTY and server grid at `broker.rs:4446-4466`; ConPTY or the child may then emit an asynchronous repaint. rc paints its banner before receiving that repaint, and ordinary repaint bytes do not necessarily contain one of `ReassertScanner`’s narrow triggers. The banner can be overwritten, scrolled, or left in history while the TUI and overlay disagree about the cursor.\n\nCurrent fix seam for hypothesis 2 is already the correct immediate one: keep `STATUS_ROW_ENABLED = false` and strengthen regression coverage at the real controller construction/geometry seam, not only the existing pure flag assertion at `rc.rs:3901-3908`. A permanent identity overlay must be composed by the one authoritative renderer on every frame, with an explicit physical layout, rather than injected by rc between raw child chunks. Existing `ScreenGrid` is the natural ownership seam, but it first needs correct wide/combining-cell semantics; merely re-enabling the old `StatusRow` after the grid exists would retain the cursor-slot, margin, and live-raw-output conflicts.\n\n3. MEDIUM — initial old-geometry repaint followed by resize/live repaint can amplify a cold-attach artifact. `OutputLog::repaint_initial` snapshots the grid for the attach, while rc sends resize immediately afterward. `broker.rs:1442-1447` updates `grid` geometry on controller resize, but that does not retract the already queued initial repaint. The initial synthesized frame and subsequent child/ConPTY redraw can therefore represent different geometries. The duplicate initial resize noted above increases redraw churn. This does not by itself explain the exact `To`/`Wh`/`Es` prefixes as strongly as the missing continuation cells, but it is a deterministic composition stressor. Focused regression: cold-attach at geometry A, resize to B before the initial writer drains, then assert one state-complete geometry-B repaint precedes raw B frames, or force baseline invalidation and repaint after resize.\n\n4. LOW on current main — missing DECSTBM replay was a real ScreenGrid defect but is already closed. ADR-0043 identifies it, and `screen.rs:610-629` now emits the tracked DECSTBM (or explicit `ESC[r`) before the final cursor CUP. Tests at `screen.rs:893-936` cover the stateful post-repaint scrolling contract. It should not be selected as the present root unless the running binary predates that change.\n\n5. LOW/refuted for the live screenshot — terminal-emulator and final-output ordering. Windows Terminal is expected to implement CUP, DECSTBM, DECSC/DECRC, and Unicode width; the deterministic wide-cell mismatch already exists in server state before bytes reach the emulator. Legacy conhost DECSC behavior can worsen the enabled overlay, but it is not required for either reproducer. rc writes child output and status injection sequentially on the pump thread (`rc.rs:2286-2303`), so the relevant interleaving is logical-frame/chunk interleaving, not concurrent partial writes to stdout. ADR-0043’s Output-before-Exit FIFO and `DisplayGuard` teardown (`rc.rs:858-898`, `1529-1590`) govern final exit/parting prose; they cannot cause stable corruption while Claude’s settings TUI is still active. `crates/spt-term/src/reader.rs:31-36,118-143` also auto-answers DSR with fixed `ESC[1;1R` while forwarding the query; a delayed physical CPR can duplicate the response, but that is a separate protocol issue and does not deterministically yield these two-character wrap scraps.",
  "files": [
    {
      "path": "crates/spt-term/src/screen.rs",
      "description": "Primary root. `Cell` has no continuation/grapheme representation (122-145); `put_char` advances one cell per scalar (330-349); erases/ICH/DCH operate on scalar cells (359-443); `render_repaint` emits those cells linearly and skips authoritative blank rows (577-629), allowing over-wide synthesized rows to wrap two-character scraps into an otherwise skipped row."
    },
    {
      "path": "crates/spt-term/Cargo.toml",
      "description": "Confirms spt-term has `vte` parsing but no direct Unicode display-width/grapheme dependency; width semantics are not implemented elsewhere in this crate."
    },
    {
      "path": "crates/spt-daemon/src/broker.rs",
      "description": "Exposure seam. `OutputLog` owns the authoritative `ScreenGrid`; `repaint_initial` (836-852) synthesizes it for cold `from_seq == 0` attaches, and `become_controller` (1048-1072) sends it as the initial frame. Resize updates PTY/grid geometry at 1442-1447 and 4446-4466."
    },
    {
      "path": "crates/spt/src/rc.rs",
      "description": "Compounding client overlay and terminal lifecycle. Status marker is disabled on current main at 199-216. Dormant `StatusRow` emits DECSTBM before DECSC and absolute clear/paint/DECRC at 236-271. PTY row subtraction is at 1870-1882; duplicate first-loop resize/status assertion at 2190-2225; child raw write followed by trigger-driven overlay injection at 2286-2303; final display teardown is separately ordered at 858-898 and 1529-1590."
    },
    {
      "path": "crates/spt-daemon/src/attach.rs",
      "description": "Resize-forwarding boundary. `AttachRecord::Resize` from an attached controller is forwarded unchanged to `brain.resize(rows, cols)` at 498-506; `send_attach_resize` encoding is 698-707. It does not repair layout or coordinate semantics."
    },
    {
      "path": "crates/spt-term/src/reader.rs",
      "description": "Separate DSR behavior, not ranked root: fixed CPR `ESC[1;1R` constants at 31-36; drain detects and answers before forwarding the original query chunk at 118-143."
    },
    {
      "path": "docs/adr/0031-server-side-screen-grid-render-repaint.md",
      "description": "Defines the authoritative current-screen/synthesized cold-repaint architecture and makes clear that live frames remain unchanged raw bytes."
    },
    {
      "path": "docs/adr/0043-terminal-render-lifecycle.md",
      "description": "Separates ordered Output-before-Exit, unconditional rc teardown, picker renderer ownership, and ScreenGrid DECSTBM replay. Useful to avoid conflating exit residue with the active-settings corruption."
    },
    {
      "path": "docs/KNOWN-HAZARDS.md",
      "description": "Section 7.47 records the renderer-ownership invariant and prior stale-glyph failure family; it does not currently record wide-glyph continuation invariants."
    },
    {
      "path": "docs/NEXT-MILESTONE-BUG-TRIAGE.md",
      "description": "Historical source-grounded status-marker RCA: one-shot absolute marker, in-flight DECSTBM/DECSC injection, resize/scroll disappearance, and recommendation to disable pending proper grid-backed composition."
    },
    {
      "path": "traceable-reqs.toml",
      "description": "`REQ-RC-IDENTITY` at 1507-1510 documents the old reserved-top-row design; `REQ-RC-IDMARKER-DISABLE` at 1603-1606 documents why current main gates it off."
    }
  ],
  "architecture": "The child Claude TUI writes an ordinary VT byte stream into the hosted PTY. The broker drain fans raw live bytes outward and independently advances one authoritative `ScreenGrid`. On a cold attach, the broker does not replay the retained raw ring; it synthesizes one full repaint from `ScreenGrid`, sends it as the initial Output frame, and then resumes unchanged raw live frames. rc decodes each Output and writes it verbatim to the physical terminal. This layering means corruption can be assigned precisely: (1) the child frame is valid under normal Unicode terminal widths; (2) the server grid currently maps Unicode scalars, not terminal columns, so its authoritative screen and cold repaint can already be wrong; (3) an enabled rc StatusRow is a second renderer mutating the same physical VT state outside both the child and server grid models; (4) Windows Terminal merely executes the resulting bytes; and (5) teardown/output-before-exit ordering acts only when the viewport ends. The exact screenshot-class `To`/`Wh`/`Es` scraps are best explained by layer 2: missing width-2 continuation cells insert two phantom logical blanks before an absolute right-hand segment, causing the synthesized row to become two physical cells overwide and wrap its last two characters into a blank row that repaint skips. The identity banner, if present, proves the hazardous layer-3 path was enabled in the running binary and can compound cursor/layout damage, but current v0.38.x main intentionally removes that path by constructing no StatusRow and reporting the PTY’s full dimensions."
}