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).*