add spec ./specs/coauthoring-rendered-preview.md (status: graduated)

This commit is contained in:
Ben Stull
2026-06-11 08:29:03 -07:00
parent cc67643989
commit 5b185a86bc
+471
View File
@@ -0,0 +1,471 @@
---
status: graduated
---
# Solution Design: Rendered Track-Changes Preview (F7)
| | |
| --- | --- |
| **Author(s)** | Ben Stull (with Claude) |
| **Reviewers / approvers** | Ben Stull |
| **Status** | `draft` |
| **Version** | v0.1.0 |
| **Source artifacts** | Feature `benstull/vscode-cowriting-plugin#21` (F7) · Deferred task `#22` (intra-diagram mermaid diffing) · Epic `#1` · Builds on F6 `#17` (PR #18) + `#19` (PR #20) · Parent specs: `coauthoring-diff-view.md` (F6, graduated), `coauthoring-inner-loop.md`, `coauthoring-attribution.md`, `coauthoring-propose-accept.md` · Lineage: `ben.stull/rfc-app#48` |
**Change log**
| Date | Version | Change | By |
| --- | --- | --- | --- |
| 2026-06-11 | v0.1.0 | Initial draft — brainstorming session 0020 (from the F6-follow-up ideation in session 0019) | Ben Stull + Claude |
---
## 1. Business Context
### 1.1 Executive Summary
F6 gave the human coauthor a *diff* of their own changes since a coauthoring
baseline — but as a two-pane native `vscode.diff` of **raw text**, with only the
right pane editable. For prose, that's the wrong altitude: a writer wants to see
the **rendered** document and what changed in it, the way "track changes" /
"suggesting mode" works in Word or Google Docs — not a raw-markdown split-diff.
F7 adds a **rendered track-changes preview**: a webview, opened beside the normal
(fully editable) source editor, that renders the markdown document and marks
**what changed since the F6 baseline** — additions highlighted, deletions struck
through — updating live as the human and Claude edit. It has **full mermaid
support**: fenced `mermaid` diagrams render as diagrams, and a diagram that
changed since the baseline carries a "changed" badge (intra-diagram diffing is a
deferred follow-up, #22). F7 **reuses the F6 baseline** as its "before" and adds
no new persistence: it is a pure, read-only view (INV-20).
### 1.2 Background
The inner loop shipped F2F5 (threads · attribution · propose/accept ·
cross-rung), F6 added the baseline + a native diff toggle (#17), and #19
broadened F6 to any file. In manual testing the operator found the raw two-pane
diff unintuitive for prose ("the diff view isn't intuitive, and only the right
document being editable isn't either") and asked for a single, intuitive surface
that shows the rendered doc and its changes. The ideation (session 0019) compared
a single-pane change gutter, inline track-changes marks, and a **rendered
preview with change highlights**; the operator chose the rendered preview and
locked the architecture (custom webview; `markdown-it` + `mermaid` + `diff`
libraries; block-level diff with atomic code/mermaid blocks). Those decisions are
recorded in #21 and are the inputs to this design.
### 1.3 Business Actors / Roles
- **Coauthor (human)** — the writer/engineer (PP-1); F7's sole user.
- **Coauthor (machine)** — Claude via `@cline/sdk`; not a user of F7, but its
landings advance the F6 baseline F7 renders against.
### 1.4 Problem Statement
A markdown writer cannot see, in the *rendered* document, what they (and Claude)
changed since the last coauthoring moment. F6's diff is raw text in a split pane;
the only "rendered" view (VS Code's built-in preview) shows the current document
with no notion of a baseline or what changed.
### 1.5 Pain Points
- Reviewing prose changes as raw-markdown diff hunks is low-altitude and noisy
(markup syntax, reflowed lines) — you read the source, not the document.
- Mermaid diagrams, tables, headings, lists — the things a render *clarifies*
are exactly where a raw diff is least legible.
- The two-pane, right-only-editable diff is an unfamiliar editing posture.
### 1.6 Targeted Business Outcomes
Open one preview beside the document; see the rendered doc with your changes
since the baseline marked inline (additions highlighted, deletions struck);
keep editing the normal source pane (where you and Claude work) and watch the
preview update live. Self-review of prose becomes a glance at a familiar
"track-changes" rendering.
### 1.7 Scope (business)
**In scope:** a command that opens a **rendered track-changes preview** webview
beside the active **markdown** document; rendering the document via `markdown-it`
with **full mermaid** diagram support; computing a **block-level** diff against
the F6 baseline (word-level refinement inside changed prose blocks); marking
added / removed / changed blocks and inline ins/del; a **whole-diagram "changed"
badge** for changed/added mermaid (and code) blocks; live update on edit
(debounced) and on baseline advance/pin; bundling mermaid as a **webview-local**
asset (no CDN); unit + host-E2E coverage of the pure render model; manual smoke.
**Out of scope (deferred, not forgotten):** **intra-diagram mermaid diffing**
(#22 — v1 uses the whole-diagram badge); preview→source **scroll-sync**;
non-markdown documents (the preview is offered only for markdown); export /
print / copy-as-clean; editing *in* the preview; per-author coloring in the
preview (F3 attribution lives in the editor, not here); replacing F6 (F7
**coexists** — see §6.7).
**Non-goals (firm):** a general-purpose markdown previewer to rival the built-in
one (F7 is specifically the *track-changes* render); a WYSIWYG editor; any change
to the sidecar, the cross-rung contract, or `SCHEMA_VERSION`.
### 1.8 Assumptions · Constraints · Dependencies
- **Anchor:** Feature #21; deferred mermaid task #22. Builds on F6 (#17/#19) and
the shipped inner loop.
- **Reuses the F6 baseline** (`DiffViewController` / `BaselineStore`, global
storage keyed by URI hash, INV-18/19) as the "before" text; F7 adds **no**
persistence and never writes the baseline (INV-20).
- **New library dependencies (npm, runtime):** `markdown-it`, `mermaid`,
`diff` (jsdiff). **No** VS Code `extensionDependencies`; **no** CDN — mermaid
is bundled as a webview-local asset (CSP-safe), never in the extension-host
bundle (the `@cline/sdk` precedent: heavy deps stay out of the host bundle).
- No LLM anywhere in F7 or its tests; no network; no new credential surface
(INV-8 untouched).
- Nothing persisted touches `.threads/`; INV-14..17 + `SCHEMA_VERSION` untouched.
### 1.9 Business Use Cases
- **BUC-1 (prose review)** Mid-session the writer opens the preview and sees the
rendered chapter with their additions highlighted and deletions struck since
Claude last landed — then keeps writing, watching it update.
- **BUC-2 (diagram review)** A doc with a mermaid flowchart: after editing the
diagram, the preview renders the new diagram with a "changed since baseline"
badge, so the writer notices the diagram moved without hunting the fence.
---
## 2. Solution Proposal
A new **webview panel** (`cowriting.trackChangesPreview`) opened beside the
active markdown editor by a command. On open and on every (debounced) document
change or baseline epoch change, the **extension host** computes a **block-level
diff** between the F6 baseline text and the current buffer, renders it to
annotated HTML with `markdown-it` (prose blocks carry inline `<ins>`/`<del>` from
a word-level refinement; code and mermaid fences are **atomic** — whole block
added/removed/changed), and **posts the HTML to the webview**. The webview swaps
in the HTML and runs **mermaid** over any `<pre class="mermaid">` blocks (mermaid
needs a DOM, so it runs client-side), applying CSS for added/removed/changed
marks and the diagram "changed" badge. The document, the sidecar, and the F6
baseline are never mutated (INV-20). The whole render pipeline downstream of "get
baseline + current text" is a **pure function** (`trackChangesModel.ts`),
unit-testable with no vscode and no webview (INV-22).
---
## 3. Product Personas
- **PP-1 Inner-loop coauthor** — the human markdown writer/engineer (as F2F6);
the only persona F7 serves.
## 4. Product Use Cases
- **PUC-1 (open / close)** In a markdown editor, run **"Cowriting: Open
Track-Changes Preview"** → a preview opens in the column beside the editor,
rendering the doc with changes-since-baseline marked. Closing the panel (or
re-running the command) dismisses it. The document is untouched.
- **PUC-2 (live edit)** Type in the source editor → the preview updates
(debounced) with the new additions highlighted; nothing in the document,
sidecar, or baseline changes.
- **PUC-3 (epoch follows the machine)** Accept a Claude proposal → the F6
baseline advances (INV-18) → the preview re-renders: the accepted text is **no
longer** marked as a change; the writer's subsequent edits still are.
- **PUC-4 (pin)** Run "Cowriting: Pin Diff Baseline to Now" (the F6 command) →
the preview empties of marks (baseline = now); edits from here are marked.
- **PUC-5 (mermaid)** A changed or added `mermaid` block renders as the **new**
diagram with a **"changed since baseline"** badge; a removed diagram renders
struck/ghosted at the block level. (Intra-diagram diffing → #22.)
- **PUC-6 (graceful edges)** Non-markdown active editor → the command warns and
opens nothing (F7 is markdown-only; F6 covers any file). A malformed
mermaid/markdown block renders an inline error chip in place of that block, not
a broken preview. Storage/baseline absent → the preview shows the doc with
*no* marks (everything is "since opened") and a one-line note.
## 5. UX Layout
A webview in the editor column **beside** the source (the `ViewColumn.Beside`
convention of the built-in preview). The source editor stays the normal, fully
editable editor — the writer and Claude work there; F3 attribution tint / F4
proposals keep working there unchanged. The preview is **read-only rendered
output**: the document as markdown, with:
- **Added** text/blocks — a soft green background (`<ins>`), themeable.
- **Removed** text/blocks — struck-through, muted (`<del>`).
- **Changed atomic block** (code / mermaid) — rendered new, with a small
**"changed"** badge at the block's top-right corner.
- A compact **header bar**: `Track changes since <epoch>` (`opened` / `Claude
landed` / `pinned`, reusing F6's epoch label) and a `+N M` summary.
Colors derive from the active VS Code theme via webview CSS variables
(`--vscode-diffEditor-insertedTextBackground`, `…removedTextBackground`, etc.)
so it matches light/dark/high-contrast. No bespoke chrome beyond the header.
---
## 6. Technical Design
### 6.1 Invariants
Parent invariants INV-1..INV-19 carry over unchanged. F7 adds:
- **INV-20 (pure read-only preview)** F7 never mutates the document, the
sidecar, the attribution state, or the F6 baseline. It only *reads* the
baseline text (via the F6 controller) and the current buffer.
- **INV-21 (sealed webview)** The webview loads only **local** bundled assets
under a strict CSP (no remote scripts/styles, no CDN); mermaid is a bundled
webview asset. No network, no LLM, no credential surface (INV-8 untouched).
- **INV-22 (deterministic, testable render model)** Everything downstream of
`(baselineText, currentText) → annotated HTML` is a **pure, vscode-free**
function (`trackChangesModel.ts`): same inputs → same HTML, unit-testable with
no editor and no webview.
- **INV-23 (atomic non-prose blocks)** Code and mermaid fences are diffed at the
**whole-block** level only — never word-refined, never partially rendered. A
changed/added one renders fully (with a "changed" badge); a removed one
renders struck at block level. (Guards against rendering half a diagram.)
### 6.2 High-level architecture
Two new units plus a webview asset; the F6 controller is reused for the baseline.
- **`trackChangesModel.ts`** (`src/`, **vscode-free, pure** — INV-22) — the
engine: `renderTrackChanges(baselineText, currentText): string` (annotated
HTML). Internals: split both texts into top-level markdown **blocks**; LCS-diff
the block sequences by normalized text → `unchanged | added | removed |
changed` block ops; for a `changed` **prose** pair, compute a **word-level**
`diff` and emit inline `<ins>`/`<del>`; **code/mermaid** blocks stay atomic
(INV-23); render each block's markdown via `markdown-it` (mermaid fences →
`<pre class="mermaid">…</pre>` for client-side rendering), wrapping each block
in a `<div class="cw-blk cw-added|removed|changed">`. Pure: `markdown-it` and
`diff` are libraries; no vscode, no DOM.
- **`trackChangesPreview.ts`** (`src/`, vscode layer) — the webview controller:
registers `cowriting.showTrackChangesPreview`; owns one panel per document
(`Map<uriKey, WebviewPanel>`); on open / debounced `onDidChangeTextDocument` /
F6 `onDidChange*` epoch change, reads the **baseline** (from
`DiffViewController`) + the current buffer, calls `renderTrackChanges`, and
`postMessage`s the HTML; builds the sealed HTML shell (CSP, nonce, local
asset URIs). Markdown-only guard; disposes panels on close.
- **Webview asset** (`media/preview.js` + `media/preview.css` + bundled
`mermaid`) — receives `{html}` messages, swaps `innerHTML`, runs
`mermaid.run()` over `.mermaid` blocks, applies theme CSS variables. Built as a
**separate esbuild bundle** (`out/media/preview.js`) so mermaid never enters
the extension-host bundle.
- **F6 reuse:** `DiffViewController` exposes the baseline (`getBaseline(uriString)`
already returns `{text,…}`); F7 subscribes to baseline epoch changes. A small
additive event (`onDidChangeBaseline`) may be added to the F6 controller so the
preview refreshes on advance/pin without polling (additive, like
`onDidApplyAgentEdit`).
```mermaid
flowchart LR
edit["source editor\n(human + Claude edits)"] -- onDidChangeTextDocument (debounced) --> ctl
base["F6 baseline\n(DiffViewController, INV-18/19)"] -- onDidChangeBaseline --> ctl["trackChangesPreview\n(vscode layer)"]
ctl -- "(baselineText, currentText)" --> model["trackChangesModel\n(pure, vscode-free)\nblock diff + markdown-it"]
model -- annotated HTML --> ctl
ctl -- postMessage{html} --> wv["webview\n(sealed CSP, local assets)\nmermaid.run() on .mermaid"]
```
### 6.3 Data model & ownership
**No persisted artifact of any kind** (INV-20). F7 holds only in-memory webview
panels keyed by document URI; the baseline it reads is owned by F6. The only
on-the-wire model is the **annotated block** the engine produces (host→webview),
which is transient HTML, not stored:
```ts
// internal to trackChangesModel.ts (illustrative — not persisted)
type BlockOp =
| { kind: "unchanged"; html: string }
| { kind: "added"; html: string }
| { kind: "removed"; html: string } // rendered struck at block level
| { kind: "changed"; html: string; // prose: inline <ins>/<del>; atomic: badge
atomic: boolean }; // true for code/mermaid (INV-23)
```
### 6.4 Interfaces & contracts
- **`trackChangesModel`** (vscode-free): `renderTrackChanges(baselineText: string,
currentText: string, opts?: { mermaid?: boolean }): string` — returns the inner
HTML for the preview body. Deterministic (INV-22). Also exports
`diffBlocks(baselineText, currentText): BlockOp[]` (the pre-render model) for
fine-grained unit tests.
- **`TrackChangesPreviewController`** (vscode layer):
`show(document)` — open/reveal the panel for a markdown doc (warn + no-op for
non-markdown); `refresh(document)` — recompute + post (debounced internally);
`isOpen(uriString): boolean` and `getLastModel(uriString): BlockOp[] | undefined`
(test seam — the last computed model, so host E2E can assert marks without
reading webview DOM). Disposable.
- **`DiffViewController`** (F6, additive): `readonly onDidChangeBaseline:
vscode.Event<{ uri: string }>` fired on every capture (open/advance/pin) so the
preview refreshes; mirrors the existing internal `onDidChangeEmitter`.
- **Command / keybinding** (`package.json`): `cowriting.showTrackChangesPreview`
("Cowriting: Open Track-Changes Preview", palette; default keybinding
`ctrl+alt+r`, `when: editorLangId == markdown`). Registered as a warning stub in
the no-folder path is **not** required — like F6 (#19) it is workspace-
independent (the baseline is global storage), so it is registered for real.
- **Build:** `esbuild.mjs` gains a second entry building `media/preview.ts` →
`out/media/preview.js` (IIFE, bundles `mermaid`). `markdown-it` + `diff` bundle
into the host `out/extension.cjs` as today.
### 6.5 PerProduct-Use-Case design
- **PUC-1 (open/close):** command → markdown guard → create or reveal a
`WebviewPanel` (`viewType "cowriting.trackChangesPreview"`, `ViewColumn.Beside`,
`retainContextWhenHidden: false`, `localResourceRoots = [out/media]`). Build the
sealed shell (CSP `default-src 'none'; script-src 'nonce-…'; style-src
'nonce-…' …; img-src data:`), then `refresh`.
- **PUC-2 (live edit):** subscribe to `onDidChangeTextDocument` filtered to the
panel's doc; **debounce** ~150 ms; recompute + post. Pure model keeps this
cheap; O(document) per render at inner-loop scale.
- **PUC-3 / PUC-4 (epoch follows machine / pin):** subscribe to F6
`onDidChangeBaseline`; on fire for the panel's doc, `refresh`. Accepted text is
in the new baseline, so it renders unmarked.
- **PUC-5 (mermaid):** the engine emits `<pre class="mermaid">SRC</pre>` for a
`mermaid` fence and tags the wrapping block `cw-changed`/`cw-added` when the
fence text differs from / is absent in the baseline; the webview runs
`mermaid.run()` and CSS draws the badge. Atomic (INV-23) — never word-diffed.
- **PUC-6 (graceful edges):** non-markdown → `showWarningMessage`, no panel.
A block that throws in `markdown-it`/`mermaid` → the engine/webview substitutes
an inline error chip for that block; the rest renders. No baseline yet → render
current doc with zero marks + a header note.
### 6.6 Non-functional requirements & cross-cutting concerns
Render cost is O(document) on a debounced edit — fine at inner-loop scale; large
docs (>~a few hundred KB) can cap the live debounce or render on idle (a tunable,
not v1-critical). The webview is **sealed** (INV-21): local assets only, strict
CSP with a per-load nonce, no network. mermaid (~MB) lives only in the webview
bundle, never the host bundle (the `@cline/sdk` size discipline). Theme-aware via
CSS variables. No telemetry, no LLM, no credentials.
### 6.7 Key decisions & alternatives considered
| Decision | Chosen | Alternatives rejected |
| --- | --- | --- |
| **Render surface** | **Custom webview** we own | **Augment the built-in markdown preview** (markdown-it plugin + CSS) — the built-in preview is hardwired to render the *active document's text*, not a diff artifact; can't cleanly feed it a baseline-vs-current delta. **Decoration-only in the editor** — that's the F6/single-pane path, not a *rendered* view |
| **Libraries vs roll-our-own** | **Depend on `markdown-it` + `mermaid` + `diff`** (npm libs), **roll the track-changes integration** (no off-the-shelf "rendered markdown diff w/ mermaid") | **roll a markdown/mermaid renderer** (months of work, worse); **depend on a 3rd-party VS Code *extension*** (e.g. a mermaid-preview extension) — fragile `extensionDependencies`, install-coupled, quality risk |
| **Where each layer runs** | markdown-it + diff in the **host** (post HTML); **mermaid in the webview** (DOM-bound) | mermaid in the host (no DOM; headless mermaid is heavy/brittle); everything in the webview (ship the diff engine into the sandbox — harder to unit-test, INV-22 lost) |
| **Diff granularity** | **Block-level + word refinement in prose; code/mermaid atomic** (INV-23) | **pure inline source ins/del** — breaks on structural edits, renders half a mermaid fence; **DOM/SVG diffing** — complex, mermaid SVGs diff poorly |
| **Mermaid changes (v1)** | **Whole-diagram "changed" badge**; intra-diagram diff **deferred (#22)** | **intra-diagram node/edge diff now** — large unscoped design (source vs parsed-graph vs SVG; layout reflow) — explicitly deferred by the operator |
| **Relationship to F6** | **Coexist** — F7 is the markdown track-changes view; F6's native diff/gutter still serves **any file incl. code** | **replace F6** — F6 works on non-markdown where a rendered preview is meaningless; removing it loses coverage |
| **Asset delivery** | **Bundle mermaid as a webview-local asset** (esbuild second entry), strict CSP | **CDN/remote mermaid** — violates INV-21 (offline/CSP); **host-bundle mermaid** — bloats `extension.cjs` for code that only runs in the webview |
| **Open keybinding** | `ctrl+alt+r` (`when: editorLangId == markdown`) — "r" for *rendered*; unbound in stock VS Code | `ctrl+k v` (taken by the built-in preview); `cmd+…` (macOS-specific, the F6 §6.7 reasoning) |
### 6.8 Testing strategy
- **Unit (vitest, vscode-free):** `trackChangesModel` — `diffBlocks` /
`renderTrackChanges` over fixtures: pure-addition, pure-deletion, prose
modification (asserts inline `<ins>`/`<del>`), block insert/remove/reorder,
a **code** fence change (atomic — no inline ins/del, whole block `cw-changed`,
INV-23), a **mermaid** fence change (atomic + emits `<pre class="mermaid">` +
`cw-changed` badge class), unchanged doc (no marks), malformed block (error
chip, rest renders), determinism (same inputs → identical HTML, INV-22).
- **Host E2E (`@vscode/test-electron`, the F2F6 pattern, no LLM):** open a
markdown fixture → run `cowriting.showTrackChangesPreview` → assert a panel is
open (`isOpen` true, viewType matches) and `getLastModel()` shows the expected
block ops (e.g. `opened` baseline → no changes; type → an `added`/`changed`
block; programmatic propose+accept → baseline advanced, accepted block now
`unchanged`; pin → no marks). Webview **DOM/mermaid rendering** is *not* asserted
in E2E (sealed sandbox) — covered by manual smoke. Non-markdown doc → command
warns, no panel.
- **Live smoke (manual — `docs/MANUAL-SMOKE-F7.md`):** open a markdown doc with
prose + a mermaid diagram + a code block → edit prose (see ins/del) → edit the
mermaid (see the diagram re-render with the "changed" badge) → ask Claude to
edit a selection + accept (the accepted text drops its marks) → pin (marks
clear). Verify light/dark theming and that `git status` shows nothing.
### 6.9 Failure modes, rollback & flags
`markdown-it`/`mermaid` throwing on a block → inline error chip for that block,
preview still renders (PUC-6). Baseline missing → render with no marks + a note.
Webview disposed by the user → controller drops the panel; re-run the command to
reopen. No feature flag: the preview is a pure read-only view (INV-20) — not
opening it is the off state; rollback is reverting the PR with zero data
migration (nothing persisted). Large-doc perf → debounce/idle cap (tunable),
never a correctness issue.
---
## 7. Delivery Plan
### 7.1 Approach / strategy
One planning-and-executing session (F7 = #21), plan written just-in-time from
this spec — the F2F6 precedent. Host-E2E tier (a VS Code extension has no
browser/deploy stage); no LLM in CI. The webview's *visual* rendering (mermaid,
theming) is verified by the manual smoke, not automated — the automated seam is
the pure render **model**.
### 7.2 Slicing plan
- **SLICE-1** `trackChangesModel` (vscode-free): block diff + word refinement +
`markdown-it` render + atomic code/mermaid (INV-22/23) + vitest suite. Add
`markdown-it` + `diff` deps.
- **SLICE-2** Webview asset + build: `media/preview.ts` (receive HTML, run
mermaid, theme CSS), esbuild second entry, bundle `mermaid`; sealed CSP shell.
- **SLICE-3** `TrackChangesPreviewController` + F6 `onDidChangeBaseline` event +
`package.json` command/keybinding + `CowritingApi` handle + test seam
(`isOpen`/`getLastModel`); markdown guard; debounced live update.
- **SLICE-4** Host E2E (open/live/epoch/pin/non-markdown per §6.8) +
`docs/MANUAL-SMOKE-F7.md` + README F7 section.
E2E are first-class plan tasks (handbook §9/§4); this app's required tier is host
E2E (the F2F6 precedent).
### 7.3 Rollout / launch plan
Non-shippable (no marketplace publish). "Done" = #21 acceptance: a rendered
track-changes preview opens beside a markdown doc, marks changes-since-baseline
(prose ins/del + atomic code/mermaid "changed" badge), updates live and on epoch
change; unit + host E2E green; live smoke performed once. #22 (intra-diagram
mermaid diff) remains open.
### 7.4 Risks & mitigations
| Risk | Mitigation |
| --- | --- |
| Rendering a *diff* of markdown breaks block structure (half a fence, broken list) | Block-level diff with **atomic** code/mermaid (INV-23); render per-block; malformed → error chip |
| mermaid bundle size / load | Webview-only bundle, never the host bundle; lazy `mermaid.run()` after HTML swap |
| Webview security | Sealed CSP, nonce, local-only assets (INV-21); no network/LLM |
| Live re-render cost on large docs | Debounce + optional idle cap; pure O(document) model |
| Scope creep into intra-diagram mermaid diff | Explicitly deferred to #22; v1 is the whole-diagram badge |
| Two change-views (F6 + F7) confuse | Distinct commands + titles; F7 markdown-only, F6 any-file; README explains when to use which |
---
## 8. Traceability matrix
| Requirement (#21) | Use case | Design | Slice |
| --- | --- | --- | --- |
| Rendered preview beside the editable source | PUC-1 | §6.2, §5 | SLICE-3 |
| Marks what changed since the F6 baseline | PUC-2/3 | INV-18 reuse, §6.2 | SLICE-1 |
| Full mermaid support | PUC-5 | §6.2, §6.4 | SLICE-1/2 |
| Mermaid change = whole-diagram badge (intra-diagram → #22) | PUC-5 | INV-23 | SLICE-1 |
| Prose additions/deletions inline | PUC-2 | §6.2 word refinement | SLICE-1 |
| Live update on edit + epoch change | PUC-2/3/4 | §6.5 | SLICE-3 |
| Pure read-only; no sidecar/contract impact | — | INV-20, §6.3 | all |
| Sealed webview, no network/LLM, local mermaid | — | INV-21 | SLICE-2 |
| Unit + host E2E, no LLM in CI | — | §6.8 | SLICE-1/4 |
## 9. Open Questions & Decisions log
- **RESOLVED (session 0019 ideation, recorded in #21):** render surface =
custom webview; libraries = `markdown-it` + `mermaid` + `diff` (no
extension-deps, no CDN); layer split = host renders/diffs, webview runs
mermaid; diff granularity = block-level + prose word-refinement, code/mermaid
atomic; mermaid v1 = whole-diagram "changed" badge; F7 **coexists** with F6.
- **RESOLVED (this spec):** open command `cowriting.showTrackChangesPreview`
(`ctrl+alt+r`, markdown-only); test seam = pure model + `getLastModel`
(webview DOM not E2E-asserted); additive F6 `onDidChangeBaseline` event.
- **OPEN → later:** intra-diagram mermaid diffing (**#22**); preview→source
scroll-sync; non-markdown rendered views; per-author coloring in the preview;
whether F7 should eventually subsume F6's prose path.
- **Deferred decisions (autonomous calls for operator review):** **F7 coexists
with F6** rather than replacing it (markdown-only vs any-file); the `ctrl+alt+r`
keybinding; webview DOM/mermaid rendering verified by manual smoke rather than
automated E2E (sealed-sandbox constraint). All cheap to revisit.
## 10. Glossary & References
- **Track-changes preview** — a rendered (not raw) view of the markdown document
marking what changed since the F6 baseline. **Baseline / epoch / advance / pin**
— as F6 (`coauthoring-diff-view.md`). **Atomic block** — a code or mermaid
fence diffed whole, never word-refined (INV-23). **Changed badge** — the
whole-diagram mark on a changed/added mermaid (or code) block; intra-diagram
diffing is #22.
- Feature #21 (F7) · deferred #22 · Epic #1 · F6 #17 (PR #18) + #19 (PR #20) ·
parent specs `coauthoring-diff-view.md`, `coauthoring-inner-loop.md`,
`coauthoring-attribution.md`, `coauthoring-propose-accept.md` · lineage
`ben.stull/rfc-app#48`.