diff --git a/specs/coauthoring-feasibility-spike.md b/specs/coauthoring-feasibility-spike.md
index ef9f72c..bc89fba 100644
--- a/specs/coauthoring-feasibility-spike.md
+++ b/specs/coauthoring-feasibility-spike.md
@@ -9,10 +9,27 @@ status: draft
| **Type** | Throwaway discovery **prototype** (handbook §5.3) — code is disposable |
| **Goal** | Prove the API-dependent design assumptions on the **real** VS Code surfaces, cheaply, **before** the full build — so post-ship iteration is polish, not design |
| **Non-goal** | Production code, real Claude/SDK wiring (except E4), persistence durability, polish, breadth |
-| **Status** | `draft` |
+| **Status** | `draft` — **in progress: E1/E3/E5 done, E2/E4 remain** |
---
+## Status (2026-07-01, session 0063)
+
+Three of the five experiments are built and passed in `vscode-cowriting-prototype`:
+
+- **E1 — PASS with pivot.** VS Code does **not** render extension CodeLens inside
+ a diff editor; per-hunk CodeLens works in a normal editor, and tweak-then-Keep
+ attributes machine words vs. writer edits correctly. Folded back into the
+ Solution Design as **D18** (the review surface is the live buffer itself,
+ in-buffer pending changes — see `coauthoring-native-surfaces.md` v0.2.0).
+- **E3 — PASS.** The `markdown-it` contribution renders author-colored marks +
+ deletions in the built-in preview, theme-aware, with a working toggle.
+- **E5 — PASS.** Title-bar + status-bar entry points read as obvious without the
+ Source Control pane.
+- **E2 (baseline router: QuickDiff vs git `HEAD` + snapshot) and E4 (comment →
+ reply → offer → proposal) are not built yet** — now the riskiest unvalidated
+ assumptions and the spike's remaining work.
+
## Why this exists
Static mockups validated the *look* and *state coverage*; they prove **nothing**
diff --git a/specs/coauthoring-native-surfaces.md b/specs/coauthoring-native-surfaces.md
index 1e8882c..0ba89d9 100644
--- a/specs/coauthoring-native-surfaces.md
+++ b/specs/coauthoring-native-surfaces.md
@@ -8,14 +8,15 @@ status: draft
| **Author(s)** | Ben Stull (with Claude) |
| **Reviewers / approvers** | Ben Stull |
| **Status** | `draft` |
-| **Version** | v0.1.0 |
-| **Source artifacts** | North-star architecture spec (design-only) for human↔machine markdown coediting. Reference: VS Code's native review surfaces (SCM/QuickDiff, diff editor, multi-diff editor, Markdown preview, Comments). Supersedes: none. |
+| **Version** | v0.2.0 |
+| **Source artifacts** | North-star architecture spec for human↔machine markdown coediting. Reference: VS Code's native review surfaces (SCM/QuickDiff, diff editor, Markdown preview, Comments). Inputs: the shipped plugin (F1–F12), the rung-2 feasibility spike (`vscode-cowriting-prototype`), `coauthoring-feasibility-spike.md`. Supersedes: none. |
**Change log**
| Date | Version | Change | By |
| --- | --- | --- | --- |
| 2026-06-27 | v0.1.0 | Initial draft — brainstorming session 0062 | Ben Stull + Claude |
+| 2026-07-01 | v0.2.0 | Session 0063 recommendation folded in: review surface is **in-buffer pending changes + per-hunk CodeLens** (supersedes the multi-diff Keep/Undo surface — D18, resolves Q2/Q7, amends INV-5/INV-12); **Q1 resolved — evolve the shipped extension in place** (D17); **comments-first ask surface** (D19); D11 updated to shipped reality (D21); rung-2 spike findings (E1/E3/E5) recorded | Ben Stull + Claude |
---
@@ -110,9 +111,9 @@ between blind trust and tedious manual reconciliation.
staying in control — seeing what changed, accepting/rejecting changes
individually, knowing authorship, reading the rendered result, and following the
machine's progress — entirely within their editor.
-- **Out of scope:** committing a specific product packaging or release; multi-file /
- whole-project machine orchestration; a server or cloud backend. This is a
- **design-only north star** (see §2): packaging and delivery decisions are deferred.
+- **Out of scope:** marketplace release; multi-file / whole-project machine
+ orchestration; a server or cloud backend. (v0.1.0 also deferred the packaging
+ decision; v0.2.0 resolves it — the shipped extension evolves in place, D17/§13-Q1.)
- **Non-goals:** replacing the writer's judgment (the machine proposes; the human
disposes); non-markdown document formats in v1; a real-time multi-human
collaboration server.
@@ -122,8 +123,9 @@ between blind trust and tedious manual reconciliation.
- **Assumptions:** the writer works in an editor with native diff, source-control,
multi-change review, Comments, and Markdown-preview surfaces (VS Code is the
target); a capable machine coauthor is available to run locally.
-- **Constraints:** serverless and git-native (no external store); the writer's
- document is never mutated without their explicit acceptance; no secret bytes in
+- **Constraints:** serverless and git-native (no external store); nothing the
+ machine proposes ever *lands* without the writer's explicit acceptance — a
+ pending suggestion is always revertible in one gesture; no secret bytes in
any persisted artifact.
- **Dependencies:** a local machine-coauthor runtime; the editor's stable extension
APIs for the surfaces above; the built-in **Git extension API** (to read `HEAD` for
@@ -154,13 +156,15 @@ Scenario: BUC-2 — keep or reject, change by change
Given the machine has proposed changes to my document
When I review them
Then I can keep or discard each change individually, or all at once
- And nothing the machine proposed alters my document until I keep it
+ And nothing the machine proposed lands in my document until I keep it —
+ discarding a change restores my text exactly as it was
```
-- **BUC-2 acceptance criteria:** no proposed change alters the document until the
- writer keeps it; each change is independently keepable/discardable; the writer can
- **edit a proposed change before keeping it**, and those edits are attributed to the
- writer; keep-all and reject-all exist.
+- **BUC-2 acceptance criteria:** no proposed change *settles* until the writer
+ keeps it, and rejecting one restores the prior text exactly; each change is
+ independently keepable/discardable; the writer can **edit a proposed change
+ before keeping it**, and those edits are attributed to the writer; keep-all and
+ reject-all exist.
**BUC-3 — As a writer, I can tell who wrote which parts of my document and read a
clean rendered version, so that I can judge the result as finished prose.**
@@ -227,7 +231,7 @@ already know:
| What the writer needs | Native surface that delivers it | What it gives them, for free |
| --- | --- | --- |
| See what changed (BUC-1) | Source-control gutter change bars + the **native diff editor** against a virtual *baseline* document | The exact diff experience they use for everything else: inline/side-by-side, navigation, accessibility |
-| Keep/reject — and **tweak** — changes (BUC-2) | The **multi-diff review editor** with per-change **Keep/Undo** and a **writable proposed side** | One native surface to sweep all of the machine's changes — editing the proposal in place before keeping or discarding each (INV-12) |
+| Keep/reject — and **tweak** — changes (BUC-2) | **Pending changes in the live editor** — proposed edits land decorated-but-unsettled in the buffer, with per-hunk **✓ Keep / ✗ Reject** CodeLens (D18) | The paradigm the platform's own AI edit sessions use: review the change *in the document*, tweak it by just typing (INV-12), revert it with one gesture (INV-5) |
| Authorship + rendered result (BUC-3) | The **built-in Markdown preview**, extended via a `markdown-it` plugin to inject authorship + change marks | Native rendering, scroll-sync, theming, and cooperation with the writer's other Markdown extensions |
| Discuss a passage — and have the machine respond (BUC-5) | The **Comments API** — threaded comments where, on a coedited document, the machine replies in-thread and offers an edit | The familiar review-comment experience, in the gutter, that can turn a remark into a proposed change |
| Follow + stop the machine (BUC-4) | A progress **notification** + a shared **OutputChannel**, with Cancel | Live, stoppable, legible machine work |
@@ -252,12 +256,16 @@ of building review machinery without getting the polish, and asks the writer to
learn a foreign surface. **Delivering through the editor's native surfaces** gets
that behavior for free, makes coediting feel built-in, and shrinks what must be
built and maintained to a thin layer of wiring over proven cores. The cost is
-accepting the platform's affordances where they differ from a bespoke one, plus a
-small stable-API boundary on per-change Keep/Undo (§6.10, §13-Q2).
+accepting the platform's affordances where they differ from a bespoke one. (The
+v0.1.0 stable-API worry on per-change Keep/Undo resolved the other way: the spike
+showed CodeLens does not render in a diff editor at all, which — together with the
+shipped F12 experience — moved the review surface into the live buffer, D18.)
-**Decision posture:** this is a **design-only north star**. It commits to the
-architecture and the native-surface mapping; it does **not** commit to a product
-packaging or a delivery sequence — those are taken later (§7.1, §13-Q1).
+**Decision posture:** this is a **north star with a committed delivery vehicle**.
+It commits to the architecture and the native-surface mapping, and (as of v0.2.0,
+D17) to the packaging: **the shipped extension evolves in place onto these
+surfaces** — its pure cores are retained, its bespoke webview surfaces sunset
+(§6.10, §7.1).
**Solution scope.** *In:* the architecture, surface mapping, data model, contracts,
and per-use-case design for native, in-editor human↔machine markdown coediting,
@@ -292,12 +300,16 @@ Scenario: PUC-1 — see the diff against the baseline checkpoint (realizes BUC-1
Scenario: PUC-2 — keep/reject the machine's proposed changes (realizes BUC-2)
Given I ask the machine to revise the document (or a selection)
When the turn completes
- Then the proposed changes open in a multi-diff review (baseline ⟷ proposed)
- And the proposed side is editable, so I can tweak the machine's wording before keeping it
- And each change offers Keep / Undo (Accept / Reject)
- And toolbar actions offer Keep all / Reject all
- And my document is unchanged until I keep a change
+ Then each proposed change appears in my editor as a pending, decorated change
+ (proposed words marked by author color, replaced words struck) — visible in
+ place but not settled
+ And each change carries ✓ Keep / ✗ Reject actions (CodeLens above the change)
+ And Keep all / Reject all are available (CodeLens menu, title bar, status bar)
+ And I can tweak the proposed wording by simply typing in the pending text
+ And rejecting a change restores my text exactly as it was
And on keep, the machine's words are attributed to the machine and my tweaks to me
+ And "Review Changes" opens the native diff (baseline ⟷ live) to sweep everything
+ at once
```
```gherkin
@@ -374,8 +386,11 @@ panel to wireframe. Described in text (source of truth):
the document is coedited, title-bar buttons (**Review Changes**, **Ask Claude**,
**Toggle Annotations**, and in snapshot mode **Mark Changes as Reviewed**) and the
`✦ Coediting · N changes` status-bar item are the everyday entry points — visible
- without going anywhere. The review also **auto-opens** on new proposals, and the
- live-progress notification carries a *Review* action.
+ without going anywhere. **Ask Claude** opens a comment thread (on the selection if
+ there is one, else on the whole document) — the comment box *is* the ask input
+ (D19). New proposals land as pending changes **in the buffer in front of the
+ writer** (no navigation at all), and the live-progress notification carries a
+ *Review* action.
- **Source Control gutter & viewlet (optional, for git users).** A dedicated
source-control entry, *"Cowriting,"* coexists with Git: its quick-diff provider
draws the familiar change bars in the editor gutter against the baseline checkpoint,
@@ -384,16 +399,26 @@ panel to wireframe. Described in text (source of truth):
— never the required path (INV-13).
- **Native diff editor.** The writer's normal diff (`baseline ⟷ live`), with the
platform's inline/side-by-side toggle, navigation, and accessibility.
-- **Multi-diff review editor.** When the machine proposes edits, a multi-diff editor
- opens showing `baseline ⟷ proposed`, each change carrying **Keep/Undo**; a
- toolbar carries **Keep all / Reject all**. This is the accept/reject surface.
+- **Pending changes in the editor (the accept/reject surface, D18).** When the
+ machine proposes edits, each change lands **in the live buffer** as a pending,
+ decorated change — proposed words marked in the machine's author color, replaced
+ words shown struck — with per-hunk **✓ Keep / ✗ Reject** CodeLens (and a
+ Keep-all/Reject-all menu). The writer tweaks a pending change by **typing in it**;
+ Reject restores the prior text exactly. This is the same review-in-the-document
+ paradigm the platform's own AI edit sessions use, so it reads as native even
+ though the decorations are the extension's. (The v0.1.0 multi-diff Keep/Undo
+ surface is superseded — the spike showed CodeLens cannot render in a diff editor,
+ §7.1/D18.)
- **Built-in Markdown preview.** The writer's normal "Open Preview to the Side";
with annotations on, authorship colors and change marks appear *inside* it. A
command/setting toggles annotations.
-- **Comments gutter.** Region threads via the native Comments API (create on
- selection, reply, resolve). On a coedited document, the machine replies in-thread
+- **Comments gutter — the primary ask surface (D19).** Region threads via the
+ native Comments API (create on selection, reply, resolve). The comment box is the
+ one native multi-line input the editor offers, so it is **how the writer asks for
+ anything**: an imperative ask ("tighten this paragraph") and a discussion remark
+ are the same gesture. On a coedited document, the machine replies in-thread
and offers an edit (BUC-5 / PUC-8); the offer, when accepted, becomes a pending
- proposed change reviewed in the multi-diff. A comment can be **started from the
+ proposed change reviewed in the buffer (PUC-2). A comment can be **started from the
rendered preview** (select text there → "Comment on this"): the selection is mapped
back to the source range and a **native** thread is created — the preview shows a
💬 marker that deep-links to it, but the thread itself renders in the gutter /
@@ -419,7 +444,7 @@ panel to wireframe. Described in text (source of truth):
### 5.2 Screen: native surfaces (serves PUC-1…PUC-6)
Covered in the §5 text above. No custom screen; states map to platform states (diff
-editor empty/identical, multi-diff empty, preview loading/error) plus the
+editor empty/identical, no pending changes, preview loading/error) plus the
coauthoring **displaced** state surfaced in the thread / proposed-change listing.
## 6. Technical Design
@@ -442,9 +467,15 @@ coauthoring **displaced** state surfaced in the thread / proposed-change listing
`threads` share one envelope; **unknown fields are preserved** on read/merge, so
the artifact round-trips to an outer review forge (e.g. Gitea/GitHub) without
loss.
-- **INV-5 — proposed changes are pending until kept:** a machine-proposed edit does
- **not** mutate the document until the writer keeps it (Keep in the multi-diff, or
- Accept). The machine proposes; the human disposes.
+- **INV-5 — proposed changes are pending until kept:** a machine-proposed edit
+ never **settles** until the writer keeps it. It appears in the live buffer as a
+ visibly pending, decorated change (D18) — but it is fully revertible: **Reject
+ restores the prior text exactly**, the model retains each proposal's original,
+ and nothing is attributed as landed machine work until Keep. The machine
+ proposes; the human disposes. *(Amended in v0.2.0: v0.1.0 kept the buffer
+ byte-untouched until Keep; the guarantee is now "nothing lands irrevocably,"
+ which is what the writer actually needs — and what makes tweak-by-typing
+ possible, INV-12.)*
- **INV-6 — pure cores stay vscode-free:** attribution, anchoring, merge, diffing,
and the progress reducer are pure modules with no `vscode` import, unit-tested in
isolation; controllers are the only `vscode`-aware layer, so surfaces can change
@@ -486,24 +517,28 @@ coauthoring **displaced** state surfaced in the thread / proposed-change listing
remains, so re-entering restores state.
- **INV-11 — the native diff is author-blind:** the native diff editor shows *what*
changed against the baseline, not *who* changed it (the diff API has no per-author
- color scoping — §6.7). Authorship (human vs. machine) is shown on a surface the
- extension renders: the annotated preview (and optionally a decoration-based
- track-changes editor), never by recoloring the native diff.
+ color scoping — §6.7). Authorship (human vs. machine) is shown on surfaces the
+ extension renders: the annotated preview and the in-editor author-colored
+ track-changes decorations (D21), never by recoloring the native diff.
- **INV-13 — no surface requires the Source Control pane:** the primary,
always-visible entry points for a coedited document are the **editor title-bar
- actions** and a **status-bar item** (both gated on `cowriting.isCoediting`), and the
- review **auto-opens** on new machine proposals. The SCM viewlet entry and the gutter
+ actions** and a **status-bar item** (both gated on `cowriting.isCoediting`), and
+ new machine proposals land **in the buffer in front of the writer** (D18) — the
+ review requires no navigation at all. The SCM viewlet entry and the gutter
dirty-diff are additional native affordances, not the required path — a writer
unfamiliar with git / Source Control never has to find them to coedit, review, or
settle a checkpoint.
-- **INV-12 — proposed changes are editable before they are kept:** the review
- surface presents the machine's proposal on a **writable** side, so the writer can
- tweak it in place before keeping it (the common "close, but a few human edits"
- case). The live document stays untouched until Keep (INV-5 still holds); on Keep,
- the words the machine proposed are attributed to the machine and the writer's
- tweaks to the writer — so authorship is correct **at the moment the change is
- made**, never reconstructed afterward. Editing happens at review time; once kept,
- the text is just text (no lingering "trapped proposal" state to untangle).
+- **INV-12 — proposed changes are editable before they are kept:** a pending
+ change sits in the live buffer, so the writer tweaks it by **typing directly in
+ it** (the common "close, but a few human edits" case) — no separate proposal
+ document, no mode. Nothing settles until Keep (INV-5); on Keep, the words the
+ machine proposed are attributed to the machine and the writer's tweaks to the
+ writer — by diffing *final ↔ the machine's original proposal ↔ baseline* — so
+ authorship is correct **at the moment the change is made**, never reconstructed
+ afterward. Once kept, the text is just text (no lingering "trapped proposal"
+ state to untangle). A pending change re-anchors around the writer's edits; one
+ displaced by heavy rewriting collapses to a whole-proposal Keep/Reject (D20,
+ INV-1 discipline).
### 6.2 High-level architecture
@@ -523,7 +558,7 @@ flowchart LR
subgraph Ctrls["Thin controllers (vscode-aware)"]
SC[SourceControl + QuickDiffProvider]
BP[Baseline provider
TextDocumentContentProvider]
- PR[Review controller
multi-diff Keep/Undo]
+ PR[Pending-change controller
in-buffer decorations + CodeLens]
PV[Preview extension
extendMarkdownIt]
TC[ThreadController
Comments API]
LP[liveProgress relay]
@@ -532,7 +567,7 @@ flowchart LR
subgraph Native["Native VS Code surfaces"]
GUT[Gutter dirty-diff + SCM viewlet]
DIFF[Native diff editor]
- MDIFF[Multi-diff review + Keep/Undo]
+ MDIFF[Live editor buffer
pending changes + CodeLens]
MDPREV[Built-in Markdown preview]
COMM[Comments gutter/peek]
NOTE[Notification + OutputChannel]
@@ -555,9 +590,10 @@ flowchart LR
- **Baseline provider** — a `TextDocumentContentProvider` for the
`cowriting-baseline:` scheme; serves the pinned baseline text for any document URI
from the out-of-repo baseline store (INV-7).
-- **Review controller** — turns the machine's emitted edits into pending proposed
- changes, opens the multi-diff review, and applies Keep/Undo by delegating to the
- proposed-change model; honors INV-5.
+- **Pending-change controller** — turns the machine's emitted edits into pending
+ proposed changes, lands them in the live buffer as decorated pending text
+ (retaining each proposal's original for exact revert), and renders per-hunk
+ Keep/Reject CodeLens; Keep settles + attributes, Reject restores; honors INV-5.
- **Preview extension** — contributes `markdown-it` so the pure `renderAnnotations`
transform runs inside the built-in preview; owns no webview.
- **Sidecar router + store** — persistence: a repo-committed sidecar when the
@@ -608,21 +644,23 @@ state (INV-7), not durable coauthoring metadata.
(drives gutter bars). Registered on the "Cowriting" `SourceControl`.
- **Machine-edit seam** — the single boundary where machine text enters the model:
`proposeAgentEdit(docUri, range, newText, provenance)` → produces **pending**
- `proposals[]` entries (never mutates the doc — INV-5). Block/word granularity via
+ `proposals[]` entries; nothing settles until Keep (INV-5). Block/word granularity via
the pure diff engine (`diffToBlockHunks` / `wordEditHunks`): a block (paragraph,
header, list item, fenced code) is the *decision* unit a writer keeps or rejects;
the word diff inside it is the *attribution* unit, so keeping a block attributes
only the words that actually changed.
-- **Review controller** — `openReview(docUri)` opens the multi-diff
- (`baseline ⟷ proposed`); `keep(proposalId)` = Accept (applies the edit, attributes
- changed words); `undo(proposalId)` = Reject (drops it); `keepAll()` / `undoAll()`
- = Keep/Reject all (apply descending, skip + report any displaced change). The
- **proposed side is a writable scratch document** (untitled / in-memory) seeded with
- the machine's text (INV-12); the left side (baseline) is read-only. `keep` diffs
- the **final** (possibly writer-tweaked) proposal against the baseline, applies it
- to the live document, and attributes machine-proposed words to the machine and
- writer tweaks to the writer by comparing *final ↔ the machine's original proposal*.
- The live document is untouched until `keep` (INV-5).
+- **Pending-change controller** — on new proposals, lands each **optimistically in
+ the live buffer** as decorated pending text (the model retains the proposal's
+ `original` for exact revert) and renders per-hunk **✓ Keep / ✗ Reject** CodeLens
+ (D18). `keep(proposalId)` = Accept: settles the change and attributes it —
+ machine-proposed words to the machine, any writer tweaks (typed directly into the
+ pending text, INV-12) to the writer, by comparing *final ↔ the machine's original
+ proposal ↔ baseline*. `reject(proposalId)` restores the retained original
+ exactly. `keepAll()` / `rejectAll()` apply descending, skip + report any
+ displaced change. Pending changes re-anchor around writer edits; a displaced one
+ collapses to whole-proposal Keep/Reject (D20). `openReview(docUri)` opens the
+ **native diff editor** (`baseline ⟷ live`) as the sweep-everything reference
+ view — read-only, author-blind (INV-11); decisions stay in the buffer.
- **Preview extension** — `extendMarkdownIt(md) → md.use(cowritingPlugin)`; the
plugin invokes the pure `renderAnnotations(tokens, attributions, proposals,
options)` transform, gated by the `cowriting.annotations` setting/command.
@@ -637,8 +675,12 @@ state (INV-7), not durable coauthoring metadata.
`isCoediting(docUri)` before attaching a surface: the QuickDiffProvider returns no
original for non-coedited docs, the comment-response controller ignores them, and
the annotation/edit commands are disabled (`when` clause `cowriting.isCoediting`).
-- **ThreadController + comment-response** — create/reply/resolve via the Comments
- API. On a coedited document only, a new comment fires `runEditTurn` to **reply in
+- **ThreadController + comment-response — the primary ask surface (D19)** —
+ create/reply/resolve via the Comments API. The comment box is the editor's one
+ native multi-line input, so **asking is commenting**: the title-bar **Ask
+ Claude** action just creates a thread (on the selection, else whole-document) and
+ focuses its input; no bespoke input surface exists. On a coedited document only,
+ a new comment fires `runEditTurn` to **reply in
the thread** and, when the remark implies a change, **offer an edit**; an accepted
offer routes through the machine-edit seam to a **pending** proposed change
(INV-5) — the comment never edits the document directly. The reply is posted as a
@@ -677,34 +719,37 @@ sequenceDiagram
```mermaid
sequenceDiagram
actor U as Writer
- participant R as Review ctrl
+ participant R as Pending-change ctrl
participant S as Machine runtime
- participant M as Multi-diff editor
+ participant E as Live editor buffer
U->>R: Ask the machine to revise
R->>S: runEditTurn(prompt, {onProgress, signal, env})
S-->>R: proposed text
R->>R: proposeAgentEdit → pending proposals[] (INV-5)
- R->>M: openReview (baseline ⟷ proposed)
- U->>M: Keep / Undo per change · Keep all / Reject all
- M->>R: keep(p)/undo(p)
- R-->>U: document updated only on Keep; authorship recorded
+ R->>E: land pending changes (decorated, original retained)
+ U->>E: tweak by typing · ✓ Keep / ✗ Reject per change (CodeLens)
+ E->>R: keep(p)/reject(p) · keepAll()/rejectAll()
+ R-->>U: Keep settles + attributes · Reject restores the original exactly
```
-- **Implementation:** the machine never edits the document; it emits **pending**
- proposed changes (INV-5). The multi-diff editor is the review surface; Keep =
- Accept (apply + attribute changed words), Undo = Reject. Keep-all/reject-all apply
- descending and skip + report any displaced change. **Stable-API boundary:** the
- multi-diff editor is stable; per-change inline **Keep/Undo overlays** as some AI
- tools render them lean on evolving editor APIs — the **stable fallback** is
- per-hunk **CodeLens** Keep/Reject within the diff (§6.10, §13-Q2).
-- **Editable before keep (INV-12):** the proposal renders on the **writable** side of
- the review, so the writer can tweak the machine's wording ("close, but a few human
- edits") before keeping. `keep` attributes the machine's words and the writer's
- tweaks separately by diffing *final ↔ machine-original ↔ baseline*. Because editing
- happens on the proposal (a scratch doc), not the live document, INV-5 holds and
- authorship is correct at creation — the preview (Option 1, the authorship surface)
- then shows clean green/blue with no post-hoc untangling. *Open detail (§13-Q7):*
- how per-change Keep/Undo behaves once the writer has freely edited across hunks.
+- **Implementation:** the machine emits **pending** proposed changes; each lands in
+ the live buffer as a decorated pending change with its original retained, and
+ nothing settles until Keep (INV-5). Per-hunk **CodeLens** carries ✓ Keep /
+ ✗ Reject (with a Keep-all/Reject-all menu); keep-all/reject-all apply descending
+ and skip + report any displaced change. The **native diff editor**
+ (`baseline ⟷ live`) is the sweep-everything reference view; the buffer is where
+ decisions happen. **Why in-buffer (D18):** the rung-2 spike verified VS Code does
+ **not** render extension CodeLens inside a diff editor, ruling out the v0.1.0
+ multi-diff Keep/Undo surface; the shipped F12 experience proved the in-buffer
+ model end-to-end, and it matches the platform's own AI edit-session paradigm.
+- **Editable before keep (INV-12):** the pending change sits in the live buffer, so
+ the writer tweaks it by typing directly in it ("close, but a few human edits")
+ before keeping. `keep` attributes the machine's words and the writer's tweaks
+ separately by diffing *final ↔ machine-original ↔ baseline*, so authorship is
+ correct at creation — the preview and the in-editor track-changes then show clean
+ green/blue with no post-hoc untangling. Pending changes **re-anchor** around
+ writer edits; one displaced by heavy rewriting collapses to a whole-proposal
+ Keep/Reject (D20, resolving the old Q7).
#### Always-visible entry points (no Source Control navigation) — INV-13
@@ -713,8 +758,9 @@ sequenceDiagram
it exists). When `cowriting.isCoediting` is set, the actions surface where the
writer already is: **editor title-bar buttons** (Review Changes, Ask Claude, Toggle
Annotations, and in snapshot mode Mark Changes as Reviewed) and a **status-bar
- item** (`✦ Coediting · N changes`, click → Review Changes). The multi-diff review
- **auto-opens** on new proposals, and the live-progress notification carries a
+ item** (`✦ Coediting · N changes`, click → Review Changes). New proposals land
+ **in the buffer in front of the writer** (D18 — the review comes to them), and
+ the live-progress notification carries a
*Review* action. The SCM viewlet entry and the gutter dirty-diff remain as native
extras for git-literate users, never the required path.
@@ -774,7 +820,7 @@ sequenceDiagram
T-->>U: machine reply posted in thread (onBehalfOf provenance)
U->>T: accept the offered edit
T->>R: proposeAgentEdit → pending proposal (INV-5)
- R-->>U: review in multi-diff (PUC-2)
+ R-->>U: pending change lands in the buffer (PUC-2)
```
- **Implementation:** gated on `isCoediting` (INV-10) — inert otherwise. The turn
@@ -809,7 +855,7 @@ sequenceDiagram
- **Observability:** the OutputChannel stream (PUC-5) is the user-facing turn log;
no telemetry service (a local extension — §9).
- **Accessibility:** inherited from the native surfaces — the diff editor,
- multi-diff, Markdown preview, and Comments all carry the editor's accessibility.
+ CodeLens, Markdown preview, and Comments all carry the editor's accessibility.
This is a primary *reason* for the native-first choice.
### 6.7 Key decisions & alternatives considered
@@ -817,20 +863,21 @@ sequenceDiagram
| Decision | Chosen | Alternatives considered | Why chosen |
| --- | --- | --- | --- |
| Diff surface | `QuickDiffProvider` + native diff editor against a `cowriting-baseline:` virtual doc | bespoke webview diff; decoration-only inline diff | Native gutter + diff editor for free: theming, inline/side toggle, a11y, navigation; zero custom rendering (INV-3) |
-| Accept/reject surface | **Multi-diff editor with Keep/Undo**, backed by the proposed-change model | source-control stage/unstage; in-editor CodeLens Keep/Reject; bespoke webview ✓/✗ | Closest to a modern "review the machine's edits" feel; reviews all changes in one native surface. Stable fallback: per-hunk CodeLens (§6.10) |
+| Accept/reject surface (v0.2.0, D18 — supersedes the v0.1.0 multi-diff choice) | **Pending changes in the live buffer** with per-hunk **✓ Keep / ✗ Reject CodeLens**, backed by the proposed-change model; native diff editor as the read-only sweep view | multi-diff editor with Keep/Undo (v0.1.0 choice — **ruled out**: the spike verified CodeLens does not render in a diff editor, and inline overlays lean on evolving APIs); a separate writable proposal document (spike E1 pivot — works, but puts an artifact between the writer and their document); source-control stage/unstage; bespoke webview ✓/✗ | The platform's own AI edit-session paradigm: review the change in the document, tweak by typing, revert in one gesture. Shipped and proven end-to-end (F12); all-stable APIs (decorations + CodeLens) |
| Rendered preview | **Extend the built-in Markdown preview** (`markdown-it` contribution) | bespoke sealed webview; both surfaces | Native scroll-sync, theming, refresh, and cooperation with the writer's other Markdown extensions; no webview to own (INV-3) |
| Baseline source | follows the file's home (INV-7): **git `HEAD`** where tracked (resets on commit, storage-free), a **snapshot** (set on entry, re-pinnable; global storage / in-memory) where there is no commit to anchor to — outside any repo, untracked, or untitled | always a stored snapshot; always defer to git (breaks outside a repo) | Git-tracked files get the native "uncommitted work, reset on commit" diff for free; `~/Downloads` / untitled still work via the snapshot fallback. Mirrors the in-repo/out-of-repo `SidecarRouter` |
| Persistence envelope | one forward-compatible, unknown-field-preserving sidecar (INV-4) | a bespoke per-feature format; preview-only ephemeral state | Round-trips to an outer review forge; survives reload; one shared `anchors`/`provenance` primitive |
| Core/controller split | pure vscode-free cores + thin controllers | UI-coupled logic | Unit-testable cores, swappable surfaces; the surface can change without touching the logic (INV-6) |
| Decision vs attribution unit | block = decision unit (keep/reject); word = attribution unit | per-word proposals (too granular to decide on); per-file (all-or-nothing) | The writer decides at a readable grain (a paragraph) while authorship stays word-precise |
-| Authorship surface | **Option 1** — the annotated preview is the authorship view (green/blue track-changes incl. deletions); native diff stays author-blind | Option 2 (in-editor decorations); Option 3 (owned author-colored track-changes diff that replaces native diff) | Preview shows author-colored changes for near-zero maintenance; editable proposals (INV-12) keep authorship clean *at creation*, so in-editor / post-hoc authorship rendering is unnecessary. Opts 2/3 deferred |
-| Editing proposals | the proposed side of the review is **writable**; pre-Keep tweaks are absorbed and attributed (machine vs. writer) at Keep (INV-12) | machine proposal read-only (tweak only after accepting) | Most revisions are "close, but a few human edits"; editing at review time keeps authorship clean at creation and avoids messy post-acceptance untangling |
+| Authorship surface (v0.2.0, D21 — updates D11 to shipped reality) | the annotated preview **and** the in-editor author-colored track-changes decorations (both shipped); native diff stays author-blind (INV-11) | preview-only (v0.1.0 D11 — overtaken: the in-editor decorations shipped 2026-06-27 and earn their keep); Option 3 (owned author-colored track-changes diff replacing native diff) | Both surfaces ride the same pure render/attribution cores; the writer gets authorship where they read (preview) and where they write (editor). Opt 3 still out |
+| Editing proposals | pending changes are **tweaked by typing directly in the buffer**; pre-Keep tweaks are absorbed and attributed (machine vs. writer) at Keep (INV-12) | a separate writable proposal document (v0.1.0); machine proposal read-only (tweak only after accepting) | Most revisions are "close, but a few human edits"; editing in place, at review time, keeps authorship clean at creation with no extra artifact and no messy post-acceptance untangling |
+| Ask surface (v0.2.0, D19) | **comments-first** — the native comment thread input is the ask input; title-bar "Ask Claude" creates/focuses a thread (selection or whole-document) | the shipped bespoke split-pane input webview (sunset — one custom surface eliminated); single-line `InputBox` (too small for real asks) | The Comments API is the editor's one native multi-line input; ask-for-an-edit and remark-on-a-passage collapse into a single familiar gesture, and the machine's answer lands in the same thread (D8/D10) |
| Coediting activation | opt-in per document via "Coedit this Document with Claude"; every surface gated on the active coediting set (INV-10) | always-on for all markdown; opt-in per workspace/folder | Per-document opt-in is the only grain that avoids hijacking Comments/SCM/preview for the writer's other files while keeping single-file precision |
-| Change discovery (resolves Q3) | primary entry = **editor title bar + status-bar item + auto-open review**; the SCM viewlet + gutter are optional extras (INV-13) | rely on the Source Control pane / gutter as the entry point | Writers working outside git won't find (or know) the Source Control pane; always-visible title-bar / status-bar actions need no git familiarity |
+| Change discovery (resolves Q3) | primary entry = **editor title bar + status-bar item**, and proposals land **in the buffer in front of the writer** (D18); the SCM viewlet + gutter are optional extras (INV-13) | rely on the Source Control pane / gutter as the entry point | Writers working outside git won't find (or know) the Source Control pane; always-visible title-bar / status-bar actions need no git familiarity |
| Authorship in the diff | the native diff is **author-blind** (what changed); authorship is shown in the preview, optionally a decoration track-changes editor (INV-11) | recolor machine changes inside the native diff editor | **Not possible:** `diffEditor.*` colors are global theme tokens with no per-author scoping; `inlineEdit.*`/chat tokens differentiate accept-state not author; decorations over the diff pane composite over green/red and are unreliable (vanish on tab-switch). Verified against the VS Code API (§13-Q5) |
| Comment → machine response | on a coedited doc, a comment fires a turn that replies + offers an edit → pending proposal | comment is inert (human-only threads) | Turns the familiar review-comment gesture into a coediting action without a new surface; the opt-in gate (INV-10) keeps it from firing on other docs |
| Comments & the preview | comments are **native** (source gutter + Comments panel); the preview can *initiate* a comment (selection → source range via the `markdown-it` source map) and show a deep-linking 💬 marker, but cannot **host** the thread (it's a webview — verified) | render a bespoke comment widget inside the preview webview | Keeps comments on the native surface (no INV-3 exception, keeps the Comments panel + gutter); the writer can still comment on what they're reading and add whole-document comments (top-anchored) |
-| Scope | design-only north star (no packaging/migration commitment) | commit a packaging + delivery sequence now | Keeps the architecture decision separate from the delivery decision (§2, §7.1) |
+| Scope & packaging (v0.2.0, D17 — resolves Q1) | north star **with a committed vehicle**: the shipped extension **evolves in place** onto these surfaces (pure cores retained, webview surfaces sunset) | greenfield extension importing the cores as a library (re-derives wiring + E2E harness the shipped repo already has); stay design-only (stalls — §7.3) | ~60% of the shipped code (21 pure modules, ~3.5k LOC) *is* this design's cores already; evolve-in-place keeps tests, history, and continuous shipping |
### 6.8 Testing strategy
@@ -841,15 +888,18 @@ sequenceDiagram
markdown-it transform (token-in → annotated-token-out).
- **Integration / E2E (`@vscode/test-electron`):** drive the controllers in the
Extension Development Host — mark-reviewed → assert checkpoint reset; ask-to-edit
- → assert pending proposals + multi-diff opened; Keep/Undo → assert document
- mutation only on Keep + authorship recorded; toggle annotations → assert preview
+ → assert pending proposals landed decorated in the buffer with originals
+ retained; Keep/Reject → assert Keep settles + attributes and Reject restores the
+ original text exactly; toggle annotations → assert preview
HTML marks; thread create → re-anchor → displaced. Where the harness cannot drive
- a native surface's chrome directly (multi-diff Keep/Undo, Comments UI, preview
+ a native surface's chrome directly (CodeLens clicks, Comments UI, preview
DOM), assert controller/provider state and pure-transform output instead. **No
Playwright** — these are native surfaces, not a webview. E2E are first-class plan
- tasks (handbook §4 / §6.8).
+ tasks (handbook §4 / §6.8). The shipped repo's 19 E2E suites carry forward under
+ evolve-in-place (D17).
- **Named E2E scenarios this design must pass:** PUC-1 pin→diff; PUC-2
- propose→review→keep/undo→keep-all; PUC-3 annotate-toggle; PUC-4/6
+ propose→tweak→keep/reject→keep-all (incl. reject-restores-exactly); PUC-3
+ annotate-toggle; PUC-4/6
thread→reanchor→displaced; PUC-5 progress→cancel.
### 6.9 Failure modes, rollback & flags
@@ -860,63 +910,91 @@ sequenceDiagram
"start writing, or ask Claude to draft something" affordance; not an error. A
coedited document always has a baseline (set on entry, INV-7), so there is **no
"missing baseline" state** to handle.
-- **Multi-diff Keep/Undo API unavailable/changed** → fall back to per-hunk CodeLens
- Keep/Reject in the diff editor (§6.10); the proposed-change *model* is unchanged.
+- **Save while changes are pending** → the buffer (and on save, the disk file)
+ contains visibly-pending machine text; safe because every pending proposal's
+ original is retained in the sidecar and Reject restores it exactly after reload
+ (INV-5, re-anchored on load per INV-1/PUC-6).
- **Feature flags / settings:** `cowriting.annotations` (preview annotations on/off),
`cowriting.liveProgress.revealOutput`. The whole capability is additive — off by
absence of coediting mode for the document (INV-10).
### 6.10 Migration & compatibility
-**Greenfield — nothing to migrate.** This is a from-scratch design for in-editor
-human↔machine markdown coediting; it does not replace an existing data format or
-contract that must be carried.
+**Evolve in place (D17, resolves Q1).** The v0.1.0 draft called this greenfield;
+that undersold the shipped extension: **~60% of its code — 21 pure, vscode-free
+modules (~3.5k LOC) — already implements this design's cores** (attribution span
+algebra, fingerprint anchoring, the sidecar envelope + router + merge, the
+block/word diff engine, the render/annotation transform, the turn-progress
+reducer, the machine turn runner). The delivery vehicle is therefore the shipped
+extension itself, migrated **surface by surface**:
-- **Data migration:** none. The sidecar envelope is additive and
- unknown-field-preserving (INV-4) from v1, so future versions extend it without a
- break. The baseline (out-of-repo, disposable) needs no migration ever.
-- **Backwards compatibility:** N/A at v1 (nothing precedes it); the envelope's
- forward-compatibility rule (INV-4) protects future consumers.
-- **Cutover:** N/A (design-only; no packaging committed — §7.1).
-- **Stable-API boundary (recorded risk):** the native diff editor, SCM/QuickDiff,
- multi-diff editor, Markdown-preview `markdown-it` contribution, Comments, and
- decorations are **stable**. The per-change inline **Keep/Undo** overlay that some
- AI tools render uses **evolving** editor APIs; the design's stable contract is
- therefore "multi-diff review + per-hunk Keep/Reject CodeLens," with the inline
- overlay as a progressive enhancement where the API allows (§13-Q2).
+- **Retained as-is (the cores):** the 21 pure modules and their unit suites; the
+ sidecar contract (INV-4) and stores; the `@cline/sdk` turn runner + live
+ progress; the in-buffer pending-change experience (F12) and the in-editor
+ author-colored track-changes decorations — both already match this design (D18,
+ D21).
+- **Rewired (thin controllers):** baseline moves behind the router (git `HEAD` +
+ snapshot, INV-7 — today it is snapshot-only); a QuickDiffProvider +
+ `cowriting-baseline:` content provider replace nothing (net-new native surface);
+ the CoeditingRegistry opt-in gate (INV-10) replaces the current always-on-for-
+ markdown behavior; ThreadController gains the comment-response loop (D8/D10).
+- **Sunset (the bespoke webview stack, ~800 LOC):** the sealed review-panel
+ webview (`trackChangesPreview` + its client) is replaced by the extended
+ built-in Markdown preview (D3); the split-pane ask-input webview is replaced by
+ comments-first asking (D19). Each sunset lands only after its native replacement
+ is green.
+- **Data migration:** none needed — the sidecar envelope carries forward
+ (additive, unknown-field-preserving, INV-4). The baseline (out-of-repo,
+ disposable) needs no migration ever.
+- **Stable-API boundary:** every surface this design now stands on — diff editor,
+ SCM/QuickDiff, Markdown-preview `markdown-it` contribution, Comments,
+ decorations, CodeLens — is **stable API**. The one evolving-API worry from
+ v0.1.0 (per-change overlays in a multi-diff review) is gone with the in-buffer
+ decision (D18).
## 7. Delivery Plan
### 7.1 Approach / strategy
-This is a **design-only north star** — it ships no code. Delivery proceeds in **four
-de-risking rungs, riskiest-API-assumption first**, so any forced design change is
-discovered on throwaway code rather than after the full build (the explicit goal:
-post-ship iteration is *polish*, not *design*):
+Delivery proceeds in **four de-risking rungs, riskiest-API-assumption first**, so
+any forced design change is discovered on throwaway code rather than after the
+full build (the explicit goal: post-ship iteration is *polish*, not *design*).
+The vehicle is the **shipped extension, evolved in place** (D17/§6.10):
1. **Static mockups (done).** Validate visual design + state coverage. *(Already
surfaced and closed the empty-state, comments-from-preview, and discoverability
gaps.)*
-2. **Throwaway feasibility spike (next).** A disposable VS Code extension (a §5.3
- *prototype*; the machine **stubbed** with canned edits) whose only job is to prove
- the API-dependent assumptions on the real surfaces and return a **go/no-go per
- assumption — closing the build-time open questions (Q2, Q4, Q7) before any plan is
- written.** Riskiest first: (a) the multi-diff proposed side is **editable** with
- working **Keep/Undo** (INV-12); (b) the **baseline router** renders QuickDiff
- against a snapshot *and* git `HEAD`, going clean on commit / Mark-Reviewed (INV-7);
- (c) the **`markdown-it`** plugin injects authorship + deletions in the built-in
- preview with a toggle (PUC-3); (d) the **comment → reply → offer → proposal** loop
- (PUC-8); (e) the **title-bar + status-bar** discoverability reads as obvious
- (INV-13). Companion: `coauthoring-feasibility-spike.md`. Output: a findings report
- + any spec deltas folded back here.
-3. **Vertical slice.** One real end-to-end flow (enter → Ask Claude via the **real
- `@cline/sdk`** → real proposal → review → keep) on a real document, minimal
- breadth — proving the loop works for real once the surfaces are validated.
-4. **Full build.** The implementation plan decomposed from this (spike-revised) spec,
- built surface-by-surface in the validated order: **pure cores → diff surface**
- (highest-confidence native win) **→ preview extension → multi-diff accept/reject**
- (largest API-boundary risk, last, with the CodeLens fallback ready). One SPEC →
- one plan → one execution per increment.
+2. **Throwaway feasibility spike (partially done — 3 of 5 experiments).** A
+ disposable VS Code extension (`vscode-cowriting-prototype`; the machine
+ **stubbed** with canned edits) proving the API-dependent assumptions on the
+ real surfaces. Companion: `coauthoring-feasibility-spike.md`. **Findings so
+ far (2026-07-01):**
+ - **E1 (editable proposals + Keep/Undo) — PASS with pivot.** VS Code does
+ **not** render extension CodeLens inside a diff editor, ruling out the
+ multi-diff review surface; per-hunk CodeLens works in a normal editor, and
+ tweak-then-Keep attributes machine words vs. writer edits correctly. Folded
+ back as **D18** (with the shipped F12 experience): the review surface is the
+ live buffer itself.
+ - **E3 (preview authorship) — PASS.** The `markdown-it` contribution injects
+ author-colored marks + deletions into the built-in preview, theme-aware,
+ with a working toggle (D3 confirmed).
+ - **E5 (discoverability) — PASS.** Title-bar + status-bar entry points read as
+ obvious with no Source Control pane (INV-13 confirmed).
+ - **E2 (baseline router: QuickDiff against git `HEAD` *and* snapshot, clean on
+ commit / Mark-Reviewed — INV-7) and E4 (comment → reply → offer → proposal —
+ PUC-8) remain — now the riskiest unvalidated assumptions and the next
+ concrete work.**
+3. **Vertical slice.** One real end-to-end flow (enter → ask via a comment thread
+ with the **real `@cline/sdk`** → real pending change in the buffer → tweak →
+ keep) on a real document, minimal breadth — proving the loop works for real
+ once E2/E4 are validated.
+4. **Full build (the evolve-in-place migration, §6.10).** The implementation plan
+ decomposed from this (spike-revised) spec, migrated surface-by-surface in the
+ validated order: **baseline router + QuickDiff/diff surface** (E2) **→
+ coediting opt-in gate → preview extension replaces the review-panel webview →
+ comments-first ask + comment-response loop replaces the input webview** (E4) —
+ each bespoke surface sunsetting only after its native replacement is green.
+ One SPEC → one plan → one execution per increment.
### 7.2 Rollout / launch plan
@@ -928,11 +1006,13 @@ host E2E green (§6.8). Marketplace packaging/release is out of scope here.
| Risk | Likelihood / impact | Mitigation |
| --- | --- | --- |
-| Per-change Keep/Undo overlay relies on evolving editor APIs | M / M | Stable contract = multi-diff + per-hunk CodeLens Keep/Reject; overlay is progressive enhancement (§6.10) |
+| Baseline router assumptions fail on real git repos (E2 unvalidated) | M / M | E2 is the next spike experiment; snapshot mode (shipped today) is the always-works fallback (INV-7) |
+| Comment-response loop feels laggy or noisy (E4 unvalidated) | M / M | E4 spikes it with latency notes; D10's off-switch ("Stop editing with Claude") bounds the blast radius |
+| Save-while-pending leaves machine text on disk before a decision | M / L | Pending state + originals persist in the sidecar; Reject restores exactly after reload (§6.9); status-bar count keeps pending changes visible |
| Two source controls (Git + Cowriting) confuse the SCM viewlet | L / M | Multiple SCM providers coexist by design; name it clearly; QuickDiff alone (no full SCM) is the minimal fallback |
-| Built-in preview can't host interactive ✓/✗ inside the preview | M / L | Accept/reject lives in the multi-diff surface, not the preview; the preview is read/annotate only (a deliberate split) |
-| Native surfaces are harder to E2E than a queryable webview | M / L | Assert controller/provider state + pure-transform output |
-| Design-only spec stalls (never delivered) | M / L | §13-Q1 names the delivery decision and owner; the architecture stands regardless |
+| Built-in preview can't host interactive ✓/✗ inside the preview | M / L | Accept/reject lives in the buffer (D18), not the preview; the preview is read/annotate only (a deliberate split) |
+| Native surfaces are harder to E2E than a queryable webview | M / L | Assert controller/provider state + pure-transform output; the shipped E2E harness carries forward (D17) |
+| Evolve-in-place migration destabilizes a working product mid-flight | M / M | Surface-by-surface order (§7.1 rung 4): each bespoke surface sunsets only after its native replacement is green; the pure cores never move |
## 8. Success Metrics & Measurement
@@ -948,7 +1028,7 @@ host E2E green (§6.8). Marketplace packaging/release is out of scope here.
| # | Metric (scores PUC-n) | Target | Fed by | Review cadence |
| --- | --- | --- | --- | --- |
| PM-1 | PUC-1 diff opens in the native diff editor with an accurate baseline⟷live | works for any file incl. untitled | host E2E | per build |
-| PM-2 | PUC-2 no document mutation until Keep; each change independently keep/undo-able | 100% (INV-5) | host E2E | per build |
+| PM-2 | PUC-2 no proposed change settles until Keep; Reject restores the original exactly; each change independently keep/reject-able | 100% (INV-5) | host E2E | per build |
| PM-3 | PUC-3 annotations render in the built-in preview and toggle clean | on/off both correct | unit + host E2E | per build |
## 9. Telemetry, Observability & Monitoring
@@ -1009,17 +1089,14 @@ a bug reaches us as a tracker issue.
| # | Question | Owner | Blocks |
| --- | --- | --- | --- |
-| Q1 | Product packaging & delivery sequence — the decision to act on this north star | Ben | Any implementation plan |
-| Q2 | Per-change Keep/Undo: how far can the stable API go before falling back to per-hunk CodeLens? | Ben | PUC-2 build (last surface) |
-| Q4 | Does any need genuinely require a webview the native surfaces can't serve (INV-3 escape hatch)? | Ben | Per-surface build |
-| Q7 | Once the writer freely edits the proposal across hunks (INV-12), how do per-change Keep/Undo behave — do per-hunk actions survive arbitrary edits, or does free editing collapse the change set to a single Keep/Reject-all? | Ben | PUC-2 build |
+| Q4 | Does any need genuinely require a webview the native surfaces can't serve (INV-3 escape hatch)? — narrowed at v0.2.0: the two shipped webviews both have native replacements (D3, D19); the one candidate left is mermaid rendering in the preview, expected to ride `markdown.previewScripts` (the `markdown-mermaid` pattern), to be confirmed at the preview-surface build | Ben | Per-surface build |
**Resolved**
| # | Decision | Resolution | Date |
| --- | --- | --- | --- |
-| D1 | Scope / altitude | Design-only north star — architecture + surface mapping, no packaging/migration commitment | 2026-06-27 |
-| D2 | Accept/reject primary surface | Multi-diff editor with Keep/Undo (CodeLens stable fallback) | 2026-06-27 |
+| D1 | Scope / altitude | Design-only north star — architecture + surface mapping, no packaging/migration commitment. *(Amended by D17: the packaging commitment is now taken.)* | 2026-06-27 |
+| D2 | Accept/reject primary surface | Multi-diff editor with Keep/Undo (CodeLens stable fallback). *(Superseded by D18: the spike showed CodeLens cannot render in a diff editor.)* | 2026-06-27 |
| D3 | Rendered preview surface | Extend the built-in Markdown preview via a `markdown-it` contribution (no webview) | 2026-06-27 |
| D4 | Diff surface | `QuickDiffProvider` + native diff editor against a `cowriting-baseline:` virtual doc | 2026-06-27 |
| D5 | Core capabilities | Pinned baseline, char-precise authorship attribution, durable git-native sidecar, live cancellable progress, region threads, on-behalf-of identity, pure-core/thin-controller discipline | 2026-06-27 |
@@ -1028,12 +1105,17 @@ a bug reaches us as a tracker issue.
| D8 | Comments can summon the machine | On a coedited doc, a comment fires a turn that replies in-thread + offers an edit → pending proposal (BUC-5, INV-5); inert on non-coedited docs | 2026-06-27 |
| D9 | Native diff is author-blind | Verified the diff editor has no per-author color API; authorship lives in the preview (and optionally a decoration track-changes editor), not the native diff (INV-11) | 2026-06-27 |
| D10 | Comment trigger | While a document is in coediting mode, **every** new comment summons the machine (no per-comment gate); coediting mode is the control, "Stop editing with Claude" the off switch | 2026-06-27 |
-| D11 | Authorship surface (Q5) | Option 1 — the annotated preview is the authorship view; native diff stays author-blind; in-editor decorations (opt 2) and an owned track-changes diff (opt 3) are out of scope | 2026-06-27 |
-| D12 | Editable proposals | The review's proposed side is writable; the writer tweaks before Keep; machine words vs. writer tweaks are attributed at Keep (INV-12); live doc untouched until Keep (INV-5) | 2026-06-27 |
+| D11 | Authorship surface (Q5) | Option 1 — the annotated preview is the authorship view; native diff stays author-blind; in-editor decorations (opt 2) and an owned track-changes diff (opt 3) are out of scope. *(Updated by D21: opt 2 shipped and is retained.)* | 2026-06-27 |
+| D12 | Editable proposals | The review's proposed side is writable; the writer tweaks before Keep; machine words vs. writer tweaks are attributed at Keep (INV-12); live doc untouched until Keep (INV-5). *(Amended by D18/D20: the writable surface is the live buffer itself.)* | 2026-06-27 |
| D13 | Baseline semantics & source | Baseline = the last settled version. Its **source follows the file's home**: git `HEAD` where tracked (auto-resets on commit, storage-free), else a snapshot set on entry (global storage / in-memory). A coedited doc always has a baseline; the only empty state is a genuinely empty document (INV-7) | 2026-06-27 |
| D14 | Pin is the non-git fallback only (resolves Q8) | "Mark Changes as Reviewed" exists **only in snapshot mode** (no committed `HEAD`); git-tracked files have no manual pin — the writer **commits** and the baseline follows `HEAD`. The pin command is `when`-gated on `cowriting.baselineMode == snapshot` | 2026-06-27 |
| D15 | Comments & the preview | Native comment threads can't render in the preview (it's a webview — verified). The preview *initiates* a comment (selection → source range via the `markdown-it` source map) and shows deep-linking 💬 markers; a whole-document comment anchors at the top. Threads stay native (gutter + Comments panel); no bespoke in-webview comment UI | 2026-06-27 |
-| D16 | Change discovery (resolves Q3) | No flow requires the Source Control pane (INV-13). Primary entry = editor title-bar actions + `✦ Coediting · N changes` status-bar item + auto-open review; SCM viewlet + gutter kept as optional git-user extras | 2026-06-27 |
+| D16 | Change discovery (resolves Q3) | No flow requires the Source Control pane (INV-13). Primary entry = editor title-bar actions + `✦ Coediting · N changes` status-bar item + auto-open review; SCM viewlet + gutter kept as optional git-user extras. *(v0.2.0: "auto-open review" becomes "proposals land in the buffer in front of the writer," D18.)* | 2026-06-27 |
+| D17 | Packaging & delivery vehicle (resolves Q1) | **Evolve the shipped extension in place** onto the native surfaces: the 21 pure core modules (~3.5k LOC, ~60%) are retained as-is, thin controllers are rewired, and the two bespoke webviews sunset only after their native replacements are green (§6.10). Greenfield rejected (re-derives wiring + E2E harness); staying design-only rejected (stalls) | 2026-07-01 |
+| D18 | Accept/reject surface (resolves Q2; supersedes D2) | **Pending changes live in the buffer** — proposals land optimistically as decorated pending text (originals retained), decided by per-hunk ✓ Keep / ✗ Reject CodeLens, tweaked by typing. Grounds: the spike verified CodeLens does **not** render in a diff editor (killing the multi-diff surface); the shipped F12 proved the in-buffer model end-to-end; it matches the platform's own AI edit-session paradigm and uses only stable APIs. INV-5 reworded from "buffer untouched" to "nothing settles until Keep; Reject restores exactly" | 2026-07-01 |
+| D19 | Ask surface | **Comments-first**: the native comment thread input is the ask input for both imperative asks and remarks; the title-bar "Ask Claude" creates/focuses a thread (selection, else whole-document); the shipped split-pane input webview sunsets. One custom surface eliminated; ask and discuss collapse into one native gesture (D8/D10) | 2026-07-01 |
+| D20 | Tweaks × per-hunk decisions (resolves Q7; amends D12) | Free editing happens in the live buffer; each pending change **re-anchors** around writer edits so per-hunk Keep/Reject survives ordinary tweaking; a change displaced by heavy rewriting collapses to a whole-proposal Keep/Reject (skip + report in keep-all/reject-all) — the INV-1 discipline applied to proposals | 2026-07-01 |
+| D21 | Authorship surfaces (updates D11) | The annotated preview **and** the in-editor author-colored track-changes decorations are both authorship surfaces (the latter shipped 2026-06-27, after D11 deferred it); both ride the same pure cores; the native diff stays author-blind (INV-11). Option 3 (owned track-changes diff) remains out | 2026-07-01 |
## 14. Glossary & References
@@ -1043,8 +1125,9 @@ a bug reaches us as a tracker issue.
Disposable; never committed by us (INV-7).
- **QuickDiffProvider** — the VS Code API that supplies gutter dirty-diff bars from a
virtual *original* document.
-- **Multi-diff editor** — VS Code's native multi-change review surface; here the
- accept/reject surface (Keep/Undo).
+- **Pending change** — a machine-proposed edit landed optimistically in the live
+ buffer as decorated, unsettled text (original retained); decided by per-hunk
+ ✓ Keep / ✗ Reject CodeLens, tweaked by typing (D18). The accept/reject surface.
- **Coauthoring sidecar** — the forward-compatible, unknown-field-preserving artifact
that persists anchors, authorship, proposed changes, and threads, and round-trips
to an outer review forge.
@@ -1059,8 +1142,8 @@ a bug reaches us as a tracker issue.
- **Pure core / thin controller** — the vscode-free, unit-tested logic vs. the
vscode-aware wiring split (INV-6).
- **References:** VS Code APIs — SCM/`QuickDiffProvider`, diff editor (`vscode.diff`),
- multi-diff editor, Markdown preview `markdown-it` contribution, Comments,
- `TextDocumentContentProvider`, decorations. *Prior art (patterns proven in earlier
+ Markdown preview `markdown-it` contribution, Comments,
+ `TextDocumentContentProvider`, decorations, CodeLens. *Prior art (patterns proven in earlier
coauthoring work, reused here on their own merits): baseline pinning, char-precise
attribution, the portable coauthoring sidecar, live turn progress, and editable
proposals (tweak-before-keep with mixed human/machine authorship).*