# issue7907 — read-only proof audit, pass 1

Candidate: `/home/ubuntu/wt/issue-7907`, branch `fix/issue-7907`, head `8a66d607f9ade09f546767eeda8baf59d67cd35f`, base `909e4c9482`. No PR. All rows below are REQUIRED/PROPOSED evidence, not executed or verified by this auditor. No servers, browser, edits to the candidate, tests, or captures were run. Only this requested scratch report was written/uploaded.

## Scope and causal audit

Read the complete three-file diff: runtime dependency patch, lockfile patch hash updates (web, mobile, client-runtime resolve the same package), and new browser test. Installed web package resolves to the new patch hash and contains the changed call. That confirms inspected disk contents, not the running dev bundle.

The old editor call chose line 0 when the decimal digit count changed; the candidate always passes `change.startLine`. `Editor` binds `fileInstance.applyDocumentChange`; its line-count-changing render path calls the bound method after updating rendered tokens/gutters. `VirtualizedFile.applyDocumentChange` forwards to `File`, marks DOM dirty, and calls `resetLayoutCache(..., startLine)`. That retains measured prefix heights, invalidates changed/shifted rows, clears checkpoints, and schedules reconciliation. Both 999→1000 and 1000→999 now differ from base. Same-digit line-count edits do not differ. Edits starting at zero still reset all heights. Insert/paste/delete/undo/redo and multiple selections converge on document changes; they do not merit separate theme/layout grids.

Remaining observation to prove: a gutter digit change can alter content width and wrapping. Retained offscreen measurements can temporarily differ from actual new wrapping; visible-row remeasurement must keep the caret visible through settlement and later scrolling. `reconcileHeights` resets on CODE element width change, not directly on content-column width change. Do not infer absence of delayed drift from a final screenshot or from preserving `scrollTop` numerically (some movement is legitimate).

## Consumers and equivalence classes

| Consumer / mode | Reachability and effect | Evidence decision |
|---|---|---|
| `FilePreviewPanel` → `EditableFileSurface` → `EditProvider` / `Editor` → `Virtualizer` / editable `File` | Only production editor construction found. Writable, nontruncated workspace text; wrap on/off; light/dark. Markdown/HTML/CSV source modes converge here when their rendered mode is disabled. Syntax-highlighted files wait for worker readiness, then use this same editor. Comments/selection/reveal share the same surface. | Primary affected path; matrix below. Plain text isolates runtime geometry. No language × theme × preview-mode expansion. |
| Files explorer and file surface in `RightPanelSurfaceContent` | Both cases mount the same panel. File picker (command palette and `filePicker.toggle` keybinding), project content search, chat file links/mentions/context chips, diff “open file,” reopen closed view, explorer/breadcrumb navigation converge on `rightPanelStore.openFile` / the panel callbacks. Line reveal cancels its guard on user keydown. | One real app entry is sufficient; use Files or picker. No separate capture per entry. |
| Web local hosting, hosted web connected to environment, remote/relay/tunnel | Editor layout and mutation occur in client; save uses the existing environment file path. No transport/contracts/server changes. | Local isolated web evidence sufficient for this patch; remote modes inherited, not separately verified. |
| Desktop Electron main renderer | Uses the web application URL and shared web editor. Diff changes no Electron shell, IPC, keyboard mapping, or desktop-specific branch. | No mandatory additional desktop capture for this one-line runtime fix. Inherit shared-surface scope explicitly; desktop execution remains UNTESTED. A desktop claim requires an actual optional desktop recording, not a web screenshot labeled desktop. |
| Native iOS/Android | Imports package parsing/type/highlighting helpers in review/guide screens, but no Pierre Editor/EditProvider path. | Not affected by changed editor runtime; no native editor capture row. A 390px browser is narrow web, not native mobile. |
| Read-only `ReadOnlySourcePreview` (host files, no write scope, truncated files), attachments, composer context HTML/CSS/diff source | `File` + `Virtualizer`, no edit session / editor change call. | Excluded from runtime transition matrix. |
| DiffPanel / `AnnotatableCodeView`, PR `PullRequestCodeTab` / `StyledDiffCodeView`; PR guide `GuideFileDiff` / `GuideExcerpt`; settings `CaptureShortcutConfig` and font previews; chat `UserMessageContextChips` diffs | Read-only CodeView/FileDiff/SSR previews. Unified/split, collapsed/expanded, annotations and themes do not invoke editor document changes. Library File/FileDiff ignore the additional argument; VirtualizedFileDiff has its existing three-argument method. No editable diff consumer found. | Excluded; do not multiply unified/split/theme rows. |
| Other package consumers | `reviewGuideTour`, review context/file-order/diff helpers, search/chat highlighters, worker provider and syntax/embedded-script helpers use types/parsers/highlighting/worker setup. | No editor call; no capture requirement. Existing/new tests are synthetic consumers, not real-app proof. |

## Scaled minimum capture matrix

Each row is a real app recording, not just screenshots: record focus/EOF before input, the input and immediate viewport response, settled response (at least several animation/layout cycles, e.g. 2–3 seconds), and a second small edit. Show line numbers and caret; record viewport and actual editor dimensions, theme and wrap setting. On base and candidate use identical fixture/font/zoom/layout; label before and after. Capture base failure and candidate success for A; candidate-only controls and responsive coverage suffice for B–D. If base does not fail, label that honestly and investigate geometry rather than inventing a before claim.

| ID | Actual web viewport / layout | Theme / wrap | Fixture and interaction/time sequence | Expected observable proof | Capture |
|---|---|---|---|---|---|
| A — lead reproduction | 1280×800; target editor scroll area 539×708 CSS px (measure actual area, account for chrome) | Dark, wrap ON | Fresh 999 logical lines × 240 chars, no trailing newline. Focus editor; go to real EOF and allow wrapped measurement to settle (repeat EOF navigation if needed). Enter: 999→1000. Pause, then Backspace on empty EOF: 1000→999. Pause. Repeat cycle; scroll up a page and return to EOF. | New blank line/caret remains visible after Enter; reverse transition also stays visible with no delayed jump, blank gap, overlap or lost content. Return to EOF remains coherent. | RECORDING REQUIRED, base/candidate paired. This single clip covers both affected directions. |
| B — wide layout | 1440×900 CSS px; real panel width recorded; keep long lines genuinely wrapped | Light, wrap ON | Reset 999-line fixture; same EOF Enter / pause / Backspace / pause cycle. After boundary round-trip, Enter twice (999→1000→1001); 1000→1001 is same-digit control. | Both boundary directions work with light gutter/caret legible; same-digit edit behaves normally. | RECORDING REQUIRED, candidate. |
| C — narrow web layout | 390×844 CSS px; open file surface, dismiss explorer if it steals editor width; record actual editor size | Dark, wrap ON | Reset 999-line fixture; EOF Enter / pause / Backspace / pause, then one further character edit. | Caret and line 1000 stay reachable/visible in narrow web; no layout obstruction or late viewport loss. | RECORDING REQUIRED, candidate. |
| D — wrap-off negative control | Reuse A's 1280×800 layout (no extra width permutation) | Dark, wrap OFF | Fresh fixture; EOF Enter 999→1000, pause, Backspace 1000→999, pause. Observe horizontal as well as vertical positioning. | No introduced jump, content corruption or spurious wrapped-height behavior with unwrapped rows. | RECORDING REQUIRED, candidate. |

Minimum: four scenario recordings, with A represented before and after (five clips total, or four artifacts if A is a labeled side-by-side video). B and C deliberately cover 1440 and 390 without a redundant full Cartesian product. Light/dark change resolved theme and remount the same editor; both represented. System theme collapses to its resolved light/dark. Do not switch theme halfway through a boundary scenario, since remount destroys the measurement history under test. No separate desktop/native captures required by the diff.

The existing added browser test is narrower: 539×708 component container, dark wrap, one forward Enter, assertion that logical line index 999 intersects viewport within 3 seconds. It does not establish real app/save integration, caret visibility, reverse boundary, responsive layouts, light theme, or absence of transient/delayed jumps. Existing virtualization tests cover generic prefix retention, middle edits, deletion, multiple edits, undo/redo, wrap-off and invalidation, but are geometry stubs and most bypass the Editor runtime call. This pass did not execute either suite.

## Real app seeding and environment

Use the already-isolated server at `http://localhost:6767` and workspace project `/tmp/issue-7907-project`; server was reported restarted by the parent, not health-checked here. Confirm it serves the candidate worktree/new patched dependency, not stale Vite optimized dependency output. Use its normal app authentication; do not publish credentials or pairing links. Do not restart other servers or use live T3 userdata.

Read-only filesystem inspection found `wrap-sample-999.txt` is exactly 240758 bytes, 999 logical lines, first/last line lengths 240, and no trailing newline. `boundary.txt` and `control.txt` are currently 1000 logical lines ending in a newline (240759 bytes): they are already edited states, not fresh 999-line fixtures. Reset/copy only sandbox fixture files before each run, and close/reopen or allow the normal file refresh to complete so autosave/query cache cannot silently retain previous state. Wait for pending saves before resetting files. This auditor did not reset anything.

Parent/capture operator can seed a fresh sandbox file from [the supplied 999-line sample](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/cad584233478ed93/fixture-999.txt), or generate exactly the added test's contents: join 999 rows with LF, each row `String(index+1).padStart(4,"0") + " " + "wrapped editor sample text ".repeat(12).slice(0,235)`; do not append LF. Validate count and bytes before capture. Open the actual file via Files or file picker inside the existing project/thread, with filesystem write scope, source mode, and no truncated/read-only banner. No need to send an agent turn or seed a fake conversation. If existing isolated state lacks a project/thread, create ordinary sandbox project/thread through the app; do not write its database directly.

Use normal zoom/default font consistently. The fixture's 240-character logical lines must visibly wrap; window width alone does not specify editor width. Measure 539×708 separately from 1280×800 viewport. At 390px ensure the file editor is actually shown, rather than capturing chat or an explorer-only pane. Focus editor before EOF/navigation keys and confirm caret is at the end of logical line 999, not a soft-wrapped visual row. Use platform-correct EOF shortcut (Ctrl+End on Linux/Windows, appropriate document-end command on macOS). Keep recording across input and settlement; a screenshot alone cannot demonstrate this timing bug.

## FRICTION category | event | avoidance

| Category | Event | Avoidance |
|---|---|---|
| Fixture state | Existing boundary/control files already have 1000 lines | Validate no trailing LF and exactly 999 logical lines for each fresh forward run; retain explicit reverse state only when intended. |
| Geometry | 1280px viewport mistaken for 539px editor; narrow explorer consumes width | Measure editor and viewport independently; record real dimensions and keep actual wrapping visible. |
| Virtualization | First document-end jump can settle short as rows acquire measurements | Confirm actual EOF/caret after settlement; repeat EOF navigation if needed before input. |
| Build identity | Restart alone does not prove browser loaded changed package | Record served candidate/build identity and confirm dependency patch; refresh stale optimized output through normal isolated workflow if necessary. |
| Evidence | A final screenshot hides a transient jump or labels inherited desktop coverage as tested | Record the input and delayed settlement; label all captures by actual client and revision. |
| Save/cache | Reset races app autosave or cached edited contents | Wait for save completion; reset only sandbox fixtures between scenarios and reopen through normal app refresh. |
| Scope | Native/mobile imports or split/unified read-only diffs inflate matrix | Follow actual Editor construction; four rows cover runtime behavior plus layout/theme controls. |

No concrete correctness failure is established by source inspection alone. Proof remains pending the real-app recordings above; specifically retain reverse-boundary and delayed-settlement observations before accepting the runtime change.
