Task 9 of the native-surfaces migration plan. fullLoop.test.ts walks the
whole inner loop in one doc, stub-turned: enter (snapshot baseline) -> two
comment asks -> reply -> offer -> pending proposals (F12 optimistic-apply,
decorated + isApplied) -> human tweak inside a pending proposal's range
(INV-11: breaks the raw fingerprint anchor, but finalizeInPlace/"Keep" is
built to survive that) -> keep one (attribution split via spansFor: claude's
words + the human tweak, char-honest) -> reject the other (buffer text
returns EXACTLY to its pre-proposal text, INV-5) -> markReviewed -> change
count 0.
scripts/smoke-native-loop.mjs + test/e2e/smoke/nativeLoop.ts are the rung-3
manual gate: launches the real EDH against sandbox/, posts a real comment via
ThreadController.createThreadOnSelection with BOTH turns left unstubbed (the
real @cline/sdk, INV-8), waits for the reply/offer, makes + accepts the edit,
and prints the created proposal ids. Lives outside test/e2e/suite so it is
never swept into `npm run test:e2e`; `npm run smoke:native` builds +
compiles then runs it. Operator-run only (real credentials) -- not executed
here, verified by typecheck + a syntax/structural pass only.
300 unit + 86 workspace/5 no-workspace host E2E green (1 pending, pre-existing
#54 undo-suite skip).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Code review of 705de31's mermaid-diff wiring: buildMermaidQueue's overrides
were consumed positionally per rendered mermaid fence, so a fence the queue
didn't know about (a pending proposal's optimistic apply inserting a whole
new mermaid block, or annotateSource's committed-deletion reinsert) shifted
every later override by one and substituted the wrong diagram's augmented
source instead of the documented verbatim fallback. Queue entries now carry
{curBody, source} and are matched to fences by body (first unconsumed match,
scanning forward) so a non-matching fence renders verbatim without consuming
anything and a later matching fence still gets its augmentation. Also resets
the per-render consumed state in the renderer.render wrapper, since VS Code
can call md.parse()/renderer.render() separately and replay cached tokens
without a fresh parse.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Task 7 scoped out reusing the F7.1 (#22) intra-diagram mermaid diff in the
built-in Markdown preview's mermaid fence rendering; Task 8 then deleted the
webview that still had it, leaving mermaidDiff.ts/mermaidFlowchartDiff.ts/
mermaidSequenceDiff.ts production-dead. Wires diffMermaid into
cowritingMarkdownItPlugin's options.highlight override via a per-render
mermaid-fence queue (built in the core rule where state.env is visible,
consumed by highlight in fence-encounter order — highlight itself has no env
param) so a mermaid fence changed since the F6/F7 baseline is re-emitted with
classDef/class/linkStyle styling directives + legend before mermaid.js ever
sees it, reusing diffBlocks' baseline/current block pairing (same pairing
renderReview used). Unchanged diagrams, disabled annotations, no baseline, and
pinned-zero-diff all pass through verbatim.
Ports the substance of the 6 mermaid-diff assertions Task 8 deleted with the
webview's E2E suite as pure unit tests in previewAnnotations.test.ts instead
(DOM-free, source-level).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deletes the last bespoke UI surface (TrackChangesPreviewController + its
sealed webview client assets); every entry point re-points at native VS
Code chrome per spec §5: the #41 right-click entries become
cowriting.openReviewPreview (enter coediting if needed -> "Open Preview to
the Side"), Ctrl+Alt+R/Cmd+Alt+R moves to cowriting.reviewChanges (native
diff), and the F12 CodeLens per-proposal titles read "Keep"/"Reject" with
a top-of-file "Keep all (N)"/"Reject all" pair once >=2 proposals are
pending. EditFlow drops its own askClaude/askEditInstruction (the
webview's only caller) and its now-unused constructor params.
Coverage that lived only in the webview's test seams (renderHtmlFor,
receiveMessage, isOpen, ...) moves to direct calls against the surviving
controllers/pure renderReview (test/e2e/suite/helpers.ts gains a shared
renderHtmlFor probe); a genuine gap (pending-proposal + unchanged-block
rendering) is backfilled in test/previewAnnotations.test.ts. README's "how
it works" is rewritten as the native-surface map; the superseded F6/F7/F9/
F10/F11 sections are kept as a marked historical record rather than
deleted outright.
292 unit + 91 E2E green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Task 7 of the native-surfaces migration plan. Pure previewAnnotations.ts
(annotateSource + cowritingMarkdownItPlugin) reuses trackChangesModel.ts's
sentinel discipline (#33/#47), generalized to a 3-way ins-claude/ins-human/del
tag (exported injectSentinels/sentinelsToSpans, colorByAuthor unaffected,
84/84 existing tests pass unmodified). Host hook in extension.ts wires
registry/diffView/attribution/proposals/config into the plugin, exposed via
CowritingApi.extendMarkdownIt + a previewAnnotationHost test seam.
cowriting.annotations setting + toggleAnnotations command/menu. Q4 mermaid
(previewScripts + options.highlight fence override) implemented per the
proven bierner.markdown-mermaid pattern; intra-diagram diff augmentation
scoped out (unverifiable via the structural E2E harness) — see
task-7-report.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Moves runEditAndPropose, the askClaude gate/prompt/progress wrapper, the
EditTarget type, the editTurn/askEditInstruction/turnSeq state, and the
cowriting.editDocument command registration out of TrackChangesPreviewController
into a new EditFlow (src/editFlow.ts), reachable from the review webview
(delegates) and, from Task 6 on, the thread controller. extension.ts now
constructs EditFlow before the preview controller and routes
acceptAllProposals/rejectAllProposals directly to ProposalController, dropping
the preview-controller indirection. E2E suites that drove the old
trackChangesPreviewController.{setEditTurnForTest,runEditAndPropose,askEditInstruction}
seams now drive the same seams on editFlow.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Task 4 gated every surface on CoeditingRegistry but only wired
ThreadController/EditorProposalController to registry.onDidChange for
restore-on-enter — ProposalController.renderAll and
AttributionController.loadAll were never called on the enter transition, so
a document's FIRST enter with pre-existing sidecar proposals/attribution
showed threads but not proposal decorations/CodeLens or committed
author-coloring until a later edit happened to fire them (a gap Task 4's
own report flagged as known and unfixed).
Both controllers now subscribe to registry.onDidChange in their
constructors and call renderAll/loadAll on entry only (both already
self-gate on isCoediting; exit stays hide-only, mirroring
ThreadController). Construction order in extension.ts (attribution before
proposals before EditorProposalController) keeps the same-tick read order
correct.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Task 2 of the native-surfaces migration plan. GitBaselineAdapter resolves a
document's HEAD blob via the built-in vscode.git extension (hardened with a
.git/logs/HEAD watch that nudges repo.status(), since the git extension's own
watcher doesn't reliably notice out-of-band commits on non-workspace-folder
repos in the E2E host). DiffViewController is reworked into a baseline router:
head mode (git-tracked, never persisted, re-read on commit) vs snapshot mode
(captured on CoeditingRegistry entry, re-pinned by "Mark Changes as Reviewed"
— renamed from cowriting.pinDiffBaseline, D14). The shipped machine-landing
baseline advance (#48/INV-18) is retired: a landed Claude edit now stays a
visible change-since-baseline until commit or review (INV-7/D21). Legacy
on-disk reasons ("opened"/"machine-landing") migrate to "entered" via
BaselineStore.normalizeReason on load.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Implements comprehensive unit test coverage for CoeditingRegistry.syncContext,
which derives context keys (cowriting.isCoediting, cowriting.baselineMode) via
vscode.commands.executeCommand — previously untested. Added 8 new tests:
- isCoediting: true for active editor with coediting doc
- isCoediting: false for undefined editor or non-entered doc
- baselineMode: uses hook result when set; defaults to "" otherwise
- exit idempotency: double exit fires onDidChange only once
All assertions inspect the mocked executeCommand call arguments directly.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>