--- status: graduated --- # Solution Design: Diff-View Toggle (F6) | | | | --- | --- | | **Author(s)** | Ben Stull (with Claude) | | **Reviewers / approvers** | Ben Stull | | **Status** | `draft` | | **Version** | v0.2.0 | | **Source artifacts** | Feature `benstull/vscode-cowriting-plugin#17` (F6) · Epic `#1` (closed — F6 is the first post-epic UX feature) · Parent specs: `vscode-cowriting-plugin-content/specs/coauthoring-inner-loop.md`, `specs/coauthoring-attribution.md`, `specs/coauthoring-propose-accept.md`, `specs/coauthoring-cross-rung-format.md` + `specs/coang-sidecar-contract.md` (all graduated) · F5 shipped: `#14` (PR #15, session 0015) · Lineage: `ben.stull/rfc-app#48` | **Change log** | Date | Version | Change | By | | --- | --- | --- | --- | | 2026-06-11 | v0.1.0 | Initial draft — brainstorming session 0017 | Ben Stull + Claude | | 2026-06-11 | v0.2.0 | Broaden to **any file + untitled buffers** (not just workspace files); baseline → **global** storage keyed by a URI hash; F6 decoupled from F2 `isTracked` with its own diffable predicate. `#19`, session 0019. See §11. | Ben Stull + Claude | --- ## 1. Business Context ### 1.1 Executive Summary F6 gives the human coauthor the review surface the already has. F4 made every machine change reviewable-before-landing (proposal diffs); F3 paints who owns the current text; but the operator's **own** contribution is visible only as final text. F6 adds a one-gesture **diff-view toggle**: flip the editor into a diff of the document against a **coauthoring baseline** — and back — so "what did I actually change?" is answered in one keystroke instead of git archaeology. The load-bearing design move is the baseline itself: it **advances automatically at every machine landing** (every successful `applyAgentEdit` seam application — the only way machine text ever enters the document, INV-9/INV-10), so everything the diff shows is operator-authored *by construction*. A single **pin** gesture gives the operator an arbitrary epoch when they want one. The baseline lives in extension workspace storage — never in the repo — so the sidecar, the cross-rung contract (INV-14..17), and `SCHEMA_VERSION` are untouched. ### 1.2 Background The inner-loop triad shipped across F2–F4 (threads · attribution · propose/accept) and F5 made its sidecar a cross-rung contract. Review parity is now asymmetric in the human's favor toward the *machine's* changes: machine edits arrive as anchored diffs (F4) and land tinted (F3), while the operator's own work between machine landings has no diff perspective at all. Git's diff is against HEAD — the wrong baseline mid-session, where the meaningful "before" is a coauthoring moment (a machine landing, a chosen instant), not the last commit. Issue #17 parked the design forks — **baseline choice** (the load-bearing one), snapshot home, whose-changes, and the relationship to git's own features — for this session; all are resolved here (§6.7, §9). ### 1.3 Business Actors / Roles - **Coauthor (human)** — the writer/engineer (PP-1); F6's sole user: the diff view exists to make their own contribution self-reviewable. - **Coauthor (machine)** — Claude via `@cline/sdk`; not a user of F6, but its landings define the default baseline epoch. ### 1.4 Problem Statement While coauthoring, the operator cannot see their own changes as a diff. The editor shows ownership of current text (F3) and machine intent before it lands (F4), but the human's contribution since the last meaningful coauthoring moment is only visible as final prose. The only diff available — git's — is anchored to the wrong moment (HEAD). ### 1.5 Pain Points - "What did I actually change?" requires git archaeology or memory. - Self-review during coauthoring is expensive exactly where machine-review is cheap — an inverted trust asymmetry. - No way to mark "I'm about to do a risky pass — let me review it as a diff afterwards" (no operator-chosen baseline moment). ### 1.6 Targeted Business Outcomes One gesture flips the active document into a diff of the operator's own changes against a coauthoring-meaningful baseline; the same gesture flips back; nothing about the document, sidecar, or editor state is disturbed. Self-review during coauthoring becomes as cheap as accepting a proposal. ### 1.7 Scope (business) **In scope:** the baseline lifecycle (initialize at first track, advance at machine landings, pin on demand); persistence of the baseline across window reloads (extension **global** storage — v0.2.0, §11); the toggle command + keybinding; a native `vscode.diff` view (readonly baseline left, the **live document** right — editing continues inside the diff view); test-facing surface; host E2E + unit coverage; manual smoke. **Out of scope (deferred, not forgotten):** baseline **splicing** (keeping pre-accept operator edits visible across a baseline advance — §6.7, §9); multiple named/selectable baselines; any cross-rung sharing of baselines; diffing against arbitrary git refs; changed-line gutter indicators in the normal (non-diff) editor view. **Non-goals (firm, from #17):** replacing git history; a general-purpose diff tool; changing how proposals render (F4 owns machine diffs). ### 1.8 Assumptions · Constraints · Dependencies - **Anchor:** Feature #17. Builds on the shipped inner loop (F2 `#4` · F3 `#6` · F4 `#12` · F5 `#14`); Epic #1 is closed. - The whole design leans on two shipped invariants: **INV-9** (the seam `applyAgentEdit` is the single machine-edit ingress) and **INV-10** (proposals never mutate the document) — together: *between machine landings, every buffer change is operator-authored.* - F3 attribution is **state, not history** (cannot reconstruct before-text) — confirmed: F6 needs a snapshot, and takes one; it does not consume attribution records at all. - Nothing persisted touches `.threads/`: the cross-rung contract (INV-14..17) and `SCHEMA_VERSION = 1` are untouched by construction (INV-19). - No new credential surface (INV-8 untouched); no LLM anywhere in F6 or its tests. ### 1.9 Business Use Cases - **BUC-1 (self-review)** Mid-session, the operator flips into the diff view and sees exactly what *they* changed since Claude last landed text — then flips back and keeps writing. - **BUC-2 (deliberate pass)** Before a risky rewrite pass, the operator pins the baseline; afterwards they review their whole pass as one diff. --- ## 2. Solution Proposal Keep one **baseline snapshot** per tracked document: the full document text captured at an epoch. The epoch is initialized when the document is first tracked, **advances automatically on every successful seam application** (`applyAgentEdit` — the moment machine text lands), and can be **pinned to now** by the operator. Because the seam is the only machine ingress (INV-9/10), the diff `baseline → current buffer` shows operator-authored changes by construction (INV-18) — no attribution filtering needed. A `cowriting.toggleDiffView` command (with default keybinding) opens a native `vscode.diff` editor: left side a readonly virtual document (`cowriting-baseline:` scheme) serving the baseline text, right side **the live document itself** — so the operator keeps editing inside the diff view, and toggling back simply closes the diff tab. Baselines persist in **extension workspace storage** via a vscode-free `BaselineStore` (the `CoauthorStore` pattern), surviving window reloads without ever entering the repo (INV-19). The machine-landing signal is one additive event on the seam owner: `AttributionController.onDidApplyAgentEdit`. --- ## 3. Product Personas - **PP-1 Inner-loop coauthor** — the human writer/engineer (as F2–F5); the only persona F6 serves. ## 4. Product Use Cases - **PUC-1 (toggle)** In a tracked document, run the toggle (command or keybinding) → a diff view opens: baseline left, live document right. Run it again → the diff closes and the normal editor is back. The document text, sidecar, threads, attributions, and proposals are untouched either way. - **PUC-2 (epoch follows the machine)** Accept a pending proposal → the baseline advances past it: the accepted text does **not** appear as a change in the diff; the operator's subsequent edits do. - **PUC-3 (pin)** Run "pin diff baseline" → the diff goes empty (baseline = now); everything the operator changes from here shows against the pin. - **PUC-4 (survive reload)** Reload the window mid-session → the baseline (and therefore the diff) is the same as before the reload. - **PUC-5 (graceful edges)** The toggle works on **any** text document — a file in or out of the workspace, or an untitled buffer (v0.2.0, §11); only a non-text-editor focus warns. A missing/ancient baseline is visible in the diff title (epoch + when) and one pin gesture recovers; external file changes (e.g. a git pull) show up as changes — pin recovers there too. ## 5. UX Layout No bespoke UI surface — native VS Code primitives only (the F2–F5 Claude-Code-rubric precedent). The diff view **is** VS Code's diff editor: readonly baseline on the left, the live, editable document on the right; all existing decorations and comment threads keep working on the right side because it is the same `TextDocument`. The diff tab title carries the epoch story: ` — my changes since ` where epoch reads `opened