F4 — Propose/accept diff flow: inline diff + keep/reject on the applyAgentEdit seam #12
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Propose/accept diff flow (propose-by-default): Claude's edits arrive as
proposals — rendered as an inline diff in the buffer — that the human
explicitly keeps or rejects. Accepting applies the edit through the
established
applyAgentEditseam, so accepted spans land Claude-attributed inthe F3 attribution substrate; rejecting leaves the document untouched. Pending
proposals persist git-natively in the sidecar's reserved
proposals[]extensionpoint (reusing the shared
anchors/provenanceprimitives from F2/F3).Problem / pain
F3's machine-edit ingress applies edits directly: the moment a live Claude
turn completes, its text is already in the document, and the human reviews
after the fact via the attribution tint. There is no ratify-before-landing
gesture — no inline diff to inspect, no keep/reject decision point — so the
human's only recourse to an unwanted machine edit is undo. The epic's third
acceptance pillar — "accept / reject Claude's proposed diffs
(propose-by-default)" — is unmet, and the trust model is backwards: the machine
writes first and the human objects later, instead of the machine proposing and
the human deciding.
Who feels it
A writer/engineer coauthoring prose or specs with Claude inside VS Code — one
human + one machine — who wants to see exactly what the machine intends to
change before it becomes part of the document, and to keep or reject each
proposal with one gesture without losing the provenance record either way.
Desired outcome / value
Machine edits become propose-by-default: a live Claude turn produces a
visible inline diff (current text vs. proposed replacement) instead of a silent
buffer mutation; the human accepts (the edit applies through the seam and its
spans render Claude-attributed, exactly as F3 established) or rejects (the
buffer is untouched). Pending proposals are first-class, git-native state — they
survive save/reopen and re-anchor across edits like threads and attributions do.
This delivers the epic's third acceptance pillar, completes the inner-loop triad
(threads · attribution · propose/accept), and gives F5 a complete provenance
story to carry up the ladder (proposed → ratified → attributed).
Acceptance — "done right"
direct buffer mutation: the affected range and proposed replacement are
rendered as an inline diff in the buffer, visibly distinct from both plain
text and accepted Claude-attributed spans.
applyAgentEdit(the single machine-edit ingress, F3 INV-9), and theresulting spans are Claude-attributed in the existing attribution substrate —
acceptance is the only path by which proposed text enters the document.
pending diff disappears cleanly.
the workspace restores them from the sidecar's
proposals[](
schemaVersionhonored, no format break — spec INV-4).text changes; a proposal whose anchor can't be confidently re-resolved is
orphaned, never silently moved or applied (F2's INV-1 ladder applies).
the persistence round-trip; host E2E covers propose → render → accept →
attributed → reload, propose → reject → untouched, and propose → reopen →
still pending, per the F2/F3 test pattern (no LLM in CI — the seam and a
programmatic proposal ingress drive the tests).
Scope / non-goals
In scope: the
proposals[]shape on the sharedanchors/provenanceprimitives; turning the machine-edit flow propose-by-default; inline diff
rendering of pending proposals in the buffer; the keep/reject UX; apply-on-accept
through the
applyAgentEditseam (growing the seam's single-range contract tomulti-range if the design requires it — the F3 spec reserves exactly this);
persistence + reload + re-anchor/orphan of pending proposals.
Out of scope (deferred): F5 cross-rung persistence format + Gitea round-trip
(#46); hunk-level / partial acceptance beyond whatever granularity the design
settles (per-proposal accept/reject is the floor); chat-panel UX; multi-file
orchestration; turn-taking choreography; attribution of out-of-session edits;
any server.
Assumptions · constraints · dependencies
PR #7) and #4 (F2 — shipped; PR #5). Feeds: F5 (cross-rung format carries
the full proposed→ratified→attributed provenance story up the ladder).
points, it does not re-litigate them:
specs/coauthoring-inner-loop.md§6.3 reservesproposals[]in the sidecar("F4 extension point — reuses
anchors[]+ provenance") and §9 routes theexact shape "OPEN → F3/F4" (F3 resolved
attributions[];proposals[]isF4's half).
specs/coauthoring-attribution.md§9: "OPEN → F4: propose/accept drivesthe same seam; accepted diffs render into this attribution substrate. The
seam's contract (single-range apply) may grow multi-range then."
applyAgentEdit(doc, range, newText, provenance, {expectedVersion?, turnId?})is the ONLY machine-edit ingress (F3 INV-9). F4drives it on accept; it self-minimizes and attributes the full intended
replacement (intent-honest, ratified).
session; this Feature consumes its reviewed Solution Design: (a) the exact
proposals[]shape (pending vs. resolved records; is a rejected proposalkept as history or dropped?); (b) the inline-diff rendering primitive
(decorations-based ghost text/strikethrough vs. VS Code's diff editor vs.
CodeLens/Comments-style action surface — F2's Comments API and F3's
Decorations API are the established in-buffer surfaces); (c) whether the F3
edit-selection command flips wholesale to propose-by-default or keeps a
direct-apply mode behind a setting; (d) whether the seam grows multi-range
now (a turn proposing several disjoint edits) or stays single-range with one
proposal per range; (e) accept/reject granularity (whole-proposal floor
vs. per-hunk).
@cline/sdkAgenton theclaude-codeprovider, zero extension-held credentials;
result.runIdis the provenancesessionId. F4 adds no new auth surface.
Source / signal
Epic #1 (
coauthoring-plugin-on-cline) decomposition item F4 — Propose/acceptdiff flow (inline-diff keep/reject); the graduated Solution Designs
vscode-cowriting-plugin-content/specs/coauthoring-inner-loop.md(§6.3 reservedproposals[], §9 OPEN→F3/F4) andvscode-cowriting-plugin-content/specs/coauthoring-attribution.md(§9 OPEN→F4);architecture lineage
ben.stull/rfc-app#48. Builds on F3 shipped in #6 (PR #7,session
vscode-cowriting-plugin-0007) and F2 shipped in #4 (PR #5). Captured insession
vscode-cowriting-plugin-0010.Priority (WSJF)
Provisional: Value high (the epic's third acceptance pillar;
propose-by-default is the core trust gesture of the coauthoring surface) ·
Time-criticality medium · Opportunity-enablement medium-high (completes
the inner-loop triad; F5 needs the full provenance story to carry up the ladder)
÷ Size medium → P1.
Decomposition (Feature → slice checklist)
propose-by-default vs. retained direct mode; multi-range seam growth;
accept/reject granularity) — consume the follow-on Solution Design.
proposals[]data model on the sharedanchors/provenanceprimitives(schema extension, vscode-free, round-trip tests — per the F2/F3 SLICE-1
pattern).
a pending proposal instead of a direct apply.
applyAgentEdit(Claude-attributed spans land exactly as F3 established).
existing Store/Anchorer.
(propose → accept → attributed → reload; reject → untouched; pending survives
reopen), no LLM in CI.
Solution notes (optional — NOT a design)
The reuse points are all in place: the sidecar's reserved
proposals[], theshared
anchors/provenanceprimitives,CoauthorStore.update(all sidecarwriters go through it), the Anchorer's resolve/orphan ladder, and the
applyAgentEditseam as the apply path on accept. Rendering candidates worthweighing in the design session: decoration-based inline diff (strikethrough old
(heavyweight, leaves the buffer), or a Comments-API action thread on the
proposal's anchor (the F2 pattern — keep/reject as thread actions). Host-only
traps F3 logged (change events before dirty-flag flip; WorkspaceEdit
diff-minimization) apply unchanged to F4's E2E. All non-binding — the forks
above are the design session's to resolve.