Files
vscode-cowriting-plugin-con…/specs/coauthoring-rendered-preview.md
T

472 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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`.