refactor: extract EditFlow (runEditAndPropose + editDocument) from the review webview ahead of its sunset (spec §6.10)

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>
This commit is contained in:
BenStullsBets
2026-07-02 08:22:44 -07:00
parent f23fc4afb3
commit ad5d34b85b
9 changed files with 300 additions and 239 deletions
+10 -10
View File
@@ -74,7 +74,7 @@ suite("F12 SLICE-1 — Ask-Claude reach (#42, INV-38)", () => {
// not whatever editor happens to be active (mirrors #41's clicked-doc resolution).
test("editDocument(uri) targets the clicked tab's document, not the active editor", async () => {
const api = await getApi();
const ctl = api.trackChangesPreviewController;
const editFlow = api.editFlow;
// Doc A is the active editor; Doc B is the "clicked tab" we pass by URI.
const a = await freshDoc("docs/f12-active.md", "# Active\n\nThe active editor paragraph.\n");
@@ -83,9 +83,9 @@ suite("F12 SLICE-1 — Ask-Claude reach (#42, INV-38)", () => {
await settle();
// Stub the document instruction prompt (the webview can't run in CI) + the LLM turn.
const origPrompt = ctl.askEditInstruction;
ctl.askEditInstruction = async () => "rewrite it";
ctl.setEditTurnForTest(async () => ({
const origPrompt = editFlow.askEditInstruction;
editFlow.askEditInstruction = async () => "rewrite it";
editFlow.setEditTurnForTest(async () => ({
replacement: "# Tab\n\nThe REWRITTEN tab paragraph.\n",
model: "sonnet",
sessionId: "e2e-f12-tab",
@@ -94,7 +94,7 @@ suite("F12 SLICE-1 — Ask-Claude reach (#42, INV-38)", () => {
await vscode.commands.executeCommand("cowriting.editDocument", b.doc.uri);
await settle();
} finally {
ctl.askEditInstruction = origPrompt;
editFlow.askEditInstruction = origPrompt;
}
// The proposal(s) landed on the TAB doc (B), and the ACTIVE doc (A) has none.
@@ -113,14 +113,14 @@ suite("F12 SLICE-1 — Ask-Claude reach (#42, INV-38)", () => {
// No URI arg (palette / keybinding) → fall back to the active editor.
test("editDocument() with no arg targets the active editor", async () => {
const api = await getApi();
const ctl = api.trackChangesPreviewController;
const editFlow = api.editFlow;
const a = await freshDoc("docs/f12-noarg.md", "# No arg\n\nThe active doc paragraph here.\n");
await vscode.window.showTextDocument(a.doc);
await settle();
const origPrompt = ctl.askEditInstruction;
ctl.askEditInstruction = async () => "rewrite it";
ctl.setEditTurnForTest(async () => ({
const origPrompt = editFlow.askEditInstruction;
editFlow.askEditInstruction = async () => "rewrite it";
editFlow.setEditTurnForTest(async () => ({
replacement: "# No arg\n\nThe REWRITTEN active doc paragraph.\n",
model: "sonnet",
sessionId: "e2e-f12-noarg",
@@ -129,7 +129,7 @@ suite("F12 SLICE-1 — Ask-Claude reach (#42, INV-38)", () => {
await vscode.commands.executeCommand("cowriting.editDocument");
await settle();
} finally {
ctl.askEditInstruction = origPrompt;
editFlow.askEditInstruction = origPrompt;
}
assert.ok(api.proposalController.listProposals(a.doc).length >= 1, "active doc received the proposal(s) on no-arg");
});