diff --git a/specs/coauthoring-native-surfaces.md b/specs/coauthoring-native-surfaces.md
index 0d5335e..054e875 100644
--- a/specs/coauthoring-native-surfaces.md
+++ b/specs/coauthoring-native-surfaces.md
@@ -1,7 +1,7 @@
---
status: draft
---
-# Solution Design: Native-First Coauthoring — a VS Code-idiomatic redesign
+# Solution Design: Coediting Markdown with a Machine — a Native VS Code Experience
| | |
| --- | --- |
@@ -9,7 +9,7 @@ status: draft
| **Reviewers / approvers** | Ben Stull |
| **Status** | `draft` |
| **Version** | v0.1.0 |
-| **Source artifacts** | North-star architecture spec (design-only). Lineage: the shipped plugin Epic `benstull/vscode-cowriting-plugin#1` and its Features F1–F12 (the experience this re-homes). Reuses concepts from `coauthoring-inner-loop.md`, `coauthoring-attribution.md`, `coauthoring-propose-accept.md`, `coauthoring-diff-view.md`, `coauthoring-rendered-preview.md`, `coauthoring-cross-rung-format.md`, `coauthoring-live-progress.md`. Supersedes: none (design-only; does not commit a repo or migration). |
+| **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. |
**Change log**
@@ -25,194 +25,215 @@ status: draft
### 1.1 Executive Summary
-A writer working with a machine coauthor needs three things to feel native to
-their editor: to **see what changed** against a known-good point, to **review and
-accept or reject the machine's proposed changes** one at a time, and to **read a
-clean rendered version** of the document with those changes legible. The existing
-plugin (Epic #1, F1–F12) proved that experience is valuable — but it built each of
-those three surfaces as bespoke UI (custom webviews, hand-rolled diff rendering,
-editor decoration overlays) that the writer must *learn*. This design re-homes the
-same experience onto the surfaces the writer **already knows** — VS Code's own diff
-editor, Source Control gutter, multi-diff review, and Markdown preview — so the
-coauthoring loop reads as a built-in capability of the editor rather than a foreign
-panel grafted onto it. The value is lower cognitive cost for the writer and a
-dramatically smaller surface for us to build, test, and maintain.
+A growing share of real writing — specs, docs, RFCs, design notes, prose — happens
+in markdown, and writers increasingly want a machine to help author it. But
+coediting a markdown *document* with a machine is awkward today. When the machine
+edits the file it hands back a **rewrite, not a reviewable change**: the writer is
+left staring at a wall of new text with no natural answer to "what did you actually
+change?" The writer can't easily **keep some of the machine's edits and drop
+others**; after a few rounds nobody can tell **which words are the human's and which
+the machine's**; and there's no **clean rendered view** of the work-in-progress with
+the changes made legible. In practice "writing with a machine" collapses into
+copy-paste between a chat window and the file — throwing away the diff, granular
+control, and provenance. This design delivers human↔machine markdown coediting
+**inside the editor**, through the surfaces a writer already uses to review
+changes, so coediting *feels like a native capability of the editor* rather than a
+chat window bolted onto the side. The value: writers can let a machine help with
+real documents while staying in control of what lands and confident about what
+changed.
### 1.2 Background
-The forge (Gitea / rfc-app) is the *outer* loop — durable review and record. The
-*inner* loop — writing, keystroke by keystroke, with a machine coauthor — lives in
-the editor. The shipped plugin delivered that inner loop as a standalone VS Code
-extension on `@cline/sdk`, accreting twelve Features: region threads, char-precise
-attribution, propose/accept, cross-rung persistence, a diff view, a rendered
-track-changes preview, an interactive review panel, a preview toolbar, an inline
-editor diff, and live turn progress. Each Feature solved a real need, but the diff,
-the accept/reject, and the preview were each implemented as **custom rendering** —
-webviews with bundled markdown-it/mermaid, sealed message channels, and
-`TextEditorDecorationType` overlays — reinventing machinery VS Code already ships.
-The accumulated UI is powerful but idiosyncratic: it does not look or behave like
-the rest of the editor, and it is large to maintain.
+Markdown is the lingua franca of technical and semi-technical writing — it is where
+specifications, documentation, RFCs, and a great deal of prose are actually drafted
+and revised. At the same time, capable machine coauthors have arrived, and writers
+want to use them on these documents, not just on code.
-VS Code, meanwhile, exposes first-class APIs for exactly these three needs. The
-strategic question this spec answers: *if we designed the plugin from scratch today
-with "feels like a built-in feature" as the north star, how close to native VS Code
-patterns could we get, and what would the architecture be?*
+But the modalities available for "writing with a machine" were built for other
+shapes of work, and they serve *document coediting* poorly:
+
+- **Chat copy-paste.** Paste the document (or a passage) into a conversation, get a
+ rewrite back, paste it into the file. The diff is gone, authorship is gone,
+ granular accept/reject is gone, and the file and the conversation drift apart.
+- **Whole-file rewrite.** The machine overwrites the file. The writer must diff it
+ by hand to see what happened and has only all-or-nothing control.
+- **Inline code completions.** Built for short, accept-as-you-type code suggestions
+ in the editor — not for reviewing a machine's substantive revisions to a prose
+ document, where the writer wants to see, weigh, and selectively keep changes.
+
+None of these lets the writer do the thing that matters for a document: **review the
+machine's proposed revision the way they'd review any change — as a diff they can
+read, accept or reject piece by piece, attribute, and preview rendered.**
+
+Crucially, the editor *already has* best-in-class machinery for exactly this — diff
+viewing, source-control review, multi-change accept/reject, threaded comments, and
+a rendered Markdown preview — but none of it is wired up for coediting with a
+machine. The opportunity is to deliver machine coediting *through* those surfaces.
### 1.3 Business Actors / Roles
| Role | Responsible for (in the business) |
| --- | --- |
-| **Coauthor (human)** | A writer/engineer authoring prose, specs, or docs in their editor. Owns the document; decides what changes land. |
-| **Coauthor (machine)** | The machine coauthor (Claude, via `@cline/sdk`). Proposes changes on the human's behalf; never lands them unilaterally. |
+| **Writer (human)** | Authors and owns a markdown document; decides what changes land in it. |
+| **Machine coauthor** | A capable machine writing assistant that the writer asks to help revise the document; it proposes changes on the writer's behalf and never lands them unilaterally. |
### 1.4 Problem Statement
-The coauthoring experience is delivered through UI the writer must learn anew —
-bespoke panels, custom-rendered diffs, and overlay decorations that do not match,
-and do not cooperate with, the editor surfaces the writer uses every day. The
-experience is *foreign to its host*. Separately, that bespoke UI is costly to
-build and maintain: every surface (diff, accept/reject, preview) is a hand-written
-renderer with its own bugs, its own test harness, and its own divergence from
-platform conventions (theming, scroll-sync, accessibility, keyboard model).
+A writer who wants a machine to help revise a markdown document has no good way to
+do it while staying in control. They cannot see exactly what the machine changed,
+cannot keep some changes and reject others, cannot tell who wrote which words, and
+cannot read a clean rendered result — at least not without leaving their editor and
+diffing, cherry-picking, and reconciling by hand. The machine's revision arrives as
+an opaque rewrite rather than a reviewable proposal, so the writer must choose
+between blind trust and tedious manual reconciliation.
### 1.5 Pain Points
| # | Pain | Who feels it | Cost / frequency today |
| --- | --- | --- | --- |
-| PP-1 | The diff/accept/preview surfaces don't look or behave like the rest of VS Code — new mental model to learn | Coauthor (human) | Every session; friction on every review |
-| PP-2 | Custom webview/decoration rendering re-implements platform machinery (theming, scroll-sync, a11y, diff layout) and drifts from it | Maintainer | Ongoing; each Feature added more surface to own |
-| PP-3 | Accept/reject lives in a bespoke panel rather than where the writer reviews changes natively (the diff) | Coauthor (human) | Every proposal review |
-| PP-4 | The rendered preview is a sealed webview that can't cooperate with other Markdown extensions the writer already uses | Coauthor (human) | Every preview open |
+| PP-1 | The machine's revision arrives as a rewrite, not a reviewable change — "what did you change?" has no easy answer | Writer (human) | Every machine revision; manual diffing each time |
+| PP-2 | Accepting the machine's help is all-or-nothing — no way to keep some edits and reject others in place | Writer (human) | Every revision with mixed-quality changes |
+| PP-3 | Authorship blurs — after a few rounds nobody can tell which words are the human's and which the machine's | Writer (human) | Cumulative; erodes trust and review confidence |
+| PP-4 | No clean rendered view of the in-progress document with the changes made legible | Writer (human) | Every time the writer wants to judge the result as prose |
+| PP-5 | The machine works opaquely — ask, wait, a blob appears; no sense of progress or ability to stop it | Writer (human) | Every machine turn |
### 1.6 Targeted Business Outcomes
| # | Outcome | Success metric | Baseline → Target | Guardrail (must not regress) | How / when measured |
| --- | --- | --- | --- | --- | --- |
-| BO-1 | The coauthoring loop reads as a built-in editor capability (familiarity, lower learning cost) | Writer can complete a see-diff → review-proposal → read-preview loop using only native surfaces they already know | bespoke panels → native surfaces | The full inner-loop experience (diff, accept/reject, preview, threads, attribution, live progress) is preserved | Dogfooding walkthrough on the design's first build |
-| BO-2 | The plugin is materially cheaper to build and maintain | Custom-rendering LOC retired in favor of platform APIs | large webview/decoration surface → thin controllers over native APIs | No capability dropped | Code-size + test-surface comparison at build time |
+| BO-1 | Writers adopt machine coauthoring for *real documents* because it's reviewable and in-editor | A writer completes a full revise → review → keep/reject → read loop without leaving the editor or copy-pasting | chat copy-paste / blind rewrite → in-editor reviewable coediting | The writer always stays in control of what lands | Dogfooding walkthrough |
+| BO-2 | Coediting feels like a built-in editor capability, not a separate tool | The diff, accept/reject, and preview use surfaces the writer already knows | bespoke/foreign UI → native surfaces | Full capability (see-change, selective accept, authorship, preview, progress) preserved | Dogfooding walkthrough |
### 1.7 Scope (business)
-- **In scope:** a from-scratch architecture for the inner-loop coauthoring
- experience that maximizes use of native VS Code surfaces for the three core needs
- (see a diff, review proposed changes, read a rendered preview), plus the proven
- supporting capabilities (region threads, authorship attribution, durable
- cross-rung persistence, live turn progress, on-behalf-of identity).
-- **Out of scope:** committing a specific repository, a migration plan from the
- shipped plugin, or a marketplace release. This is a **design-only north-star**
- (see §2): repo-and-migration decisions are deferred to a delivery decision, not
- taken here.
-- **Non-goals:** changing *what* the coauthoring experience is (the loop is
- validated); a server or cloud backend (the inner loop stays serverless,
- git-native); multi-file agent orchestration beyond the single-document loop.
+- **In scope:** enabling a writer to coedit a markdown document with a machine while
+ 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.
+- **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.
### 1.8 Assumptions · Constraints · Dependencies
-- **Assumptions:** VS Code's stable APIs (SCM/QuickDiff, diff editor, multi-diff
- editor, Markdown preview `markdown-it` contribution, Comments, decorations,
- `TextDocumentContentProvider`, `FileSystemWatcher`) remain available; the machine
- coauthor runs locally via `@cline/sdk` as in the shipped plugin.
-- **Constraints:** serverless and git-native (no external store); no secret bytes
- in artifacts or transcripts (§6.3-handbook); a VS Code extension has **no §9
- deploy pipeline stage** (it is not a webapp — see §7.2).
-- **Dependencies:** `@cline/sdk` (loaded via runtime dynamic `import()` — never
- bundled; a POC-validated fact carried forward). The shipped plugin's pure cores
- (attribution, anchoring, merge, turn-progress reducer) are the reuse source.
+- **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
+ any persisted artifact.
+- **Dependencies:** a local machine-coauthor runtime; the editor's stable extension
+ APIs for the surfaces above.
### 1.9 Business Use Cases
-**BUC-1 — As a human coauthor, I can see what changed in my document since a point I
-chose, in a familiar diff, so that I can trust what I am about to keep.**
+**BUC-1 — As a writer, I can see exactly what the machine changed in my document
+since a point I chose, so that I can judge it instead of trusting blindly.**
```gherkin
-Scenario: BUC-1 — review changes against a chosen point
- Given I marked the document as "good" at some earlier moment
- And the document has changed since then
+Scenario: BUC-1 — see what changed
+ Given I marked my document as "good" at some earlier moment
+ And the machine has since revised it
When I ask to see what changed
- Then the changes are shown in the same diff experience I use for everything else
+ Then the changes are shown to me as a clear before/after I can read
```
-- **BUC-1 acceptance criteria:** the human sees an accurate before/after of the
- document against the chosen point, in their editor's normal diff experience.
+- **BUC-1 acceptance criteria:** the writer sees an accurate before/after of the
+ document against the chosen point.
-**BUC-2 — As a human coauthor, I can review the machine's proposed changes and accept
-or reject them one at a time, so that I stay in control of what lands.**
+**BUC-2 — As a writer, I can keep or reject the machine's proposed changes one at a
+time, so that I stay in control of what lands.**
```gherkin
-Scenario: BUC-2 — accept or reject proposed changes
- Given the machine coauthor has proposed changes to my document
+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 proposed change individually, or all at once
- And nothing the machine proposed lands until I keep it
+ 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
```
- **BUC-2 acceptance criteria:** no proposed change alters the document until the
- human keeps it; each change is independently keepable/discardable; an accept-all
- and reject-all exist.
+ writer keeps it; each change is independently keepable/discardable; keep-all and
+ reject-all exist.
-**BUC-3 — As a human coauthor, I can read a clean, rendered version of my document with
-the changes and authorship made legible, so that I can judge the result as prose,
-not as markup.**
+**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.**
```gherkin
-Scenario: BUC-3 — read the rendered result
- Given my document has changed and has mixed authorship
- When I open its rendered preview with annotations on
+Scenario: BUC-3 — authorship and rendered result
+ Given my document has mixed human/machine authorship and recent changes
+ When I open its rendered view with annotations on
Then I see the document rendered, with changes and authorship visibly marked
- And I can turn the annotations off to read it clean
+ And I can turn annotations off to read it clean
```
-- **BUC-3 acceptance criteria:** the rendered document shows track-changes and
- authorship when annotations are on, renders clean when off, in a preview that
- behaves like the editor's normal Markdown preview.
+- **BUC-3 acceptance criteria:** the rendered document shows changes and authorship
+ when annotations are on, renders clean when off.
-**BUC-3a (unhappy) — As a human coauthor, when an anchored thread / attribution /
-proposal can no longer be confidently placed after edits, I am shown it as orphaned
+**BUC-4 — As a writer, I can follow the machine's progress while it works and stop
+it, so that it is never an opaque black box.**
+
+```gherkin
+Scenario: BUC-4 — visible, stoppable machine work
+ Given I asked the machine to revise my document
+ When it is working
+ Then I can see live progress
+ And I can cancel before it finishes
+```
+
+**BUC-3a (unhappy) — As a writer, when a comment, an attributed span, or a proposed
+change can no longer be confidently placed after edits, I am shown it as displaced
rather than silently moved to the wrong place.**
---
## 2. Solution Proposal
-**Build software: a brand-new VS Code extension that delivers the inner-loop
-coauthoring experience almost entirely through native VS Code surfaces**, keeping
-the shipped plugin's validated *experience* and its proven *pure cores* while
-discarding its bespoke *rendering*.
+**Build software: a VS Code extension that lets a writer coedit a markdown document
+with a machine coauthor, delivering the whole loop through the editor's own native
+review surfaces.**
-The unifying idea: **model the coauthored document as a tiny Source Control.** The
-last point the human marked "good" (the **pinned baseline**, carried from F6) is the
-SCM *original*; the live document is the working tree; the machine's proposed edits
-are *pending changes*. Once the relationship is framed as source control, VS Code's
-own machinery supplies the three core surfaces for free:
+The enabling idea: **model the coauthored document as a tiny source control.** The
+point the writer last marked "good" (a **pinned baseline**) is the source-control
+*original*; the live document is the working tree; the machine's proposed edits are
+*pending changes*. Once the relationship is framed as source control, the editor's
+own machinery supplies the experience the writer needs — and it is machinery they
+already know:
-| Core need | Native surface | Replaces (shipped plugin) |
+| What the writer needs | Native surface that delivers it | What it gives them, for free |
| --- | --- | --- |
-| See any diff | `QuickDiffProvider` gutter bars + the **native diff editor** (`vscode.diff`) against a `cowriting-baseline:` virtual document | F6 custom diff-view controller |
-| Propose / accept / reject | the **multi-diff editor** with per-change **Keep/Undo** (the "AI edits" review feel), backed by the existing propose/accept model | F4/F10/F12 webview + decoration accept/reject UI |
-| Rendered preview | **extend the built-in Markdown preview** via a `markdown-it` plugin contribution (authorship + track-changes injected into VS Code's own preview) | F7/F10 sealed webview renderer |
-| Region threads | the **Comments API** (already native in F2 — kept as-is) | — (already native) |
-| Live turn progress | notification activity-line + a shared **OutputChannel** + cancellable turn | #60 (kept; already native) |
+| 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 changes (BUC-2) | The **multi-diff review editor** with per-change **Keep/Undo** | One native surface to sweep all of the machine's changes, accepting or discarding each |
+| 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 (supports BUC-1/3) | The **Comments API** — threaded comments anchored to a region | The familiar review-comment experience, in the gutter |
+| Follow + stop the machine (BUC-4) | A progress **notification** + a shared **OutputChannel**, with Cancel | Live, stoppable, legible machine work |
-**Why this approach over the alternatives.** The do-nothing option (keep the shipped
-plugin) leaves a foreign-feeling, maintenance-heavy UI. A "polish the webviews"
-option spends effort making bespoke surfaces *look* native without getting the
-platform's behavior (theming, scroll-sync, a11y, keyboard model, cooperation with
-other extensions) — it pays the cost without the benefit. Re-homing onto native
-surfaces gets the behavior for free and shrinks what we own. The cost is accepting
-the platform's affordances where they differ from our bespoke ones, and a small
-stable-API boundary on the per-change Keep/Undo overlay (§6.10, §13).
+**Why this approach over the alternatives.** Doing nothing leaves writers in the
+copy-paste-from-chat world (§1.2), which structurally cannot offer a diff,
+selective accept, or authorship. Building a **bespoke coediting UI** (custom panels,
+hand-rolled diff rendering, overlay decorations) can *imitate* these surfaces but
+won't inherit the platform's behavior — theming, scroll-sync, accessibility, the
+keyboard model, navigation, cooperation with other extensions — so it pays the cost
+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).
**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 new repo
-vs. an in-place re-platform, nor to a data migration. Those are taken at delivery
-(§7.1, Q1 in §13).
+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).
**Solution scope.** *In:* the architecture, surface mapping, data model, contracts,
-and per-use-case design for the native-first plugin, plus carry-forward of pinned
-baseline, authorship attribution, cross-rung persistence, live progress, threads,
-and on-behalf-of identity. *Out:* repo/migration choice, marketplace publish,
-multi-file orchestration. *Non-goal:* re-deriving the coauthoring experience — it is
-inherited from F1–F12.
+and per-use-case design for native, in-editor human↔machine markdown coediting,
+including baseline pinning, authorship attribution, durable git-native persistence,
+live machine progress, region threads, and on-behalf-of identity. *Out:* product
+packaging/release, multi-file orchestration, non-markdown formats. *Non-goal:*
+mutating the writer's document without explicit acceptance.
---
@@ -220,59 +241,59 @@ inherited from F1–F12.
| Product persona | In the plugin | Maps to business role(s) |
| --- | --- | --- |
-| **PP-1 Inner-loop writer** | Pins a baseline, asks the machine to edit, reviews proposed changes in the diff, reads the annotated preview, opens region threads. | Coauthor (human) |
-| **PP-2 Machine coauthor (Claude)** | Runs a turn via `@cline/sdk`, streams progress, and emits **proposed** edits (never lands them); authored spans carry its provenance. | Coauthor (machine) |
+| **PP-1 Writer** | Pins a baseline, asks the machine to revise the document (or a selection), reviews proposed changes in the diff, reads the annotated preview, opens region threads, watches/stops the machine. | Writer (human) |
+| **PP-2 Machine coauthor** | Runs a turn locally, streams progress, and emits **proposed** edits (never lands them); authored spans carry its provenance. | Machine coauthor |
## 4. Product Use Cases
```gherkin
Scenario: PUC-1 — see the diff against the pinned baseline (realizes BUC-1)
- Given I run "Cowriting: Pin Review Baseline to Now"
- When I later edit the document
- Then the SCM gutter shows change bars against the baseline
+ Given I run "Pin Review Baseline to Now"
+ When I (or the machine) later change the document
+ Then the source-control gutter shows change bars against the baseline
And "Open Changes" opens the native diff editor (baseline ⟷ live), inline or side-by-side at my choice
```
```gherkin
-Scenario: PUC-2 — review and accept/reject proposed changes (realizes BUC-2)
- Given I ask the machine coauthor to edit the document (or a selection)
+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 each change offers Keep / Undo (Accept / Reject)
- And toolbar actions offer Accept all / Reject all
- And the document is unchanged until I keep a change
+ And toolbar actions offer Keep all / Reject all
+ And my document is unchanged until I keep a change
```
```gherkin
Scenario: PUC-3 — read the annotated rendered preview (realizes BUC-3)
Given my document has changes and mixed authorship
- When I open the built-in Markdown preview with Cowriting annotations enabled
- Then track-changes (insert/delete) and authorship colors render inside the native preview
- And toggling "cowriting.annotations" off renders the document clean
+ When I open the built-in Markdown preview with annotations enabled
+ Then insert/delete marks and authorship colors render inside the native preview
+ And toggling annotations off renders the document clean
```
```gherkin
-Scenario: PUC-4 — region thread (realizes BUC-3a; carried from F2)
- Given I select a region and add a coauthoring thread
- When I edit around it or reload
- Then the thread re-anchors; if it cannot be confidently placed it is shown orphaned, never silently moved
+Scenario: PUC-4 — region thread (realizes BUC-3a)
+ Given I select a passage and add a coauthoring thread
+ When I edit around it or reopen the document
+ Then the thread re-anchors; if it cannot be confidently placed it is shown displaced, never silently moved
```
```gherkin
-Scenario: PUC-5 — live turn progress (carried from #60)
+Scenario: PUC-5 — live machine progress (realizes BUC-4)
Given I started a machine turn
When the machine is working
Then a notification shows the live activity line and token count
- And the "Cowriting: Claude" OutputChannel streams the full text
+ And an OutputChannel streams the full text
And I can cancel the turn
```
```gherkin
-Scenario: PUC-6 — reload / external change (realizes BUC-3a)
+Scenario: PUC-6 — reopen / external change (realizes BUC-3a)
Given I reopen the workspace or an external change lands (a git pull)
When the document loads
- Then baseline, proposals, attributions, and threads re-resolve against current text
- And anything that cannot be confidently placed is shown orphaned
+ Then baseline, proposed changes, attributions, and threads re-resolve against current text
+ And anything that cannot be confidently placed is shown displaced
```
## 5. UX Layout
@@ -280,90 +301,89 @@ Scenario: PUC-6 — reload / external change (realizes BUC-3a)
The experience is delivered through **native VS Code surfaces**; there is no bespoke
panel to wireframe. Described in text (source of truth):
-- **Source Control gutter & viewlet.** A dedicated `SourceControl` named
- *"Cowriting"* coexists with the real Git SCM. Its `QuickDiffProvider` draws the
- familiar change bars in the editor gutter against the pinned baseline. Its
- resource group(s) list the document's pending coauthoring changes; "Open Changes"
- uses the native diff editor.
-- **Native diff editor.** `vscode.diff(baselineUri, docUri)` — the writer's normal
- diff, with the platform's inline/side-by-side toggle, navigation, and a11y.
+- **Source Control gutter & viewlet.** A dedicated source-control entry,
+ *"Cowriting,"* coexists with Git. Its quick-diff provider draws the familiar
+ change bars in the editor gutter against the pinned baseline; its resource group
+ lists the document's pending coauthoring changes; "Open Changes" uses the native
+ diff editor.
+- **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 **Accept all / Reject all**. This is the accept/reject surface.
+ opens showing `baseline ⟷ proposed`, each change carrying **Keep/Undo**; a
+ toolbar carries **Keep all / Reject all**. This is the accept/reject surface.
- **Built-in Markdown preview.** The writer's normal "Open Preview to the Side";
- with Cowriting annotations on, authorship colors and track-changes marks appear
- *inside* it (contributed `markdown-it` plugin + preview stylesheet). A command/
- setting toggles annotations.
+ 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), exactly as F2.
-- **Notification + OutputChannel.** Live turn progress, with a Cancel action.
+ selection, reply, resolve).
+- **Notification + OutputChannel.** Live machine progress, with a Cancel action.
### 5.1 Design tooling decision
- **Decision:** **Code directly.**
-- **Rationale:** the entire thesis is to use native VS Code chrome, so there is no
- net-new bespoke visual surface to explore. Scoring the six-question rubric:
- Q1 −1 (no net-new surface — native editor chrome), Q2 −1 (UI is specified at
+- **Rationale:** the experience is delivered through native VS Code chrome, so there
+ is no net-new bespoke visual surface to explore. Scoring the six-question rubric:
+ Q1 −1 (no net-new surface — native editor chrome), Q2 −1 (UI specified at
wireframe-level prose against known platform surfaces), Q3 −1 (no multi-page
- visual language), Q4 −1 (VS Code *is* the design system here), Q5 −1 (solo build),
- Q6 −1 (logic-dominant — wiring pure cores to platform APIs). Total **−6 → Skip
+ visual language), Q4 −1 (the editor *is* the design system here), Q5 −1 (solo
+ build), Q6 −1 (logic-dominant — wiring cores to platform APIs). Total **−6 → Skip
Design**. The only "designed" pixels are the preview annotation stylesheet (a
- small CSS theme using VS Code theme variables), which does not warrant Design.
+ small CSS theme using the editor's theme variables), which does not warrant
+ Design.
- **Design:** N/A — no bespoke UI surface (native VS Code surfaces only).
### 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
-coauthoring **orphaned** state surfaced in the Comments thread / proposal listing.
+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
+coauthoring **displaced** state surfaced in the thread / proposed-change listing.
## 6. Technical Design
### 6.1 Invariants
-- **INV-1 (carried from F2/INV-1):** an anchored artifact (thread, attribution,
- proposal) is **never silently moved** to a wrong span; if it cannot be confidently
- re-resolved it becomes **orphaned** (visible, flagged).
-- **INV-2 (carried):** persistence is **git-native and serverless** — diffable,
- human-resolvable JSON; **no server, no external store**. (The *baseline* is the
- one exception: it is a throwaway local artifact stored **outside** the repo —
- INV-7.)
+- **INV-1 — never silently moved:** an anchored artifact (thread, attributed span,
+ proposed change) that cannot be confidently re-resolved becomes **displaced**
+ (visible, flagged), never relocated to a wrong span.
+- **INV-2 — git-native and serverless:** durable coauthoring data is diffable,
+ human-resolvable JSON committed alongside the document; no server, no external
+ store. (The *baseline* is the exception — a throwaway local artifact kept outside
+ the repo, INV-7.)
- **INV-3 — native-first:** the diff, accept/reject, and rendered-preview surfaces
are delivered by **native VS Code APIs**, not bespoke webviews or decoration
overlays. New custom rendering requires an explicit, recorded justification
(§6.7) that no native surface fits.
-- **INV-4 (carried):** the persisted artifact is **forward-compatible and
- cross-rung**: `schemaVersion` present; `anchors`, `provenance`, `attributions`,
- `proposals`, `threads` share one envelope; **unknown fields are preserved** on
- read/merge round-trips (the F5 cross-rung contract).
-- **INV-5 — proposals are pending until kept:** a proposed edit does **not** mutate
- the document until the human keeps it (Keep in the multi-diff, or Accept). This is
- the propose-by-default rule (carried from F4), now expressed through the SCM/diff
- surface.
+- **INV-4 — forward-compatible & portable:** the persisted artifact carries a
+ `schemaVersion`; `anchors`, `provenance`, `attributions`, `proposals`, and
+ `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-6 — pure cores stay vscode-free:** attribution, anchoring, merge, diffing,
- and the turn-progress reducer are pure modules with no `vscode` import,
- unit-tested in isolation; controllers are the only `vscode`-aware layer (the
- shipped plugin's most valuable discipline — carried wholesale).
-- **INV-7 (carried from F6):** the **pinned baseline** is the diff *original* and is
- stored **outside** the working tree (global storage keyed by document URI hash),
+ 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
+ without touching the logic.
+- **INV-7 — baseline lives outside the repo:** the pinned baseline is the diff
+ *original* and is stored in extension global storage keyed by document URI hash,
so it never pollutes the repo and works for any file (including untitled).
- **INV-8 — provenance carries on-behalf-of identity:** every authored span /
- message records `provenance` (human git identity, or agent with `onBehalfOf` the
- human), reused across attributions, proposals, and threads (carried from
- identity + F3 + F5).
-- **INV-9 (carried from #60):** a machine turn is **observable and cancellable** —
- it streams progress and honors an `AbortSignal`; the progress reducer is pure.
+ message records `provenance` (the human's git identity, or the machine acting
+ `onBehalfOf` a human), reused across attributions, proposed changes, and threads.
+- **INV-9 — machine turns are observable and cancellable:** a turn streams progress
+ and honors an `AbortSignal`; the progress reducer is pure.
### 6.2 High-level architecture
-The shape mirrors the shipped plugin's most durable pattern: **pure, vscode-free
-cores** + **thin controllers** that wire those cores to **native surfaces**.
+**Pure, vscode-free cores** + **thin controllers** that wire those cores to **native
+surfaces**.
```mermaid
flowchart LR
subgraph Cores["Pure cores (vscode-free, unit-tested)"]
AT[Attribution tracker
char-precise spans]
- AN[Anchorer
fingerprint + offsets + orphan]
+ AN[Anchorer
fingerprint + offsets + displaced]
MG[mergeArtifacts
union-by-id, unknown-field-safe]
DF[Diff/hunk engine
word- & block-level]
TP[turnProgress
reducer]
@@ -372,11 +392,11 @@ flowchart LR
subgraph Ctrls["Thin controllers (vscode-aware)"]
SC[SourceControl + QuickDiffProvider]
BP[Baseline provider
TextDocumentContentProvider]
- PR[Proposal/review controller
multi-diff Keep/Undo]
+ PR[Review controller
multi-diff Keep/Undo]
PV[Preview extension
extendMarkdownIt]
TC[ThreadController
Comments API]
LP[liveProgress relay]
- ST[SidecarRouter + Store]
+ ST[Sidecar router + store]
end
subgraph Native["Native VS Code surfaces"]
GUT[Gutter dirty-diff + SCM viewlet]
@@ -386,7 +406,7 @@ flowchart LR
COMM[Comments gutter/peek]
NOTE[Notification + OutputChannel]
end
- SDK[["@cline/sdk (dynamic import)"]]
+ SDK[[Machine-coauthor runtime
dynamic import]]
AN --> SC --> GUT
BP --> DIFF
@@ -400,40 +420,40 @@ flowchart LR
```
- **SourceControl + QuickDiffProvider** — owns the "Cowriting" source control and
- the gutter dirty-diff; must never write to the user's document.
+ the gutter dirty-diff; must never write to the document.
- **Baseline provider** — a `TextDocumentContentProvider` for the
- `cowriting-baseline:` scheme; serves the pinned baseline text for any doc URI from
- the out-of-repo baseline store (INV-7).
-- **Proposal/review controller** — turns the machine's emitted edits into pending
- proposals, opens the multi-diff review, and applies Keep/Undo (Accept/Reject) by
- delegating to the propose/accept model; must honor INV-5.
+ `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.
- **Preview extension** — contributes `markdown-it` so the pure `renderAnnotations`
transform runs inside the built-in preview; owns no webview.
-- **SidecarRouter + Store** — persistence (carried from F8): repo `.threads/` when
- in-workspace, global-storage sidecar by URI-hash otherwise; cross-rung envelope.
+- **Sidecar router + store** — persistence: a repo-committed sidecar when the
+ document is in a workspace, a global-storage sidecar keyed by URI hash otherwise.
### 6.3 Data model & ownership
| Entity | Owned by | Key fields | System of record |
| --- | --- | --- | --- |
-| Coauthoring sidecar | the extension (sole writer) | `schemaVersion`, `document.path`, `anchors{}`, `provenance`, `attributions[]`, `proposals[]`, `threads[]`, unknown-field passthrough | git-committed `.threads/.json` (in-workspace) or global-storage sidecar (out-of-workspace), per SidecarRouter |
+| Coauthoring sidecar | the extension (sole writer) | `schemaVersion`, `document.path`, `anchors{}`, `provenance`, `attributions[]`, `proposals[]`, `threads[]`, unknown-field passthrough | git-committed `.threads/.json` (in-workspace) or global-storage sidecar (out-of-workspace) |
| Pinned baseline | the extension | doc URI hash → baseline text + pin reason/time | **out-of-repo** global storage (INV-7) — throwaway, not committed |
-| Provenance | the extension | `kind` (human/agent), `id`, `email`, `onBehalfOf`, `agent{sdk,model,sessionId}` | embedded in sidecar (INV-8) |
+| Provenance | the extension | `kind` (human/machine), `id`, `email`, `onBehalfOf`, `agent{runtime,model,sessionId}` | embedded in sidecar (INV-8) |
-The envelope is the F5 **cross-rung contract** verbatim (v1.x, additive,
-unknown-field-preserving) — the sidecar round-trips to the Gitea substrate
-unchanged. The **baseline is deliberately not** in this envelope: it is local,
-disposable review state (INV-7), not durable coauthoring metadata.
+The sidecar envelope is **forward-compatible and portable** (INV-4): additive,
+unknown-field-preserving, so it round-trips to an outer review forge unchanged. The
+**baseline is deliberately not** in this envelope: it is local, disposable review
+state (INV-7), not durable coauthoring metadata.
```jsonc
-// .threads/docs/spec.md.json — the cross-rung envelope (carried from F5)
+// .threads/docs/spec.md.json — the coauthoring envelope (INV-4)
{
"schemaVersion": 1,
"document": { "path": "docs/spec.md" },
"anchors": { "a1": { "fingerprint": { "text": "…", "before": "…", "after": "…", "lineHint": 42 } } },
"attributions": [ { "anchorId": "a1", "provenance": , "span": { "start": 0, "end": 12 } } ],
"proposals": [ { "id": "p1", "anchorId": "a1", "status": "pending", // pending|accepted|rejected (INV-5)
- "granularity": "block", // word|block (carried from #47)
+ "granularity": "block", // word|block — the decision unit
"provenance": , "before": "…", "after": "…" } ],
"threads": [ { "id": "t1", "anchorId": "a1", "status": "open", "messages": [ … ] } ]
// unknown top-level fields preserved verbatim (INV-4)
@@ -443,29 +463,30 @@ disposable review state (INV-7), not durable coauthoring metadata.
### 6.4 Interfaces & contracts
- **Baseline provider** — `TextDocumentContentProvider` for `cowriting-baseline:`;
- `provideTextDocumentContent(uri) → baselineText`. `BaselineStore.pin(docUri,
- text, reason)` / `get(docUri) → {text, reason} | null` (out-of-repo, INV-7).
-- **QuickDiffProvider** — `provideOriginalResource(docUri) → cowriting-baseline:`
- URI (drives gutter bars). Registered on the "Cowriting" `SourceControl`.
-- **Agent edit seam (carried from F3/F4)** — 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 the pure diff engine (`diffToBlockHunks` /
- `wordEditHunks`, carried from #47/F4). Word-diff splitting reconciled by
- `matchEvent` (the F4 host fix) lives in the pure engine.
-- **Proposal/review controller** — `openReview(docUri)` opens the multi-diff
- (baseline⟷proposed); `keep(proposalId)` = Accept (applies the edit, attributes
- changed words via the F5/F4 `acceptBlock` path, INV-40-style); `undo(proposalId)`
- = Reject (drops it); `keepAll()` / `undoAll()` = Accept/Reject all (descending,
- orphan-skip + report — carried from #46).
+ `provideTextDocumentContent(uri) → baselineText`. `BaselineStore.pin(docUri, text,
+ reason)` / `get(docUri) → {text, reason} | null` (out-of-repo, INV-7).
+- **QuickDiffProvider** — `provideOriginalResource(docUri) → cowriting-baseline:` URI
+ (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
+ 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).
- **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.
-- **Turn runner (carried from #60)** — `runEditTurn(prompt, { onProgress,
- signal, env }) → AgentResult`; pure `turnProgress` reducer; `liveProgress` relay
- to notification + OutputChannel; `CLAUDE_CODE_AUTO_CONNECT_IDE=0` env carried (the
- #59 Apple-Events fix).
-- **ThreadController** (carried from F2) — create/reply/resolve via Comments API.
+- **Turn runner** — `runEditTurn(prompt, { onProgress, signal, env }) →
+ AgentResult`; pure `turnProgress` reducer; `liveProgress` relay to a notification
+ + OutputChannel; Cancel → `abort()`. The machine runtime is loaded via runtime
+ dynamic `import()` (it is ESM and must not be bundled), and spawned with IDE
+ auto-connect disabled so it never triggers an OS automation-permission prompt.
+- **ThreadController** — create/reply/resolve via the Comments API.
### 6.5 Per–Product-Use-Case design
@@ -477,44 +498,44 @@ sequenceDiagram
participant C as SCM/QuickDiff ctrl
participant B as Baseline provider
participant V as Native diff editor
- U->>C: Pin baseline (cowriting.pinBaseline)
+ U->>C: Pin baseline
C->>B: BaselineStore.pin(docUri, text)
- U->>U: edits document
+ U->>U: edits document (or the machine proposes)
C-->>U: gutter dirty-diff bars (provideOriginalResource)
U->>V: Open Changes
V->>B: read cowriting-baseline:docUri
V-->>U: native diff (baseline ⟷ live)
```
-- **Implementation:** QuickDiffProvider points at the `cowriting-baseline:` URI;
+- **Implementation:** the QuickDiffProvider points at the `cowriting-baseline:` URI;
the platform renders gutter bars and powers "Open Changes" / `vscode.diff`. No
- custom diff rendering (replaces F6). Honors INV-3, INV-7.
+ custom diff rendering. Honors INV-3, INV-7.
-#### PUC-2 — review and accept/reject proposed changes
+#### PUC-2 — keep/reject the machine's proposed changes
```mermaid
sequenceDiagram
actor U as Writer
- participant R as Proposal/review ctrl
- participant S as "@cline/sdk"
+ participant R as Review ctrl
+ participant S as Machine runtime
participant M as Multi-diff editor
- U->>R: Ask Claude to edit (cowriting.edit)
+ 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 · Accept all / Reject all
+ U->>M: Keep / Undo per change · Keep all / Reject all
M->>R: keep(p)/undo(p)
- R-->>U: document updated only on Keep; attribution recorded
+ R-->>U: document updated only on Keep; authorship recorded
```
- **Implementation:** the machine never edits the document; it emits **pending**
- proposals (INV-5). The multi-diff editor is the review surface; Keep = Accept
- (apply + attribute changed words), Undo = Reject. Accept-all/reject-all carried
- from #46 (descending, orphan-skip + report). **Stable-API boundary:** the
- multi-diff editor is stable; per-change inline **Keep/Undo overlays** as Copilot
- Edits renders them lean on evolving chat-edit APIs — the **stable fallback** is
- per-hunk **CodeLens** Accept/Reject within the diff (§6.10, §13-Q2).
+ 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).
#### PUC-3 — annotated rendered preview
@@ -527,150 +548,138 @@ sequenceDiagram
U->>P: Open Preview (annotations on)
P->>E: render document tokens
E->>T: transform(tokens, attributions, proposals)
- T-->>E: tokens w/ authorship + track-changes marks
+ T-->>E: tokens w/ authorship + change marks
E-->>P: annotated HTML (preview stylesheet)
P-->>U: native preview, annotated
```
- **Implementation:** a contributed `markdown-it` plugin runs the pure
- `renderAnnotations` transform (carried/adapted from F7/F9/F10 render engines) and
- a small preview stylesheet using VS Code theme variables. Toggle via
- `cowriting.annotations`. No webview (replaces F7/F10). Authorship coloring +
- insert/delete marks; pinned-baseline-clean rule carried (#48): a freshly pinned,
- zero-diff document renders clean.
+ `renderAnnotations` transform and a small preview stylesheet using the editor's
+ theme variables. Toggle via `cowriting.annotations`. No webview. Authorship
+ coloring + insert/delete marks; a freshly pinned, zero-diff document renders
+ clean.
#### PUC-4 / PUC-6 — threads & re-anchoring
-- **Implementation:** ThreadController + Anchorer, carried from F2/F8 unchanged.
- On load / external change (FileSystemWatcher), every anchor re-resolves
- (exact-unique → context-disambiguated → orphaned, INV-1). Proposals and
- attributions ride the same `anchors` primitive, so they re-anchor and orphan by
- the same ladder.
+- **Implementation:** ThreadController + Anchorer. On load / external change
+ (FileSystemWatcher), every anchor re-resolves (exact-unique → context-
+ disambiguated → displaced, INV-1). Proposed changes and attributions ride the same
+ `anchors` primitive, so they re-anchor and displace by the same ladder.
-#### PUC-5 — live turn progress
+#### PUC-5 — live machine progress
-- **Implementation:** carried from #60 verbatim — pure `turnProgress` reducer,
- `runEditTurn` `onProgress`/`AbortSignal`, `liveProgress` relay to a notification
- activity-line + token count + the "Cowriting: Claude" OutputChannel; Cancel →
- `agent.abort()`. `CLAUDE_CODE_AUTO_CONNECT_IDE=0` env (the #59 fix) carried.
+- **Implementation:** pure `turnProgress` reducer; `runEditTurn`
+ `onProgress`/`AbortSignal`; `liveProgress` relay to a notification activity-line +
+ token count + an OutputChannel; Cancel → `abort()`. The machine runtime is spawned
+ with IDE auto-connect disabled so it never triggers an OS automation prompt.
### 6.6 Non-functional requirements & cross-cutting concerns
-- **Security & privacy:** serverless; no secret bytes in sidecars or transcripts
- (§6.3-handbook). The machine coauthor runs locally; provenance records identity,
- not credentials. The #59 fix (no spurious macOS automation prompt) is carried via
- `CLAUDE_CODE_AUTO_CONNECT_IDE=0`.
+- **Security & privacy:** serverless; no secret bytes in sidecars or transcripts.
+ The machine runs locally; provenance records identity, not credentials. The
+ runtime is spawned with IDE auto-connect disabled (no spurious OS
+ automation-permission prompt).
- **Performance & scale:** anchor resolution is O(document) text search per anchor
on load/change — fine for human-scale documents; skip non-text/oversized files.
Native diff/preview rendering is the platform's, not ours.
-- **Availability & resilience:** no service; the worst case is an orphaned anchor
+- **Availability & resilience:** no service; the worst case is a displaced anchor
(handled, INV-1) or a missing baseline (degrade to "no diff yet").
- **Observability:** the OutputChannel stream (PUC-5) is the user-facing turn log;
no telemetry service (a local extension — §9).
-- **Accessibility:** inherited from native surfaces — the diff editor, multi-diff,
- Markdown preview, and Comments all carry VS Code's a11y. This is a primary
- *reason* for the native-first choice (the bespoke webviews did not).
+- **Accessibility:** inherited from the native surfaces — the diff editor,
+ multi-diff, 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
| Decision | Chosen | Alternatives considered | Why chosen |
| --- | --- | --- | --- |
-| Diff surface | `QuickDiffProvider` + native diff editor against a `cowriting-baseline:` virtual doc | custom webview diff (F6); decoration-only inline diff (F12) | 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** (operator-chosen), backed by the propose/accept model | SCM stage/unstage change-group; in-editor CodeLens Accept/Reject; bespoke webview ✓/✗ (F4/F10) | Closest to the modern "AI edits" review feel; reviews all changes in one native surface. Stable fallback: per-hunk CodeLens (§6.10) |
-| Rendered preview | **Extend the built-in Markdown preview** (`markdown-it` contribution) | sealed custom webview (F7/F10); both surfaces | Native scroll-sync, theming, refresh, and cooperation with other Markdown extensions; deletes the webview (INV-3) |
-| Baseline storage | out-of-repo global storage by URI hash (carried F6/INV-7) | commit baseline into repo; in-memory only | Never pollutes the repo; works for any file incl. untitled; disposable review state |
-| Persistence envelope | the F5 cross-rung sidecar contract, unchanged | new format; inner-loop-only sidecar | Round-trips to the Gitea substrate; forward-compatible; already proven (INV-4) |
-| Core/controller split | pure vscode-free cores + thin controllers (carried) | UI-coupled logic | The shipped plugin's highest-value discipline: unit-testable cores, swappable surfaces (INV-6) |
-| Scope | design-only north star (no repo/migration commitment) | commit a v2 repo + migration now | Keeps the architecture decision separate from the delivery decision (§2, §7.1) |
+| 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) |
+| 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 storage | out-of-repo global storage by URI hash (INV-7) | commit the baseline into the repo; in-memory only | Never pollutes the repo; works for any file incl. untitled; disposable review state |
+| 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 |
+| 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) |
### 6.8 Testing strategy
-- **Unit (vitest, vscode-free):** the pure cores carry their existing suites —
- attribution spans, anchoring (fingerprint/resolve/orphan/offset shift),
- `mergeArtifacts` (union-by-id, unknown-field preservation), diff engine
- (`wordEditHunks` / `diffToBlockHunks`), `turnProgress` reducer, and the new
- `renderAnnotations` markdown-it transform (token-in → annotated-token-out).
+- **Unit (vitest, vscode-free):** the pure cores — attribution spans, anchoring
+ (fingerprint/resolve/displaced/offset shift), `mergeArtifacts` (union-by-id,
+ unknown-field preservation), the diff engine (`wordEditHunks` /
+ `diffToBlockHunks`), the `turnProgress` reducer, and the `renderAnnotations`
+ markdown-it transform (token-in → annotated-token-out).
- **Integration / E2E (`@vscode/test-electron`):** drive the controllers in the
Extension Development Host — pin baseline → assert QuickDiff original; ask-to-edit
→ assert pending proposals + multi-diff opened; Keep/Undo → assert document
- mutation only on Keep + attribution recorded; toggle annotations → assert preview
- HTML marks; thread create → re-anchor → orphan. Where the harness cannot drive a
- native surface's chrome directly (multi-diff Keep/Undo, Comments UI, preview DOM),
- fall back to asserting controller/provider state and the pure transform output —
- the F2/F6/F7 precedent. **No Playwright** — these are native surfaces, not a
- webview. E2E are first-class plan tasks (handbook §4 / §6.8).
+ mutation only on Keep + authorship recorded; 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
+ 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).
- **Named E2E scenarios this design must pass:** PUC-1 pin→diff; PUC-2
- propose→review→keep/undo→accept-all; PUC-3 annotate-toggle; PUC-4/6
- thread→reanchor→orphan; PUC-5 progress→cancel.
+ propose→review→keep/undo→keep-all; PUC-3 annotate-toggle; PUC-4/6
+ thread→reanchor→displaced; PUC-5 progress→cancel.
### 6.9 Failure modes, rollback & flags
-- **Orphaned anchor** (INV-1) → shown orphaned at last-known `lineHint`, never moved
- → auto-re-anchors if the text reappears → no alert (local).
+- **Displaced anchor** (INV-1) → shown displaced at last-known `lineHint`, never
+ moved → auto-re-anchors if the text reappears → no alert (local).
- **Missing baseline** → no QuickDiff original → degrade to "pin a baseline to see
changes"; no diff shown, no error.
- **Multi-diff Keep/Undo API unavailable/changed** → fall back to per-hunk CodeLens
- Accept/Reject in the diff editor (§6.10); the propose/accept *model* is unchanged.
+ Keep/Reject in the diff editor (§6.10); the proposed-change *model* is unchanged.
- **Feature flags / settings:** `cowriting.annotations` (preview annotations on/off),
- `cowriting.liveProgress.revealOutput` (carried #60). The whole capability is
- additive — off by absence of a pinned baseline / sidecar.
+ `cowriting.liveProgress.revealOutput`. The whole capability is additive — off by
+ absence of a pinned baseline / sidecar.
### 6.10 Migration & compatibility
-**Design-only — nothing is migrated by this spec.** It defines the target
-architecture; the repo-and-migration decision is deferred (§2, §7.1, §13-Q1).
+**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.
-- **Data migration:** none committed here. The persistence envelope is the F5
- cross-rung contract **unchanged**, so existing `.threads/` sidecars are readable
- as-is by a native-first build — making an in-place re-platform low-risk *if*
- later chosen. The baseline (out-of-repo, disposable) needs no migration.
-- **Backwards compatibility:** the sidecar contract stays v1.x additive
- (unknown-field-preserving, INV-4); any current consumer keeps working.
-- **Cutover:** N/A (design-only). If delivered as a v2 re-platform, the natural
- order is surface-by-surface (diff → preview → accept/reject), each native surface
- replacing its bespoke predecessor behind the unchanged cores.
+- **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 as Copilot
- Edits renders it uses **evolving** chat-edit APIs; the design's stable contract is
- therefore "multi-diff review + per-hunk Accept/Reject CodeLens," with the
- Copilot-style inline overlay as a progressive enhancement where the API allows.
+ 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).
## 7. Delivery Plan
### 7.1 Approach / strategy
This is a **design-only north star**: it produces no implementation plan and ships
-no code. When the operator decides to act on it, the delivery decision (§13-Q1)
-picks one of:
-
-- **New app, clean break** — a fresh repo + `app.json` (flotilla provisioning),
- the shipped plugin frozen; build native-first from the reused cores.
-- **v2 re-platform in place** — replace bespoke surfaces one at a time behind the
- unchanged pure cores (diff → preview → accept/reject), reading existing sidecars
- as-is (low-risk per §6.10).
-
-Either way the build order follows the surface mapping: stand up the pure cores
-(mostly carried), then **the diff surface** (smallest, highest-confidence native
-win), then **the preview extension**, then **the multi-diff accept/reject**
-(largest API-boundary risk — do it last, with the CodeLens fallback ready). One
-SPEC → one plan → one execution applies per delivered Feature once decomposed.
+no code. When the operator decides to act on it, the build order follows the surface
+mapping — stand up the pure cores, then **the diff surface** (smallest,
+highest-confidence native win), then **the preview extension**, then **the
+multi-diff accept/reject** (largest API-boundary risk — do it last, with the
+CodeLens fallback ready). Each delivered increment is one SPEC → one plan → one
+execution once decomposed.
### 7.2 Rollout / launch plan
-A VS Code extension has **no §9 webapp deploy pipeline** (localhost/PPE/prod) — like
-the shipped plugin, "done" for a delivered Feature = acceptance met + unit and host
-E2E green; there is no marketplace publish in scope. Rollout, if/when productized,
-is a marketplace concern out of scope here.
+A VS Code extension has **no §9 webapp deploy pipeline** (localhost/PPE/prod) — it is
+not a webapp. "Done" for a delivered increment = acceptance met (§1.9) + unit and
+host E2E green (§6.8). Marketplace packaging/release is out of scope here.
### 7.3 Risks & mitigations
| Risk | Likelihood / impact | Mitigation |
| --- | --- | --- |
-| Per-change Keep/Undo overlay relies on evolving chat-edit APIs | M / M | Stable contract = multi-diff + per-hunk CodeLens Accept/Reject; overlay is progressive enhancement (§6.10) |
-| Two SourceControls (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 |
+| 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) |
+| 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 (F2/F6/F7 precedent) |
+| 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 |
## 8. Success Metrics & Measurement
@@ -679,8 +688,8 @@ is a marketplace concern out of scope here.
| Outcome | Measured by (source) | Baseline captured how | Owner | Review cadence |
| --- | --- | --- | --- | --- |
-| BO-1 (familiarity) | Dogfooding walkthrough: complete diff→review→preview using only native surfaces | The shipped plugin's bespoke-panel flow as the before | Ben | At first native-first build |
-| BO-2 (cheaper to maintain) | Custom-rendering LOC + test-surface retired vs. capability retained | Current webview/decoration LOC count | Ben | At first native-first build |
+| BO-1 (adoption for real docs) | Dogfooding: complete revise→review→keep/reject→preview without leaving the editor or copy-pasting | The copy-paste-from-chat flow as the before | Ben | At first build |
+| BO-2 (feels built-in) | The diff, accept/reject, and preview use surfaces the writer already knows | Bespoke-UI alternatives as the before | Ben | At first build |
### 8.2 Product success metrics
@@ -693,32 +702,32 @@ is a marketplace concern out of scope here.
## 9. Telemetry, Observability & Monitoring
A local VS Code extension — **no telemetry service, no server monitoring.** The
-user-facing turn log is the "Cowriting: Claude" OutputChannel (PUC-5). Deliberately
-omitted: events pipeline, dashboards, alerts, SLOs (nothing is operated as a
-service). N/A — TEL/SLO/ALR rows are not applicable to a serverless local
-extension.
+user-facing turn log is the OutputChannel (PUC-5). Deliberately omitted: events
+pipeline, dashboards, alerts, SLOs (nothing is operated as a service). N/A —
+TEL/SLO/ALR rows are not applicable to a serverless local extension.
## 10. Operations
### 10.1 Operational plan
-N/A — a local VS Code extension has no day-to-day operator. The "operator" is the
-developer running it. No on-call, no runbooks for a service. Routine maintenance is
+N/A — a local VS Code extension has no day-to-day operator; the "operator" is the
+developer running it. No on-call, no service runbooks. Routine maintenance is
dependency patching (§10.3).
### 10.2 Support & incident response
-Pre-v1; issues are filed on the plugin's Gitea tracker as today. One line: a bug
-reaches us as a tracker issue and is triaged in a session.
+Pre-v1; issues are filed on the project tracker and triaged in a session. One line:
+a bug reaches us as a tracker issue.
### 10.3 Maintenance & lifecycle
-- **Dependency patching:** `@cline/sdk` and dev deps via the normal tracker/Patchwatch flow.
+- **Dependency patching:** the machine-coauthor runtime and dev deps via the normal
+ tracker flow.
- **Backup / restore:** N/A — sidecars are git-committed; the baseline is disposable.
-- **Capacity & cost:** local; the only cost is the machine coauthor's token usage,
- surfaced live (PUC-5).
-- **Deprecation / sunset:** if delivered as a new app, the shipped plugin is frozen
- (cutover §6.10); this design itself is superseded only by a later architecture spec.
+- **Capacity & cost:** local; the only cost is the machine's token usage, surfaced
+ live (PUC-5).
+- **Deprecation / sunset:** this design is superseded only by a later architecture
+ spec.
## 11. Documentation plan
@@ -728,17 +737,17 @@ reaches us as a tracker issue and is triaged in a session.
| # | Document | Audience | Location | New / updated |
| --- | --- | --- | --- | --- |
| DOC-1 | This Solution Design | dev/operator | content repo `specs/coauthoring-native-surfaces.md` | new |
-| DOC-2 | README "how the coauthoring loop maps to native surfaces" | dev/user | framework repo README | new (at delivery) |
+| DOC-2 | README "how coediting maps to native editor surfaces" | dev/user | framework repo README | new (at delivery) |
## 12. Traceability matrix
| Pain | Business UC | Product UC | Tests | Measured by |
| --- | --- | --- | --- | --- |
-| PP-1, PP-3 | BUC-2 | PUC-2 | host E2E: propose→review→keep/undo→accept-all | PM-2 / BO-1 |
| PP-1 | BUC-1 | PUC-1 | host E2E: pin→QuickDiff→native diff | PM-1 / BO-1 |
-| PP-1, PP-4 | BUC-3 | PUC-3 | unit (renderAnnotations) + host E2E: annotate toggle | PM-3 / BO-1 |
-| PP-2 | — | PUC-1/2/3 | retired custom-rendering LOC | BO-2 |
-| (orphaning) | BUC-3a | PUC-4/6 | unit (anchorer) + host E2E: reanchor→orphan | PM-1 |
+| PP-2 | BUC-2 | PUC-2 | host E2E: propose→review→keep/undo→keep-all | PM-2 / BO-1 |
+| PP-3, PP-4 | BUC-3 | PUC-3 | unit (renderAnnotations) + host E2E: annotate toggle | PM-3 / BO-1 |
+| PP-5 | BUC-4 | PUC-5 | host E2E: progress→cancel | BO-1 |
+| (displacement) | BUC-3a | PUC-4/6 | unit (anchorer) + host E2E: reanchor→displaced | PM-1 |
## 13. Open Questions & Decisions log
@@ -746,7 +755,7 @@ reaches us as a tracker issue and is triaged in a session.
| # | Question | Owner | Blocks |
| --- | --- | --- | --- |
-| Q1 | New app (clean break) vs. v2 re-platform in the current repo — the delivery decision | Ben | Any implementation plan |
+| 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) |
| Q3 | One "Cowriting" SourceControl vs. QuickDiff-only (no full SCM viewlet entry) — how much SCM presence is wanted | Ben | PUC-1 build |
| Q4 | Does any need genuinely require a webview the native surfaces can't serve (INV-3 escape hatch)? | Ben | Per-surface build |
@@ -755,34 +764,36 @@ reaches us as a tracker issue and is triaged in a session.
| # | Decision | Resolution | Date |
| --- | --- | --- | --- |
-| D1 | Scope / altitude | Design-only north star — architecture + surface mapping, no repo/migration commitment | 2026-06-27 |
+| 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 |
| 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 | Carry-forward from the shipped plugin | Pinned baseline (F6), authorship attribution (F3), cross-rung sidecar (F5), live progress (#60), region threads (F2), on-behalf-of identity, and the pure-core/thin-controller discipline | 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 |
| D6 | Design tooling | Code directly (rubric −6); no bespoke UI surface | 2026-06-27 |
## 14. Glossary & References
-- **Pinned baseline** — the human-chosen "good" snapshot used as the diff *original*;
- stored out-of-repo, disposable (INV-7; from F6).
+- **Pinned baseline** — the writer-chosen "good" snapshot used as the diff *original*;
+ stored out-of-repo, disposable (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 (the "AI edits"
- review feel); here the accept/reject surface (Keep/Undo).
-- **Cross-rung sidecar** — the F5 forward-compatible, unknown-field-preserving
- coauthoring artifact that round-trips between the inner loop and the Gitea
- substrate.
-- **Provenance** — the author envelope (human git identity, or agent on-behalf-of a
- human) reused across attributions, proposals, and threads (INV-8).
-- **Orphaned** — an anchor that could not be confidently re-resolved; shown, never
+- **Multi-diff editor** — VS Code's native multi-change review surface; here the
+ accept/reject surface (Keep/Undo).
+- **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.
+- **Provenance** — the author envelope (the human's git identity, or the machine
+ acting on-behalf-of a human) reused across attributions, proposed changes, and
+ threads (INV-8).
+- **Displaced** — an anchor that could not be confidently re-resolved; shown, never
silently moved (INV-1).
+- **Decision unit vs attribution unit** — a block (paragraph/header/list-item/fence)
+ is what the writer keeps or rejects; the word diff inside it is what carries
+ authorship.
- **Pure core / thin controller** — the vscode-free, unit-tested logic vs. the
vscode-aware wiring split (INV-6).
-- **References:** shipped plugin Epic #1 (F1–F12); specs `coauthoring-inner-loop.md`,
- `coauthoring-attribution.md`, `coauthoring-propose-accept.md`,
- `coauthoring-diff-view.md`, `coauthoring-rendered-preview.md`,
- `coauthoring-cross-rung-format.md`, `coauthoring-live-progress.md`. `@cline/sdk`
- (loaded via runtime dynamic `import()`). VS Code APIs: SCM/`QuickDiffProvider`,
- diff editor (`vscode.diff`), multi-diff editor, Markdown preview
- `markdown-it` contribution, Comments, `TextDocumentContentProvider`, decorations.
+- **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
+ coauthoring work, reused here on their own merits): baseline pinning, char-precise
+ attribution, the portable coauthoring sidecar, and live turn progress.*