Files
vscode-cowriting-plugin-con…/specs/coauthoring-native-surfaces.md
T
BenStullsBets 4ccba9ea93 spec(0062): baseline = clean checkpoint set on entry; only empty state is an empty doc (D13)
Entering coediting sets the baseline to current content automatically;
'Pin Review Baseline to Now' resets it ('everything so far is settled; show
changes from here'). A coedited doc always has a baseline -> removed the
'no/missing baseline' empty state entirely; the only empty state is a
genuinely empty document. Updated INV-7, PUC-1/7, baseline provider, preview
(marks since checkpoint), failure modes, flags, glossary.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 07:13:22 -07:00

975 lines
58 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
status: draft
---
# Solution Design: Coediting Markdown with a Machine — a Native VS Code Experience
| | |
| --- | --- |
| **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. |
**Change log**
| Date | Version | Change | By |
| --- | --- | --- | --- |
| 2026-06-27 | v0.1.0 | Initial draft — brainstorming session 0062 | Ben Stull + Claude |
---
## 1. Business Context
*The business lens — solution-agnostic throughout. No mechanism is proposed until §2.*
### 1.1 Executive Summary
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
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.
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) |
| --- | --- |
| **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
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 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 | 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:** 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:** 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 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 — 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 to me as a clear before/after I can read
```
- **BUC-1 acceptance criteria:** the writer sees an accurate before/after of the
document against the chosen point.
**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 — 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
```
- **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-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 — 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 annotations off to read it clean
```
- **BUC-3 acceptance criteria:** the rendered document shows changes and authorship
when annotations are on, renders clean when off.
**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-5 — As a writer, I can leave a remark on a passage of a document I'm coediting
and have the machine respond and offer to make the change, so that a comment can
become an edit without me leaving the document.**
```gherkin
Scenario: BUC-5 — a comment summons a response and an offer to edit
Given I have invited the machine to coedit this document
When I leave a comment on a passage
Then the machine replies to my comment
And it offers to make the change I described
And if I accept, its edit arrives as a proposed change I review like any other
```
- **BUC-5 acceptance criteria:** on an invited document a comment elicits a machine
reply and an offer; accepting produces a reviewable proposed change (it never
edits directly); on a *non-invited* document nothing is summoned.
**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 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 enabling idea: **model the coauthored document as a tiny source control.** The
point the writer last marked "good" (the **baseline checkpoint**, set on entering
coediting and re-pinnable) 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:
| 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) |
| 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 |
**Coediting is something a document opts into.** A writer turns a document into a
coedited document with one command — **"Coedit this Document with Claude"** — and
**only then** do the surfaces above attach to it: the SourceControl/QuickDiff entry,
the auto-responding comments, the annotation toggle, and the machine-edit entry
points. The extension registers its providers once but **scopes every surface to the
active coediting set** (INV-10), so it never hijacks Comments, Source Control, or the
preview for the writer's *other* markdown files. This gate is what makes the
auto-responding-comments behavior safe: a comment only summons the machine on a
document the writer has explicitly invited it into.
**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 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 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.
---
## 3. Product Personas
| Product persona | In the plugin | Maps to business role(s) |
| --- | --- | --- |
| **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 baseline checkpoint (realizes BUC-1)
Given I am coediting a document (entering set the baseline to a clean checkpoint)
When I (or the machine) change the document
Then the source-control gutter shows change bars for what changed since the checkpoint
And "Open Changes" opens the native diff editor (baseline live), inline or side-by-side at my choice
And running "Pin Review Baseline to Now" resets the checkpoint to the current content (the diff goes clean)
```
```gherkin
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
And on keep, the machine's words are attributed to the machine and my tweaks to me
```
```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 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)
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 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 an OutputChannel streams the full text
And I can cancel the turn
```
```gherkin
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, proposed changes, attributions, and threads re-resolve against current text
And anything that cannot be confidently placed is shown displaced
```
```gherkin
Scenario: PUC-7 — enter (and leave) coediting mode (realizes INV-10)
Given an ordinary markdown document
When I run "Coedit this Document with Claude" (command, editor title, or context menu)
Then this document gains the Cowriting source-control entry, the annotation toggle, and machine-edit/comment-response behavior
And the baseline is set to the document's current content (a clean checkpoint)
And my other markdown documents are unaffected
And running "Stop editing with Claude" (context menu) removes those surfaces (the saved sidecar is kept for next time)
```
```gherkin
Scenario: PUC-8 — comment summons a machine reply + offer to edit (realizes BUC-5)
Given I am coediting this document
When I add a comment on a passage (native Comments gutter)
Then a machine turn runs (with live progress, PUC-5) and replies in the thread
And it offers to make the change; accepting opens its edit as a pending proposed change (PUC-2)
And on a document I am not coediting, adding a comment summons nothing
```
## 5. UX Layout
The experience is delivered through **native VS Code surfaces**; there is no bespoke
panel to wireframe. Described in text (source of truth):
- **Entering coediting.** A **"Coedit this Document with Claude"** command (command
palette, editor title menu, and editor/explorer context menu, markdown-gated)
turns the active document into a coedited document; a **"Stop editing with Claude"**
command — in the same command palette **and the editor/explorer context menu**
reverses it. A subtle status-bar item ("✦ Coediting") indicates when the active
document is in coediting mode. Until a document is entered, **none** of the
surfaces below attach to it (INV-10). **Entering also sets the baseline to the
document's current content** — a clean checkpoint to measure future changes
against (INV-7).
- **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 **Keep all / Reject all**. This is the accept/reject surface.
- **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
and offers an edit (BUC-5 / PUC-8); the offer, when accepted, becomes a pending
proposed change reviewed in the multi-diff.
- **Notification + OutputChannel.** Live machine progress, with a Cancel action.
### 5.1 Design tooling decision
- **Decision:** **Code directly.**
- **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 (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 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 **displaced** state surfaced in the thread / proposed-change listing.
## 6. Technical Design
### 6.1 Invariants
- **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 — 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 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 is the clean checkpoint, kept outside the repo:** the baseline
is the diff *original* — the last point the writer declared "clean." It is **set
automatically when a document enters coediting**, and **reset to the current
content by "Pin Review Baseline to Now"** (the "everything so far is settled; show
me changes from here" gesture). A coedited document therefore *always* has a
baseline — there is **no "missing baseline" state**; the only empty state is a
genuinely empty document. It is stored in extension global storage keyed by
document URI hash, so it never pollutes the repo and works for any file (incl.
untitled), and it is disposable review state, not durable metadata.
- **INV-8 — provenance carries on-behalf-of identity:** every authored span /
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.
- **INV-10 — coediting is opt-in per document:** every coauthoring surface (the
"Cowriting" SourceControl + QuickDiff, auto-responding comments, preview
annotations, the machine-edit entry points) activates **only** for a document the
writer has explicitly entered into coediting mode ("Coedit this Document with
Claude"). The extension registers its providers globally but **gates every
surface on membership in the active coediting set**, so it never hijacks
Comments/SCM/preview for documents the writer did not opt in. Exiting coediting
(or closing the workspace) removes the document from the set; the durable sidecar
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.
- **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).
### 6.2 High-level architecture
**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<br/>char-precise spans]
AN[Anchorer<br/>fingerprint + offsets + displaced]
MG[mergeArtifacts<br/>union-by-id, unknown-field-safe]
DF[Diff/hunk engine<br/>word- & block-level]
TP[turnProgress<br/>reducer]
RA[renderAnnotations<br/>markdown-it transform]
end
subgraph Ctrls["Thin controllers (vscode-aware)"]
SC[SourceControl + QuickDiffProvider]
BP[Baseline provider<br/>TextDocumentContentProvider]
PR[Review controller<br/>multi-diff Keep/Undo]
PV[Preview extension<br/>extendMarkdownIt]
TC[ThreadController<br/>Comments API]
LP[liveProgress relay]
ST[Sidecar router + store]
end
subgraph Native["Native VS Code surfaces"]
GUT[Gutter dirty-diff + SCM viewlet]
DIFF[Native diff editor]
MDIFF[Multi-diff review + Keep/Undo]
MDPREV[Built-in Markdown preview]
COMM[Comments gutter/peek]
NOTE[Notification + OutputChannel]
end
SDK[[Machine-coauthor runtime<br/>dynamic import]]
AN --> SC --> GUT
BP --> DIFF
DF --> PR --> MDIFF
AT --> RA --> PV --> MDPREV
AN --> TC --> COMM
TP --> LP --> NOTE
SDK --> PR
Cores --- ST
Ctrls --- ST
```
- **SourceControl + QuickDiffProvider** — owns the "Cowriting" source control and
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 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.
- **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/<path>.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/machine), `id`, `email`, `onBehalfOf`, `agent{runtime,model,sessionId}` | embedded in sidecar (INV-8) |
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 coauthoring envelope (INV-4)
{
"schemaVersion": 1,
"document": { "path": "docs/spec.md" },
"anchors": { "a1": { "fingerprint": { "text": "…", "before": "…", "after": "…", "lineHint": 42 } } },
"attributions": [ { "anchorId": "a1", "provenance": <Provenance>, "span": { "start": 0, "end": 12 } } ],
"proposals": [ { "id": "p1", "anchorId": "a1", "status": "pending", // pending|accepted|rejected (INV-5)
"granularity": "block", // word|block — the decision unit
"provenance": <Provenance>, "before": "…", "after": "…" } ],
"threads": [ { "id": "t1", "anchorId": "a1", "status": "open", "messages": [ ] } ]
// unknown top-level fields preserved verbatim (INV-4)
}
```
### 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). `pin` is
called **automatically on `enterCoediting`** (reason `entered`) and on the explicit
**"Pin Review Baseline to Now"** (reason `pinned`); a coedited document always has a
baseline, so `get` returns null only for documents that are not being coedited.
- **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). 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).
- **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** — `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.
- **CoeditingRegistry** (the activation gate, INV-10) — `enter(docUri)` /
`exit(docUri)` / `isCoediting(docUri) → boolean` / `onDidChange`. Holds the active
coediting set (persisted across reloads). **Every** controller above checks
`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
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
machine `Comment` carrying `onBehalfOf` provenance (INV-8).
### 6.5 PerProduct-Use-Case design
#### PUC-1 — see the diff against the baseline checkpoint
```mermaid
sequenceDiagram
actor U as Writer
participant C as SCM/QuickDiff ctrl
participant B as Baseline provider
participant V as Native diff editor
U->>C: Pin baseline
C->>B: BaselineStore.pin(docUri, text)
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:** the QuickDiffProvider points at the `cowriting-baseline:` URI;
the platform renders gutter bars and powers "Open Changes" / `vscode.diff`. No
custom diff rendering. Honors INV-3, INV-7.
#### PUC-2 — keep/reject the machine's proposed changes
```mermaid
sequenceDiagram
actor U as Writer
participant R as Review ctrl
participant S as Machine runtime
participant M as Multi-diff editor
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
```
- **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.
#### PUC-3 — annotated rendered preview
```mermaid
sequenceDiagram
actor U as Writer
participant P as Built-in MD preview
participant E as Preview extension (extendMarkdownIt)
participant T as renderAnnotations (pure)
U->>P: Open Preview (annotations on)
P->>E: render document tokens
E->>T: transform(tokens, attributions, proposals)
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 and a small preview stylesheet using the editor's
theme variables. Toggle via `cowriting.annotations`. No webview. Authorship
coloring + insert/delete marks are shown for changes since the baseline checkpoint;
a freshly entered or freshly pinned, zero-diff document renders clean.
#### PUC-4 / PUC-6 — threads & re-anchoring
- **Implementation:** ThreadController + Anchorer, **gated on `isCoediting(docUri)`**
(INV-10). 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-7 — enter / leave coediting
- **Implementation:** `cowriting.enterCoediting(docUri)` adds the URI to the
CoeditingRegistry's active set and persists it; `cowriting.exitCoediting` removes
it (the sidecar is kept). The registry fires `onDidChange`; controllers (QuickDiff,
comment-response, annotations, edit commands) attach/detach and the
`cowriting.isCoediting` context key flips so menu `when` clauses show/hide. Commands
are markdown-gated. No surface exists for a document until it is entered.
#### PUC-8 — comment summons a machine reply + offer
```mermaid
sequenceDiagram
actor U as Writer
participant T as ThreadController
participant G as CoeditingRegistry
participant S as Machine runtime
participant R as Review ctrl
U->>T: add comment on a passage
T->>G: isCoediting(docUri)?
G-->>T: yes
T->>S: runEditTurn(comment + context, {onProgress, signal})
S-->>T: reply text (+ optional edit)
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)
```
- **Implementation:** gated on `isCoediting` (INV-10) — inert otherwise. The turn
runs with live progress (PUC-5). The reply is a machine `Comment`; an accepted
offer never edits the document directly, only proposes (INV-5). **While the
document is in coediting mode, every new comment fires a turn (D10)** — the opt-in
mode is the control, so there is no per-comment gate; "Stop editing with Claude"
is the off switch.
#### PUC-5 — live machine progress
- **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.
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 a displaced anchor
(handled, INV-1); a baseline always exists once coediting is entered (INV-7), so
there is no missing-baseline path.
- **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.
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 | 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 |
| 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 |
| 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 |
| 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 |
| 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 — 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 + 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→keep-all; PUC-3 annotate-toggle; PUC-4/6
thread→reanchor→displaced; PUC-5 progress→cancel.
### 6.9 Failure modes, rollback & flags
- **Displaced anchor** (INV-1) → shown displaced at last-known `lineHint`, never
moved → auto-re-anchors if the text reappears → no alert (local).
- **Empty document** (genuinely no content) → the only true empty state → a gentle
"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.
- **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.
- **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).
## 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 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) — 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 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 |
| Design-only spec stalls (never delivered) | M / L | §13-Q1 names the delivery decision and owner; the architecture stands regardless |
## 8. Success Metrics & Measurement
### 8.1 Business success metrics
| Outcome | Measured by (source) | Baseline captured how | Owner | Review cadence |
| --- | --- | --- | --- | --- |
| 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
| # | 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-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
A local VS Code extension — **no telemetry service, no server monitoring.** The
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 service runbooks. Routine maintenance is
dependency patching (§10.3).
### 10.2 Support & incident response
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:** 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's token usage, surfaced
live (PUC-5).
- **Deprecation / sunset:** this design is superseded only by a later architecture
spec.
## 11. Documentation plan
- **Where docs live:** spec + dev docs → framework repo; this Solution Design →
content repo `specs/`; no public-facing site in scope.
| # | Document | Audience | Location | New / updated |
| --- | --- | --- | --- | --- |
| DOC-1 | This Solution Design | dev/operator | content repo `specs/coauthoring-native-surfaces.md` | new |
| 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 | BUC-1 | PUC-1 | host E2E: pin→QuickDiff→native diff | PM-1 / BO-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 |
| PP-1 (comment→edit) | BUC-5 | PUC-8 | host E2E: comment→reply→offer→pending proposal | BO-1 |
| (no hijack) | — | PUC-7 | host E2E: enter/exit gates every surface; non-coedited doc inert | BO-2 |
## 13. Open Questions & Decisions log
**Open**
| # | 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) |
| 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 |
| 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 |
**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 |
| 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 |
| D6 | Design tooling | Code directly (rubric 6); no bespoke UI surface | 2026-06-27 |
| D7 | Coediting is opt-in per document | A document enters coediting via "Coedit this Document with Claude"; every surface gates on the active coediting set (INV-10), so other markdown files are never hijacked | 2026-06-27 |
| 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 |
| D13 | Baseline semantics | Baseline = the clean checkpoint; set automatically on entering coediting and reset by "Pin Review Baseline to Now"; a coedited doc always has one. The only empty state is a genuinely empty document — no "missing baseline" state (INV-7) | 2026-06-27 |
## 14. Glossary & References
- **Baseline (checkpoint)** — the last point the writer declared "clean," used as the
diff *original*; set automatically when a document enters coediting and reset by
"Pin Review Baseline to Now"; 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; 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:** 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, live turn progress, and editable
proposals (tweak-before-keep with mixed human/machine authorship).*