F4: document propose/accept — manual smoke, README, playground loop (#12)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ben Stull
2026-06-10 22:14:13 -07:00
parent 8fdea97d36
commit 169abf06c8
4 changed files with 58 additions and 7 deletions
+26 -2
View File
@@ -12,7 +12,8 @@ catalog (a pure, key-free SDK call) in a notification and the
"Cowriting (Cline SDK)" output channel. "Cowriting (Cline SDK)" output channel.
Features shipped so far: F2 region-anchored threads (Feature #4), F3 live Features shipped so far: F2 region-anchored threads (Feature #4), F3 live
human/Claude attribution (Feature #6). human/Claude attribution (Feature #6), F4 propose/accept diff flow
(Feature #12).
## Architecture ## Architecture
@@ -67,13 +68,36 @@ Attribution" output channel) rather than silently moved or discarded.
- **`Cowriting: Ask Claude to Edit Selection`** — select text → enter an - **`Cowriting: Ask Claude to Edit Selection`** — select text → enter an
instruction → a live `@cline/sdk` turn runs on the built-in `claude-code` instruction → a live `@cline/sdk` turn runs on the built-in `claude-code`
provider (rides your local Claude Code Pro/Max login; the extension stores no provider (rides your local Claude Code Pro/Max login; the extension stores no
credentials). The replacement lands in the buffer as a Claude-attributed span. credentials). As of F4 the turn ends in a **proposal** (see below); accepted
text lands as a Claude-attributed span.
- **`Cowriting: Toggle Attribution`** — show/hide attribution decorations. - **`Cowriting: Toggle Attribution`** — show/hide attribution decorations.
- `cowriting.applyAgentEdit` _(palette-hidden)_ — the single machine-edit - `cowriting.applyAgentEdit` _(palette-hidden)_ — the single machine-edit
ingress seam. Tests drive this directly so CI requires no LLM. ingress seam. Tests drive this directly so CI requires no LLM.
Design: `vscode-cowriting-plugin-content/specs/coauthoring-attribution.md`. Design: `vscode-cowriting-plugin-content/specs/coauthoring-attribution.md`.
## F4 — Propose/accept diff flow (Feature #12)
Claude's edits arrive as **pending proposals** — propose-by-default, the
document never changes without your say-so. A proposal renders two ways at
once: an **amber tint** on the target range, and a "Claude proposes" comment
thread showing a fenced `diff` of current → proposed text with two actions:
- **✓ Accept Proposal** — applies the replacement through the `applyAgentEdit`
seam, so it lands Claude-attributed (F3) — and the proposal disappears.
- **✗ Reject Proposal** — the document is untouched; the proposal disappears.
Pending proposals persist git-natively in the same sidecar (`proposals[]`,
sharing the F2/F3 `anchors` fingerprints), survive reload, and re-anchor as
surrounding text changes. If the **target text itself** changes, the proposal
goes **stale** (status-bar count, accept disabled — never applied by guess);
undo the change and it becomes decidable again. `cowriting.proposeAgentEdit`
_(palette-hidden)_ is the programmatic propose ingress E2E drives — no LLM in
CI.
Design: `vscode-cowriting-plugin-content/specs/coauthoring-propose-accept.md`.
Live smoke: [`docs/MANUAL-SMOKE-F4.md`](docs/MANUAL-SMOKE-F4.md).
## Develop ## Develop
- `npm run watch` — rebuild on change. - `npm run watch` — rebuild on change.
+8 -3
View File
@@ -1,5 +1,10 @@
# F3 manual smoke — live `claude-code` turn (spec §6.8) # F3 manual smoke — live `claude-code` turn (spec §6.8)
> **As of F4 (#12)** the edit-selection turn ends in a **proposal** — the
> direct-apply behavior in §2 step 4 now happens on **✓ Accept Proposal**. The
> full propose→accept smoke is [`MANUAL-SMOKE-F4.md`](./MANUAL-SMOKE-F4.md);
> §1's scripted smoke is unchanged (it drives the SDK turn only, no editor).
The live turn is deliberately NOT in CI (unit + host E2E drive the seam). It The live turn is deliberately NOT in CI (unit + host E2E drive the seam). It
gets this documented smoke, run once per machine that has Claude Code gets this documented smoke, run once per machine that has Claude Code
installed and signed in (Pro/Max). The extension itself holds no credentials installed and signed in (Pro/Max). The extension itself holds no credentials
@@ -37,9 +42,9 @@ the model id, a non-empty `sessionId`, and exits 0.
with a clear error (`runEditTurn` throws on any non-`completed` run with a clear error (`runEditTurn` throws on any non-`completed` run
status); in-editor, the command shows an error notification and NO edit is status); in-editor, the command shows an error notification and NO edit is
applied. applied.
- Buffer edited mid-turn: start an edit-selection turn, type elsewhere in the - Buffer edited mid-turn: superseded by F4 — the turn ends in a proposal, so a
document before it completes → warning notification "document changed", no mid-turn edit can't race an application; the proposal simply renders where
partial application (stale `expectedVersion`, spec §6.9). its target re-resolves (or stale if the target itself changed — F4 INV-11).
## Smoke log ## Smoke log
+20
View File
@@ -0,0 +1,20 @@
# Manual smoke — F4 propose/accept (live turn)
Pre-req: Claude Code installed + signed in (the `claude-code` provider rides
that login — the extension holds no credentials, INV-8). Run on a real machine,
not CI.
1. `npm run build`, then F5 (the EDH opens the committed `sandbox/` playground).
2. Open `playground.md`, select a sentence → right-click → **Ask Claude to Edit
Selection** → give an instruction.
3. ✅ A **proposal** appears: the selection gets an amber tint and a "Claude
proposes" comment thread shows a `diff` of current → proposed. **The document
text is unchanged** (INV-10).
4. Click **✓ Accept Proposal** in the thread title. ✅ The replacement lands and
renders Claude-tinted (the F3 attribution substrate); the proposal disappears.
5. Repeat with another selection and click **✗ Reject Proposal**. ✅ The document
is untouched; the proposal disappears.
6. Propose again, then save and reload the window. ✅ The pending proposal is
restored at its re-resolved range.
7. Failure path: edit the proposed-on text, then try Accept. ✅ Refused with a
warning ("target text changed"); undo your edit → Accept works.
+4 -2
View File
@@ -8,8 +8,10 @@ open in your dev window (#8).
Play here: Play here:
1. Type a sentence below — it renders with the human left-border as you type. 1. Type a sentence below — it renders with the human left-border as you type.
2. Highlight it → right-click → **Ask Claude to Edit Selection**the 2. Highlight it → right-click → **Ask Claude to Edit Selection**Claude
replacement lands Claude-tinted. **proposes** a diff (amber tint + a "Claude proposes" thread); click
**✓ Accept Proposal** and it lands Claude-tinted, or **✗ Reject Proposal**
to discard.
3. Edit inside the tinted span — it splits character-precisely. 3. Edit inside the tinted span — it splits character-precisely.
4. Save, then peek at `sandbox/.threads/playground.md.json` — the git-native 4. Save, then peek at `sandbox/.threads/playground.md.json` — the git-native
attribution record. Reopen the file: spans re-resolve. attribution record. Reopen the file: spans re-resolve.