feat(attribution): landBaseline opt + signalLanded for F12 optimistic apply (#64)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -249,7 +249,7 @@ export class AttributionController implements vscode.Disposable {
|
|||||||
range: vscode.Range,
|
range: vscode.Range,
|
||||||
newText: string,
|
newText: string,
|
||||||
provenance: Provenance,
|
provenance: Provenance,
|
||||||
opts?: { expectedVersion?: number; turnId?: string },
|
opts?: { expectedVersion?: number; turnId?: string; landBaseline?: boolean },
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
if (!this.isTracked(document)) return false;
|
if (!this.isTracked(document)) return false;
|
||||||
if (opts?.expectedVersion !== undefined && document.version !== opts.expectedVersion) return false;
|
if (opts?.expectedVersion !== undefined && document.version !== opts.expectedVersion) return false;
|
||||||
@@ -290,15 +290,25 @@ export class AttributionController implements vscode.Disposable {
|
|||||||
"(host minimized differently?) — the edit may be mis-attributed (INV-9).",
|
"(host minimized differently?) — the edit may be mis-attributed (INV-9).",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (ok) {
|
if (ok && opts?.landBaseline !== false) {
|
||||||
// F6 (INV-18): a real machine landing — signal the baseline to advance so
|
// F6 (INV-18): a real machine landing — advance the baseline. F12 (INV-48)
|
||||||
// this text never shows as a change in the diff view. Fire regardless of
|
// suppresses this for optimistic apply: the proposed text is in the buffer
|
||||||
// attribution-match bookkeeping above; the landing happened either way.
|
// but the change stays PENDING (baseline at pre-proposal) until accept.
|
||||||
this.applyEmitter.fire({ document });
|
this.applyEmitter.fire({ document });
|
||||||
}
|
}
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* F12/#64 (INV-51): fire the machine-landing signal WITHOUT applying text — used
|
||||||
|
* by finalize-in-place, where the proposed text already landed in the buffer via
|
||||||
|
* optimistic apply (`landBaseline:false`) and accept only needs to advance the
|
||||||
|
* F6 baseline so the now-accepted change stops reading as pending.
|
||||||
|
*/
|
||||||
|
signalLanded(document: vscode.TextDocument): void {
|
||||||
|
if (this.isTracked(document)) this.applyEmitter.fire({ document });
|
||||||
|
}
|
||||||
|
|
||||||
// ---- PUC-4: persistence on save ----------------------------------------------------
|
// ---- PUC-4: persistence on save ----------------------------------------------------
|
||||||
|
|
||||||
private onDidSave(document: vscode.TextDocument): void {
|
private onDidSave(document: vscode.TextDocument): void {
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import * as assert from "assert";
|
||||||
|
import * as fs from "fs";
|
||||||
|
import * as path from "path";
|
||||||
|
import * as vscode from "vscode";
|
||||||
|
import type { CowritingApi } from "../../../src/extension";
|
||||||
|
|
||||||
|
const WS = process.env.E2E_WORKSPACE!;
|
||||||
|
const settle = () => new Promise((r) => setTimeout(r, 400));
|
||||||
|
|
||||||
|
async function getApi(): Promise<CowritingApi> {
|
||||||
|
const ext = vscode.extensions.getExtension("benstull.vscode-cowriting-plugin")!;
|
||||||
|
const api = (await ext.activate()) as CowritingApi;
|
||||||
|
assert.ok(api?.trackChangesPreviewController && api?.proposalController, "exports controllers");
|
||||||
|
return api;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function freshDoc(rel: string, body: string): Promise<{ doc: vscode.TextDocument; key: string }> {
|
||||||
|
const abs = path.join(WS, rel);
|
||||||
|
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
||||||
|
fs.writeFileSync(abs, body, "utf8");
|
||||||
|
const uri = vscode.Uri.file(abs);
|
||||||
|
const doc = await vscode.workspace.openTextDocument(uri);
|
||||||
|
await vscode.window.showTextDocument(doc);
|
||||||
|
await settle();
|
||||||
|
return { doc, key: uri.toString() };
|
||||||
|
}
|
||||||
|
|
||||||
|
suite("F12 inline diff — seam landBaseline (#64, INV-48)", () => {
|
||||||
|
test("applyAgentEdit with landBaseline:false applies text but does NOT advance the baseline", async () => {
|
||||||
|
const { doc, key } = await freshDoc("docs/f12-land.md", "# T\n\nalpha here.\n");
|
||||||
|
const api = await getApi();
|
||||||
|
await vscode.commands.executeCommand("cowriting.showTrackChangesPreview");
|
||||||
|
await settle();
|
||||||
|
const before = api.diffViewController.getBaseline(key)?.text ?? doc.getText();
|
||||||
|
const start = doc.getText().indexOf("alpha");
|
||||||
|
const ok = await api.attributionController.applyAgentEdit(
|
||||||
|
doc,
|
||||||
|
new vscode.Range(doc.positionAt(start), doc.positionAt(start + "alpha".length)),
|
||||||
|
"ALPHA",
|
||||||
|
{ kind: "agent", id: "claude", agent: { sdk: "x", model: "m", sessionId: "s" } },
|
||||||
|
{ landBaseline: false },
|
||||||
|
);
|
||||||
|
await settle();
|
||||||
|
assert.ok(ok, "edit applied");
|
||||||
|
assert.ok(doc.getText().includes("ALPHA"), "text in buffer");
|
||||||
|
assert.strictEqual(api.diffViewController.getBaseline(key)?.text ?? doc.getText(), before, "baseline unchanged");
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user