F10 SLICE-4: host E2E for interactive review (open/toggle/propose→accept→reject/clean-editor/hidden-F6/status); update obsolete F9/toggle suites (#29)

Adds test/e2e/suite/f10Review.test.ts covering the F10 flow: open preview
(mode "on", fresh baseline all-unchanged), type → cw-by-human span, propose →
cw-proposal block with ✓/✗, accept → lands + baseline advances + block clears,
reject → vanishes + doc untouched, toggle off → renderPlain has no cw- marks,
status-bar PUC-6 (indicator with no panel, hidden once opened), and the
clean-editor INV-32 facts (retired toggleAttribution, F6 ctrl+alt+d hidden).

Rewrites the obsolete F9 authorship-mode test as an F10 review test
(cw-by-claude in the on-state render; getMode defaults to "on"). Updates
attribution.test.ts (drops the retired isVisible/toggleAttribution toggle,
asserts the toggle is gone) and noWorkspace.test.ts (toggleAttribution +
acceptProposal/rejectProposal are retired, preview-only — INV-32).

Honesty fix in the status-bar seam: hideStatus() clears statusItem.text so
statusText() reports undefined when the indicator is hidden (it previously
returned its stale last value after a panel opened).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ben Stull
2026-06-12 00:31:54 -07:00
parent 42d0ec155e
commit cdeb41ede4
5 changed files with 284 additions and 38 deletions
+20 -26
View File
@@ -14,16 +14,20 @@ async function getApi(): Promise<CowritingApi> {
return api;
}
// F9 host E2E (no LLM): authorship mode reflects Claude's landed span. Owns its
// own markdown doc, disjoint from the other suites' fixtures.
suite("F9 authorship preview (host E2E — seam ingress, no LLM)", () => {
const DOC_REL = "docs/f9authorship.md";
// F10 host E2E (no LLM): the rewrite of the obsolete F9 authorship-mode test.
// F9's "authorship" mode / renderAuthorship is gone — the on-state renderReview
// now author-colors Claude's landed prose. This suite confirms a Claude-landed
// span renders as a cw-by-claude span in the on-state preview HTML. Owns its own
// markdown doc, disjoint from the other suites' fixtures.
suite("F10 review preview — Claude-authored prose is cw-by-claude in the on-state (host E2E, no LLM)", () => {
const DOC_REL = "docs/f10claude.md";
const TARGET = "The sentence Claude will compose over.";
const REPLACEMENT = "The sentence CLAUDE COMPOSED via the seam.";
test("authorship mode marks Claude's accepted edit; track-changes mode still works", async () => {
test("an accepted Claude edit author-colors as cw-by-claude in the on-state render; mode defaults to on", async () => {
const abs = path.join(WS, DOC_REL);
fs.mkdirSync(path.dirname(abs), { recursive: true });
fs.writeFileSync(abs, `# F9\n\n${TARGET}\n`, "utf8");
fs.writeFileSync(abs, `# F10\n\n${TARGET}\n`, "utf8");
const uri = vscode.Uri.file(abs);
const doc = await vscode.workspace.openTextDocument(uri);
await vscode.window.showTextDocument(doc);
@@ -31,11 +35,11 @@ suite("F9 authorship preview (host E2E — seam ingress, no LLM)", () => {
const api = await getApi();
const key = uri.toString();
// open the preview (track-changes mode by default)
// open the preview — annotations default ON (F10/INV-33)
await vscode.commands.executeCommand("cowriting.showTrackChangesPreview");
await settle();
assert.ok(api.trackChangesPreviewController.isOpen(key), "preview open");
assert.strictEqual(api.trackChangesPreviewController.getMode(key), "changes", "defaults to track-changes");
assert.strictEqual(api.trackChangesPreviewController.getMode(key), "on", "annotations default to on");
// Claude composes via the seam (propose → accept)
const start = doc.getText().indexOf(TARGET);
@@ -43,32 +47,22 @@ suite("F9 authorship preview (host E2E — seam ingress, no LLM)", () => {
uri: key,
start,
end: start + TARGET.length,
newText: "The sentence CLAUDE COMPOSED.",
newText: REPLACEMENT,
model: "sonnet",
sessionId: "e2e-f9",
turnId: "turn-f9",
sessionId: "e2e-f10",
turnId: "turn-f10",
});
assert.ok(await api.proposalController.acceptById(DOC_REL, id!), "accept applies");
assert.ok(await api.proposalController.acceptById(DOC_REL, id!), "accept applies via the seam");
await settle();
// attribution has a Claude span now
// attribution recorded a Claude (agent) span (data layer intact)
const claudeSpan = api.attributionController.getSpans(DOC_REL).find((s) => s.authorKind === "agent");
assert.ok(claudeSpan, "Claude span recorded by F3");
// flip to authorship mode → the preview reads a Claude span
api.trackChangesPreviewController.setMode(key, "authorship");
await settle();
assert.strictEqual(api.trackChangesPreviewController.getMode(key), "authorship");
const spans = api.attributionController.spansFor(doc);
assert.ok(spans.some((s) => s.author === "claude"), "spansFor reports a Claude span for the preview");
// back to track-changes — still functional (regression)
api.trackChangesPreviewController.setMode(key, "changes");
await settle();
assert.strictEqual(api.trackChangesPreviewController.getMode(key), "changes");
assert.ok(
(api.trackChangesPreviewController.getLastModel(key) ?? []).length >= 1,
"track-changes model still computed",
);
// the on-state render author-colors the landed Claude text as cw-by-claude
const html = api.trackChangesPreviewController.renderHtmlFor(key);
assert.match(html, /<span class="cw-by-claude">/, "landed Claude prose is author-colored in the on-state render");
});
});