You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
| TRIAL | feat/fws-trial-all | open — #1413 (trial build of all 15; head 4fc14c4 = 11 merge commits + addenda 6a0feb1 + 376e013 (spec composition, apply_diff self-observation) + 178e6f4 (CodeRabbit round 3: apply_patch observation, mcp merge array guard + spec-mock parity, safeWriteJson doUnmock, webview writeDelayMs shared default, ChangeCard no-files step + grow, CheckpointSettings sibling, Catalan) + d2239ce (restore safety: rev-parse checkpoint verification + realpath symlink containment) + 4fc14c4 (CodeRabbit round 4: McpHub.spec unknown-safe error.code guard, exported createInitialExtensionState + pre-hydration defaults test); all CodeRabbit findings replied — cross-process TOCTOU scoped out and tracked as upstream follow-up Zoo-Code-Org#1414 with epic risk wording proposed; all 15 component PR heads verified mergeable into upstream/main; CI on 4fc14c4 GREEN (ubuntu unit + e2e-mock + codecov/patch + compile + VSIX build all success); PR Zoo-Code-Org#1413 body updated to the green run 33134141568 (artifact 9671394094, inner vsix sha256 1cbe1793...); all CodeRabbit findings confirmed incl. the round-4 pair (01:59-02:02 UTC); the 8 component PRs re-pushed with the propagated fixes (round-10 notes on each row) and all 8 new heads merge-checked clean against upstream/main; round 11: head 83d83a3 = previous + addendum 6 (re-merge of the updated Zoo-Code-Org#141194fea2f + Zoo-Code-Org#1412c48522c after the trial-review user feedback: apply_diff per-write checkpoint + change card; change-card open-in-editor control incl. the CodeRabbit a11y native-button fix; CI GREEN); round 12: head d6d08e8 = 83d83a3 + addendum 7 (re-merge of Zoo-Code-Org#1411341a9c2 + Zoo-Code-Org#1412502f8ca after the CodeRabbit round-11 fixes; CI GREEN run 33161152644 — inner vsix sha256 016e930f…, PR body on that green run; no visible UI change) + addendum 8 (re-merge of Zoo-Code-Org#141167d8525 — the trial-review audit found the edit and search_replace tools wrote with no per-write checkpoint / journal entry / change card; both now wired with the same checkpointSave call site as the other four write tools + focused 8-test spec; CI running); fork PR #34 (→ trial) still open, CI 15/15 green, re-verified mergeable against the new trial tip d6d08e8; round 13 (2026-08-29): the CodeRabbit review round was fixed in the introducing PRs — Zoo-Code-Org#1410 → 39afe1b (a card whose file was re-written in a later step is now refused instead of overwriting the newer write, and an unreadable journal fails loudly instead of a false no-op), Zoo-Code-Org#1411 → c202745 + 8c5c53b (typed settings test doubles + nl changeCardDetail sentence), Zoo-Code-Org#1412 → a0693e9 + ea90ea8 (correlated webview failure results, localized no-task results, focusable change-card error states, openFile path labels, es/hi/it/ko locale corrections, schema-invalid spec coverage); all 18 CodeRabbit findings replied (15 original + 3 round-2 on Zoo-Code-Org#1412) and CR confirmed each fix; all four heads merge-checked clean vs upstream/main (efc30cf) and ubuntu CI green (Zoo-Code-Org#1410 6/6, Zoo-Code-Org#1411 6/6 — the round-1 mocked-E2E failure was a history-write/restart race flake, green on re-run, Zoo-Code-Org#1412 8/8); trial re-merged via addendum 9 (779cb4b, empty CI-retry commit for the flake) + addendum 10 (22a3f33, 46 files +496/-99) — CI GREEN run 33257633298 (inner vsix sha256 aaf90d5e…), PR Zoo-Code-Org#1413 body updated to that green run (artifact 9716305621); fork PR #34 re-verified MERGEABLE/CLEAN against the new trial tip 22a3f33; round 14 (2026-08-30): upstream/main moved efc30cf → 147147c (5 commits: Zoo-Code-Org#1434 the subtask-approval mocked-E2E timeout fix — the exact flake that failed Zoo-Code-Org#1411's round-1 run, Zoo-Code-Org#1426 Extension Host visual regression rework incl. new electron visual suite + playwright harness restructure, Zoo-Code-Org#1445 label clipping, Zoo-Code-Org#1446 typed host message capture, Zoo-Code-Org#1449 partial-ask e2e ignore); all 16 series heads re-verified merge-tree CLEAN against the new tip 147147c; required checks per the main ruleset = 6 (check-translations, platform-unit-test ubuntu + windows, compile, knip, e2e-mock) — all green on every head; the org is rolling out a new PR review gate (Zoo-Code-Org#1437: required CI → CodeRabbit current-head review → human maintainer approval, SQUASH merge queue ALLGREEN) — its 'PR review gate' status is advisory for fork PRs (hard enforcement = native required checks + review protections) and no code change is needed for it; Zoo-Code-Org#1412 carried a stale CodeRabbit CHANGES_REQUESTED (all findings fixed + individually confirmed by CR) so it was labeled awaiting-author — /approve requested on Zoo-Code-Org#1412 and /review requested on Zoo-Code-Org#1411 + Zoo-Code-Org#1413 at 03:49Z but CodeRabbit picked up no manual command within 30 min (the new gate flow owns CR triggering; the org CR config may restrict fork users), so empty commits were pushed to re-trigger the native synchronize flow: Zoo-Code-Org#14118c5c53b → 0d31ea0, Zoo-Code-Org#1412ea90ea8 → 63dcd24 (the push also dismisses the two stale CodeRabbit CHANGES_REQUESTED reviews per the ruleset dismiss_stale_reviews_on_push), Zoo-Code-Org#141322a3f33 → 5cb1d96 (addendum 11); zero code change (empty commits), all three re-verified merge-tree CLEAN against main 147147c; NOTE: every one of the 16 series PRs is currently mergeable=true / mergeStateStatus=blocked — the block is the ruleset requiring 1 approving review + code-owner review, i.e. awaiting maintainer review, not CI; CI (6 required checks) is green on all 16 heads; the only remaining automation work is CodeRabbit current-head reviews on the three re-pushed PRs (watching), after which the series is fully at the human-approval stage; round 15 (2026-08-30, after inspecting Zoo-Code-Org#1440 which cleared the bot stage): the gate flow per Zoo-Code-Org#1440's process comment = (1) required CI green, (2) workflow starts CodeRabbit automatically, (3) CodeRabbit APPROVES the latest commit (native review state — COMMENTED/acked findings do NOT count), (4) human maintainer approval; Zoo-Code-Org#1440 cleared step 3 with a clean re-review (zero new comments → APPROVED in 2 min) and sits at awaiting-maintainer; our 16 PRs were all at the gate step "Wait for CodeRabbit to approve the latest commit" (CR states were COMMENTED), so all 16 got the same empty-commit re-trigger: 1380→046b78cad, 1381→309ffd644, 1382→b67e57312, 1383→2a42f886f, 1384→d03877438, 1394→a00eef8e0, 1395→7fd49bcea, 1403→cb1360613, 1404→abc10b43e, 1405→be894d9e0, 1406→7b83143cf, 1408→e96df628a, 1410→87471b558 (plus the earlier 1411→0d31ea062, 1412→63dcd24e2, 1413→5cb1d9674); zero code change in all 16 (empty commits), all 16 re-verified merge-tree CLEAN vs main 147147c, all 16 bodies carry a review-gate re-trigger note (code heads unchanged); the "PR review gate" status stays pending by design until a maintainer approves (advisory — the hard gates are the 6 required checks [all green] + 1 approving review + code-owner); watching CI + CR approvals on the 16 new heads; round 16 (2026-08-30): gate-mechanism forensics + @coderabbitai canary result + 1412 CI clear. (a) The review gate (guide comment + "Zoo Code / PR review gate" status + awaiting-coderabbit/awaiting-maintainer labels) is produced by the NEW label-pr-review-state.yml which exists ONLY on Zoo-Code-Org#1437 gate branch feature/human-pr-review-gate-0shw6cv6mcu (main still runs the old label-only reconciler, last touched 07-13); it executes when zoomote[bot] workflow_dispatches it from that branch — dispatch run 33280010451 (08-29 23:02Z) batch-posted Zoo-Code-Org#1440 plus 13 of our guide comments in a single run. Zoo-Code-Org#1440 bot pass, end to end: real-diff push (18:10Z) → CodeRabbit native APPROVED review on the exact head commit within ~2 min (zero findings) → workflow phase "maintainer" (awaiting-maintainer label + gate step "A human maintainer must now review and approve it"); the gate status is advisory — the hard gates are the 6 required CI checks + 1 approving review + code-owner review (re-verified against ruleset 15494382 "Protect Main", which also has a merge_queue rule with ALLGREEN grouping). (b) CANARY NEGATIVE: @coderabbitai review mention on Zoo-Code-Org#1381 (05:58Z) received a CodeRabbit reply within 7 seconds: "Action not completed — No files to review. CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused." → no review object can be created on an empty-commit head; consistent with the 90-min watcher showing zero fresh CR reviews on all 16 heads → the mention path cannot clear the CR stage; no rollout performed. (c) 1412 addendum 12 (empty commit ab281f5) cleared the flaky ubuntu unit run (the vitest EnvironmentTeardownError x9 was an infra false positive, no assertion failures) → 1412 now 6/6 required green and stable. ALL 16 PRs now: 6/6 required checks green, mergeable, zero mechanical blockers; the only remaining gates are the two human rules (1 approving review + code-owner review), plus — for the merge-queue path only — the advisory extension-host-visual on the 6 B1-stack PRs, whose root cause (B1 task-start baseline checkpoint → SeeNewChangesButtons rendering below the completion row against the pre-FWS Zoo-Code-Org#1426 baseline; deterministic 432-px delta per Playwright / 973 px in the raw PNG diff; pixel-identical across 1404/1406/1410/1411/1412/1413) is documented in those PR bodies for maintainer disposition. The CR stage clears automatically if Zoo-Code-Org#1437 merges (its .coderabbit.yaml gates auto-review on the coderabbit-review-active label all 16 carry); otherwise the path to merge is direct maintainer approval (the gate comment is advisory). |# [ENHANCEMENT] File-write safety series (upstream epic Zoo-Code-Org#1375): version-guarded atomic writes + per-step visibility/rollback — PR series plan
PR series plan + execution tracker (DTE-style: one independently mergeable PR per phase).
Story of record: upstream epic Zoo-Code-Org/Zoo-Code#1375.
Phase 0 PRs (already open): #1380, #1381, #1382.
1. Goal
Make agent file writes fast, loud-failing, self-healing, atomic, and auditable — in that order.
Safety: no silent corruption (races, truncated tool calls, cross-instance state writes); stale writes fail loudly with a remediation the model can act on.
Speed: zero artificial latency on the default write path.
Visibility: every agent write is checkpointed, journaled, and shown as a per-step change card; rollback per file or per step. Explicit non-goals (per epic): shell-tool writes, workspace lockfiles, diff-editor approval UX replacement, worktrees as the task-start mechanism.
OPEN (#1405, tracking #1399); round 10: head 56ce4bf (CodeRabbit fixes from trial addendum: safeWriteJson cleanup now doUnmock + resetModules; CI green)
OPEN (#1408, tracking #1400); round 10: head 88c9352 (CodeRabbit fixes from trial addendum: apply_patch hunk read now records the S2 observation for the guarded publish; CI green)
Per-step change cards + changeCardDetail setting (extension host; split from the planned B3a by the 1000-line cap)
B1+B2
2d
OPEN (#1411, tracking #1401); round 10: head 9b0787a (CodeRabbit fixes from trial addendum: exported createInitialExtensionState + pre-hydration changeCardDetail test; CI green; round 11: head 94fea2f (user feedback from the trial review: apply_diff writes now record the per-write checkpoint and emit the per-step change card, parity with write_to_file / edit_file / apply_patch; new 4-test spec; CI GREEN); round 12: head 67d8525 (341a9c2 + trial-review audit fix: the edit and search_replace tools — exact string replacement, exposed by default — now also record the per-write checkpoint and emit the per-step change card, parity with all six write tools; focused 8-test spec; CI running); round 13: heads c202745 + 8c5c53b (CodeRabbit review fixes: Slider test double typed narrow instead of any + nl changeCardDetail.description full sentence; VSCodeCheckbox/VSCodeLink settings doubles fully typed; tsc 0, eslint 0, 5/5 spec; CI GREEN 6/6 — the round-1 mocked-E2E failure was a history-write/restart race flake, green on re-run; merge-tree clean)
B3c
B3
Per-file/per-step rollback service (extension host; split from B3a)
B3a
1d
OPEN (#1410, tracking #1409); round 10: head d64389c (CodeRabbit fixes from trial addendum: checkpoint availability verification + symlinked-ancestor realpath containment; CI green); round 13: head 39afe1b (CodeRabbit review fixes on the Zoo-Code-Org#1435 semantic head: stale-card rollback refused via latest-entry checkpointId comparison instead of overwriting the newer write; journal read failures (EACCES/EISDIR/torn JSON) propagate instead of a false no-op — only ENOENT yields []; EISDIR + non-Error rejection regression tests; tsc 0, eslint 0, 100% changed-line coverage; CI GREEN 6/6; merge-tree clean)
B3b
B3
Change cards UI + rollback buttons (webview)
B3a+B3c
1d
tracking (#1402); round 10: head 0e021ef (CodeRabbit fixes from trial addendum: ChangeCard no-files step resolution, Tailwind v4 grow, sibling settings, Catalan fix; CI green; round 11: head c48522c (user feedback from the trial review: per-file open-in-editor control on change cards - CodeAccordion jump icon on diff rows + native button on no-diff rows - via the existing openFile webview message; changeCard.openFile i18n key in all 18 locales; 3 new spec tests incl. the CodeRabbit a11y fix making the compact-row control a native button; CI re-running); round 12: head 502f8ca (typed VSCodeCheckbox change events + typed settings test doubles; CI GREEN); round 13: head ea90ea8 (CodeRabbit review fixes: the three rollback/restore handler cases post correlated failure results when the import/journal/git restore throws, no-active-task results post localized copy across 18 locales, the change-card error states are focusable status elements (role=status + tabIndex + aria-label = the error), the compact-row open-file label names the target file (openFile {{path}} slot, 18 webview locales), es/hi/it/ko locale corrections, and the spec covers the schema-invalid JSON path; tsc 0, eslint 0, 100% changed-line coverage; CI GREEN 8/8; merge-tree clean)
3. PR specs
S1 — A1 Version token
New filesrc/utils/versionToken.ts: computeVersionToken(filePath): Promise<string> + pure versionTokenOfStat(stats) for testability.
Token format: dev:ino:size:mtimeNs:ctimeNs from onefs.stat; ns fields as decimal strings from BigInt (no float precision loss).
No production callers in this PR — pure infrastructure. A disk fact: every process observing the same file state computes the same token.
Tests (unit): determinism on identical stat; distinct token on size/mtime change; BigInt large-size handling; absent file rejects with ENOENT.
Acceptance: unit suite green; zero behavior change (no imports from production code).
S2 — A2 Observation registry
New filesrc/core/task/observationRegistry.ts: per-task Map<absolutePath, { version, observedAt }>; owner = task, so parent and subtask observations are independent.
Task owns an instance; ReadFileTool records an observation after a successful read (one extra stat, same call the write guard will reuse).
No behavior change — observations are recorded but not yet consulted.
Tests: registry unit tests (observe/get/replace-on-reobserve); ReadFileTool spec-level test asserting a read of an existing file registers an observation with the on-disk version; subtask isolation test.
Acceptance: read path cost = +1 stat; no other observable change.
S3 — A4 Atomic publish
New filesrc/services/file-safety/safeWriteText.ts: temp file in a private per-write staging dir → write → fsync → close → atomic rename; on Windows: ReplaceFile with DACL copy, rename fallback. Generalizes the staging/backup/rollback logic currently inside safeWriteJson (which is refactored to call this).
saveDirectly (all five write tools + write_to_file) switches from raw fs.writeFile to safeWriteText.
Behavior-preserving (no version guard yet): same writes succeed, but now crash/power-loss safe (reader sees only old-or-new complete content).
Tests: staging dir created/cleaned; fsync called before rename (mock fs); crash-during-write leaves no torn target (simulate failure between write and rename); Windows ReplaceFile path + DACL preservation; safeWriteJson existing suite still 100% (no regression); patch coverage 100%.
Acceptance: saveDirectly no longer calls raw fs.writeFile; safeWriteJson behavior unchanged.
S4 — A3 Guarded write/edit (the behavior change)
New filesrc/core/tools/guardedWrite.ts: compare-and-swap on the write path:
unobserved target → createIfAbsent: new file succeeds; existing file fails (forces a read first — the model re-reads and retries with the observed version);
observed-absent → createIfAbsent;
observed-present → replaceIfVersion(version): mismatch fails with "stale version — re-read the file, then retry";
edit keeps its literal-match check plus the version guard; unobserved edit fails with "file not read yet — read the file, then retry".
Per-absolute-path tail-promise chain (in-process FIFO) wrapping read → guard → publish: concurrent subtask mutations to the same file are deterministically ordered — one wins, the rest fail as stale.
Diff budget note: core (guard + FIFO chain + registry hook) is S4a; tool wiring is S4b if the combined diff would exceed the 800-line target.
Cross-process stance: no lockfile (would block the user's own editor); two processes on the same file are detected via the token, loser fails as stale and re-reads.
Tests (unit + concurrency): each guard branch per tool; stale-version failure carries the remediation suffix; unobserved-edit failure; two concurrent writers on one path → exactly one succeeds; observed-absent then concurrent-create → second fails stale; regression: normal single-writer flow unchanged (no new failures on existing tool suites).
Acceptance: all five write tools fail loudly (tool-call error, step event in chat) on stale/unobserved writes; model self-heals via re-read+retry in the standard loop; no silent overwrite path remains.
Regression test: two extension instances (parallel tabs) racing updateTaskHistory on the locked-merge write; asserts the second instance's merge preserves the first's fields (no clobbered history item).
Base material: local branch fix/920-concurrent-task-history-cross-instance (5 commits, prior work).
NativeToolCallParser: a finalization failure must not reuse the streaming-phase nativeArgs (partial JSON). Fail the tool call with an explicit "arguments were truncated" error instead of writing truncated content to disk.
Tests: truncated write_to_file content argument → tool call errors, zero bytes written to disk; complete args still succeed; regression suite for the parser's finalization path.
Acceptance: no code path can write a partial tool-call argument to disk.
L1 — Async post-save diagnostics
saveDirectly tail: instead of awaiting LSP diagnostics, emit them as an asynchronous follow-up event once settled.
Tests: save resolves without waiting on diagnostics; follow-up event fires with the settled diagnostic payload; no event on clean save (or a no-op event, per implementation).
Acceptance: post-save latency drops by the LSP-settle time; diagnostic information is still delivered (just later).
L2 — Chat-diff default approval path
Make the chat-diff (PREVENT_FOCUS_DISRUPTION) path the default approval flow; diff-editor path remains available.
Setting: flips the existing PREVENT_FOCUS_DISRUPTION default false → true (src/shared/experiments.ts:22); toggle kept as escape hatch, storage key unchanged (§6).
Tests: default path resolves to chat-diff; focus not stolen from the active editor (behavioral test at the provider level).
Acceptance: default approval no longer opens/refocuses the diff editor.
B1 — Per-write checkpoint
Extend ShadowCheckpointService: every successful write_to_file / edit_file / apply-patch records a checkpoint (currently only user message sends do), keyed by step; checkpoint payload reuses the existing shadow git at <globalStorage>/tasks/<taskId>/checkpoints.
Task start becomes a real O(1) baseline (today: no-op). Not a worktree.
Tests: checkpoint created per successful write (mock service assertions); task-start baseline recorded once; rollback-to-checkpoint restores file content; checkpoint count bounded per task.
Acceptance: after any agent write, git-in-shadow can show that step's snapshot; task start is cheap (no worktree).
Setting: perWriteCheckpoints (boolean, default true) — master switch for the B cluster; full Persisted Setting Checklist round trip ships in this PR (§6).
B2 — Per-task change journal
Append-only changes.jsonl under the task dir; one entry per file write: path, operation, checkpoint id, diff stats (additions/deletions from the already-computed approval diff).
Torn-tail repair on load (incomplete final line discarded, rest parsed).
Tests: append format; load with clean tail; load with torn tail (write truncated line, repair drops it); journal entry references the B1 checkpoint id.
Acceptance: journal is the single per-task audit list; survives a crash mid-write (no corrupt load).
B3 — Per-step change cards + rollback
Per-step change cards in the existing chat flow: reuse the unified diff + computeDiffStats already produced for approval; "N files changed this step" + per-file diff.
Rollback to any checkpoint, per file or per step (shadow git restore, journal as the index).
Auto-approval paths get the same cards after the fact.
Diff budget note: B3a = extension host (card payloads + rollback service), B3b = webview (cards UI + rollback buttons); split point = the 800-line target per PR.
Tests: card payload for a multi-file step; rollback per file restores only that file; rollback per step restores all its files; auto-approval step still emits cards.
Acceptance: the user can see and undo any agent step after the fact, including on fully auto-approved runs.
Check Zoo-Code-Org#1319 status before starting; adapt or close as covered
Windows ReplaceFile DACL edge cases (S3)
Dedicated unit tests on the Windows CI lane; rename fallback kept
B1 shadow-git growth (checkpoints per write)
Per-task dir, bounded by existing checkpoint policy; monitor task dir size in tests
Cross-process writes: token detects, doesn't lock
Epic decision — a lockfile would block the user's own editor; loser re-reads and retries
6. Configuration / user settings
Principle: the safety core (A1–A6) is not configurable — an off-switch would reintroduce the corruption this epic fixes. Configurable surface = the B cluster (visibility/rollback convenience) + existing behavior toggles (speed, approval surface). Every new setting follows the AGENTS.md Persisted Setting Checklist — full round trip: global-settings.ts (definition + shared default constant) → ExtensionState/message types → SettingsView binding to local cachedState (never live state) → updateSettings payload → webviewMessageHandler persistence via ContextProxy → ClineProvider.getState() with default → getStateToPostToWebview() (destructure + return) → every consumer same default semantics → import/export schema → focused tests (UI binding/save, persisted + unset via posted state).
New settings
Key
Type
Default
Controls
Lands in
perWriteCheckpoints
boolean
true
Master switch for the whole B cluster: per-write checkpoints (B1), JSONL journal (B2), change cards + rollback (B3). Off = writes behave as today, no B artifacts, no rollback.
B1
changeCardDetail
`"full"
"summary"`
"summary"
Card granularity: summary = one line ("N files changed +X −Y") + per-file list + rollback, diff rendered lazily on expand; full = inline unified diff rendered by default. Auto-approval steps always get the compact card regardless.
No "off" level for card detail: visibility is the point; users who don't want cards turn the master off.
Checkpoint retention stays with the existing checkpoint policy (bounded per task); revisit only on disk-usage reports — no setting now.
Flip default false → true (experimental map, src/shared/experiments.ts:22). Keep the toggle as the escape hatch for users who prefer diff-editor approval. Decision inside the L2 PR: if the experimental settings section is hard to discover, surface it as a first-class approval-surface setting — move/rename in the UI only; the storage key stays stable so existing user values carry over.
Deliberately NOT settings (decision record)
A1–A4, A5, A6 (version token, observation registry, CAS guard, atomic publish, internal-state firebreak, truncation guard): always on. No "legacy unsafe mode" — the epic's acceptance criteria (no silent apply failures, loud + recoverable stale) are non-negotiable.
L1 async diagnostics: informational follow-up event; no toggle (a setting would only re-introduce the blocking path or drop the information).
Round-trip cost and diff budget
Each new setting touches ~8–10 files (types, extension host, provider, webview settings, schema, tests) ≈ 100–300 changed lines, and counts toward the diff budget (800-line target / 1000 hard cap):
perWriteCheckpoints round trip ships inside the B1 PR (not a separate PR) — it is part of the 2d B1 estimate.
changeCardDetail round trip ships inside B3a (extension-host side); if B3a + round trip exceeds budget, B3a keeps setting + payload and B3b is UI-only.
L2's default flip is one line in experiments.ts + its spec update — trivial.
Per-setting tests (checklist): SettingsView save binds cachedState and posts updateSettings; posted state asserts both persisted and default; import/export round-trips.
i18n: names/descriptions added to all locale files; check-translations CI covers it.
7. E2e plan (apps/vscode-e2e, aimock)
Principle (AGENTS.md test pyramid): e2e only for real extension-host boundaries and full-workflow smoke; detailed assertions stay at unit/spec. Existing base suites to extend: suite/tools/write-to-file.test.ts (real task → write_to_file → disk assertions, aimock-replayed) and suite/subtasks.test.ts (+ fixtures/subtasks.ts). New fixtures follow the aimock workflow: TEST_FILE-filtered record runs, toolCallId matching for turn 2+, match strings without timestamps/paths, verify with pnpm --filter @roo-code/vscode-e2e test:ci:mock.
Phase
E2e decision
Phase 0
none — unit-level fixes
S1, S2
none — pure function + spec level
S3
no new e2e (crash-safety = unit tests on mocked fs); existing write-to-file smoke must stay green as the real-host regression
S4
E1 stale-write self-heal (ships with S4b): scripted multi-turn fixture — tag prompt → write_to_file on an existing file the model never read → expect the tool-call failure step ("file not read yet") → read_file (matched by toolCallId) → write_to_file retry → attempt_completion; assert final on-disk content = new content and the chat step shows the failure + success. E2 concurrent subtask writers (ships with S4b or as a follow-up small PR): extend the subtasks suite — two subtasks write the same file; assert final content is one complete version (no torn/interleaved file); keep ordering assertions loose to avoid CI flake
S5
unit-level cross-instance test only; a true two-live-instance e2e would require extending the restart coordinator (sequential phases today) — stretch goal, out of scope for the series
S6
E3 truncated tool call writes nothing (ships with S6): fixture whose write_to_file arguments are truncated JSON → task step shows the "arguments were truncated" error → assert the target file is absent/unchanged on disk. Feasibility check during implementation: if aimock cannot replay a malformed arguments payload, fall back to parser-level integration test (still justified)
L1
none — timing assertions are flaky in e2e
L2
extend write-to-file suite: default approval surface resolves to chat-diff; approval completes and file is saved without the diff editor opening
B1
unit; optional restart-scenario smoke that the checkpoints dir exists after a write
B2
none — unit (journal format/repair)
B3
webview-ui tests for card rendering + rollback button state; E4 step rollback end-to-end (ships with B3b): multi-step write task → assert per-step change cards appear → invoke rollback of one step → assert on-disk content of that step's files reverts to the pre-step version
8. Per-PR execution rules (AGENTS.md)
One PR per phase; each PR: focused diff, its own commit history, own CI.
Diff budget (two tiers): total changed lines (additions + deletions) target < 800 per PR, hard cap 1000. The 800 target leaves a ~200-line buffer so that fixes for reviewer/bot (CodeRabbit) comments added to the same PR — new tests, extra branch coverage, a small refactor — still land under the 1000 cap without a force-push/reopen. If the initial diff already exceeds 800, expect to split into sequential sub-PRs (S4a/S4b, B3a/B3b below). Verify with the PR diff stat before opening, and re-check the stat after any review-fix commit so the final PR stays ≤ 1000.
Narrowest-layer tests: pnpm --dir src exec vitest run <path>; suite green before push.
All PRs merged (Phase 0 + S1–S6 + L1–L2 + B1–B3, including any S4/S4b and B3a/B3b splits); every PR at or under the 800-line target, and never over the 1000-line hard cap (the 800→1000 band is reserved for review-fix commits).
E2e suite green on the mock lane (pnpm --filter @roo-code/vscode-e2e test:ci:mock), including E1 (stale-write self-heal), E2 (concurrent subtask writers), E3 (truncated tool call writes nothing), and E4 (step rollback end-to-end).
Settings: perWriteCheckpoints and changeCardDetail complete the full Persisted Setting Checklist (UI save via cachedState, posted-state persisted + default assertions, import/export round trip, all locales); PREVENT_FOCUS_DISRUPTION defaults to true with the existing toggle preserved.
Execution status (synced 2026-08-29 (round 13): UI/UX review of the change-card rollback flow found a P1 semantic bug - the card rollback restored a file to the step's own POST-write checkpoint (a no-op for the newest step; contradicts the epic AC "undo what the agent did" and the card's own confirm copy). User chose Option A + forward restore, shipped as: (1) Zoo-Code-Org#1410 head 630f273 - rollback now resolves the restore target from the B2 change journal to the file's PRE-step state (preceding journal entry's checkpoint; task-start baseline for the file's first change; undoing a create deletes it again, undoing a delete restores it) + new restoreLatestFile (forward direction; clean no-op success when the task never wrote the file); 20-test spec; all local gates green (vitest 20/20, tsc 0, eslint 0 with suppression unchanged, 100% changed-line coverage); merge-tree clean vs upstream/main efc30cf. (2) Zoo-Code-Org#1412 head 9a430d6 (stacked on 630f273) - per-file "Restore latest version" control (two-step confirm + warning), new checkpointRestoreLatestFile webview message + handler case (results reuse checkpointRollbackResult carrying kind rollback/restore-latest; legacy no-kind results still route to the rollback control), per-file rollback confirm now shows the warning text, 5 new/updated chat:changeCard.* keys x 18 locales, +5 ChangeCard tests + +4 handler tests; merge-tree clean vs efc30cf. New upstream tracking issue Zoo-Code-Org#1435 documents the semantic correction; both PR bodies refilled. Trial addendum 9: head 7591bc8 (cherry-picks f0f3313 + 7591bc8 applied cleanly onto d6d08e8; local gates: tsc 0 both dirs, eslint 0, vitest 31+22 green, merge-tree clean; Code QA CI running). Fork PR #34 (-> trial) remains open, CI 15/15, head 7efbf33 merge-tree clean vs the new trial tip 7591bc8. The fuzzy-match observability/perf suggestions stay tracked as upstream Zoo-Code-Org#1418; all other previously-green heads unchanged)
open, CI green (fixed a missed DiffViewProvider spec assertion), awaiting review (tracking #1386); round 10: head 6548013 (CodeRabbit fixes from trial addendum: webview writeDelayMs now uses the shared DEFAULT_WRITE_DELAY_MS + pre-hydration test; CI re-running)
open — #1395 (amended to a37dd24 after the 5th CodeRabbit pass: the "no temp left on failure" spec now drives a real post-commit backup cleanup failure (unlink EPERM) and asserts the target stays committed with no temp behind — the non-fatal cleanup path is now covered; 4th-pass tempPath fchmod fix retained; local gates green: 49 unit, safeWriteText.ts 100% stmts/branch/lines, eslint 0, tsc 0; ALL checks green incl. ubuntu unit + e2e-mock + codecov/patch; CodeRabbit review completed; 14/14 findings replied; tracking #1391)
open — #1403 (991ab69, rebased onto S3 head a37dd24: preDiagnostics race fixed; diagnostics tail filtered to the saved file via arePathsEqual (case-insensitive on Windows); stale .catch comment corrected; as any -> bracket notation (ledger 310->306); 4th CodeRabbit pass fixed — 100 ms in-memory settle delay moved from the blocking save path into the diagnostics tail (saves with diagnostics off / writeDelayMs 0 no longer pay it); 77/77 unit, eslint 0, tsc 0; all 12 CodeRabbit findings replied — 3 cured by the S3 rebased base; e2e-mock passed (subtasks resumeTask flake did not reproduce); stacked on Zoo-Code-Org#1395 (S3); tracking #1396)
B1
feat/per-write-checkpoints-b1
open — #1404 (amended to abfbe7f (round 3: task-start baseline now awaited before the request loop + deferred-promise test) — perWrite garbled i18n fixed in 7 locales, task-start baseline forced, CheckpointSettings spec updated; ALL checks green incl. ubuntu+windows unit + codecov/patch, independent on upstream main 78c712a: per-write checkpoints in write_to_file/edit_file/apply_patch — checkpoint now gated on full patch success (handlers report success; no checkpoint after rejected approval / failed write), task-start baseline guard (incl. unset default-on test), perWriteCheckpoints setting default-on round-trip incl. explicit-false webview-state test + 17-locale translations; ubuntu CI + e2e-mock + codecov green; all 5 CodeRabbit findings replied (1 false positive — single shared default constant verified); local gates: eslint 0, tsc 0 src+webview, affected suites green; tracking #1397)
S4a
feat/guarded-write-s4a
open — #1405 (amended to 7a25fc0 — enqueue settled-chain eviction + replaceIfVersion ENOENT normalization + regression tests; ubuntu + codecov/patch green after the amend; on S3 head a37dd24 + S1×3 + S2 stack: CAS core + per-path FIFO chain + registry hook; CodeRabbit pass fixed — resolveAbsolutePath always path.resolve (registry-key match), pre/post-read bigint stat token capture on native + legacy read paths (mutation mid-read leaves target unobserved), safeWriteJson locks the resolved publish target (symlink-alias coordination); local gates: 169 units, 100% line coverage on changed lines, eslint 0, tsc 0; 3/3 findings replied; tracking #1399)
S4b
feat/guarded-write-wiring-s4b
open — #1408 (rebased to 68be264 on amended S4a 7a25fc0 — inherits the ENOENT fix; CI re-running; guard wired once at the DiffViewProvider.saveDirectly choke point (6 tools / 7 call sites), per-tool writeKind plumbing, fail-closed on collected taskRef; local gates: 188 units, 100% on changed lines, eslint 0, tsc 0; tracking #1400)
B2
feat/change-journal-b2
open — #1406 (amended to 93a8329 (round 3: mistake counter now resets only on a fully successful patch + 2 regression tests) on B1 head baacf59 — handler result objects, no-op/move gating, partial-flush gate patchSucceeded
B3a
feat/change-cards-b3a
open — #1411 (amended to 2500ab3 (round 3: apply-patch + edit-file checkpoints now awaited, no fire-and-forget interleaving + 2 deferred-promise tests) on B2 head 410591e — round-2 CR fixes: checkpointSave.spec negative assertions now filter recorded say calls by the change_card type (the three-argument toHaveBeenCalledWith can never match the seven-argument call, so the old assertion guarded nothing), mergeExtensionState spec fixture uses non-default perWriteCheckpoints/changeCardDetail and asserts a partial push that omits the keys preserves them; CI re-running; change_card payload + checkpointSave emission, tool approval-diff plumbing (WriteToFile / EditFile / ApplyPatch), changeCardDetail setting full round trip, i18n 18 locales; split from the planned B3a scope by the 1000-line cap — 934 changed lines; local gates: 429 units, eslint 0 src+webview+types, tsc 0 both dirs, 100% on changed lines; round 11: head 341a9c2 (94fea2f + CodeRabbit fix: pre-hydration defaults spec now asserts both the changeCardDetail and perWriteCheckpoints initializer defaults; CI GREEN); round 12: head 67d8525 (trial-review audit: the edit and search_replace tools wrote with no per-write checkpoint / journal entry / change card — both now wired with the same checkpointSave call site + the auto-approved compact-card flag; focused 8-test spec; merge-tree clean vs upstream/main; CI running); tracking #1401)
B3c
feat/rollback-service-b3c
open - #1410 (round 13: head 630f273 - rollback semantic correction, upstream Zoo-Code-Org#1435: rollbackFile/rollbackStep resolve the restore target from the B2 change journal to the file's PRE-step state (preceding journal entry's checkpoint; task-start baseline for the file's first change; undoing a create deletes it again, undoing a delete restores it); new restoreLatestFile(task, filePath) forward direction (no-op success when the task never wrote the file); 20-test spec; incremental diff 2 files +371/-134; local gates: vitest 20/20 + checkpoints dir 79/79, tsc 0, eslint 0 (suppression unchanged), 100% changed-line coverage; merge-tree clean vs upstream/main efc30cf; CI running; tracking #1409 + semantic correction #1435)
B3b
feat/change-cards-ui-b3b
open - #1412 (round 13: head 9a430d6 stacked on B3c 630f273 - per-file "Restore latest version" control (two-step confirm + warning text), new typed checkpointRestoreLatestFile webview message + handler case (results reuse checkpointRollbackResult with kind rollback/restore-latest so the two per-file controls never cross-talk; legacy no-kind results still route), per-file rollback confirm now shows the warning text, i18n: 5 new/updated chat:changeCard.* keys x 18 locales, ChangeCard spec +5 tests, handler spec +4 tests; incremental diff 27 files +929/-150 (includes the B3c service files arriving via the stack); local gates: tsc 0 both dirs, eslint 0 (suppression unchanged), vitest 22+11 green, 100% changed-line coverage, i18n parity 18/18; merge-tree clean vs upstream/main efc30cf; CI running; raw-diff budget exception still documented in the PR body; tracking #1402 + semantic correction #1435)
open - #1413 (trial build of all 15 + the B3c/B3b semantic correction; round 13 head 7591bc8 = d6d08e8 + cherry-picks of Zoo-Code-Org#1410630f273 (f0f3313) and Zoo-Code-Org#14129a430d6 (7591bc8), both applied cleanly; local gates: tsc 0 both dirs, eslint 0, vitest 31+22 green, merge-tree clean vs efc30cf; Code QA CI running - PR body will be refilled with the new run/artifact/inner-vsix sha256 once it settles; screenshot release assets (v3.80.0-fws-trial tag) unchanged)
| TRIAL | feat/fws-trial-all | open — #1413 (trial build of all 15; head 4fc14c4 = 11 merge commits + addenda 6a0feb1 + 376e013 (spec composition, apply_diff self-observation) + 178e6f4 (CodeRabbit round 3: apply_patch observation, mcp merge array guard + spec-mock parity, safeWriteJson doUnmock, webview writeDelayMs shared default, ChangeCard no-files step + grow, CheckpointSettings sibling, Catalan) + d2239ce (restore safety: rev-parse checkpoint verification + realpath symlink containment) + 4fc14c4 (CodeRabbit round 4: McpHub.spec unknown-safe error.code guard, exported createInitialExtensionState + pre-hydration defaults test); all CodeRabbit findings replied — cross-process TOCTOU scoped out and tracked as upstream follow-up Zoo-Code-Org#1414 with epic risk wording proposed; all 15 component PR heads verified mergeable into upstream/main; CI on 4fc14c4 GREEN (ubuntu unit + e2e-mock + codecov/patch + compile + VSIX build all success); PR Zoo-Code-Org#1413 body updated to the green run 33134141568 (artifact 9671394094, inner vsix sha256 1cbe1793...); all CodeRabbit findings confirmed incl. the round-4 pair (01:59-02:02 UTC); the 8 component PRs re-pushed with the propagated fixes (round-10 notes on each row) and all 8 new heads merge-checked clean against upstream/main; round 11: head 83d83a3 = previous + addendum 6 (re-merge of the updated Zoo-Code-Org#1411 94fea2f + Zoo-Code-Org#1412 c48522c after the trial-review user feedback: apply_diff per-write checkpoint + change card; change-card open-in-editor control incl. the CodeRabbit a11y native-button fix; CI GREEN); round 12: head d6d08e8 = 83d83a3 + addendum 7 (re-merge of Zoo-Code-Org#1411 341a9c2 + Zoo-Code-Org#1412 502f8ca after the CodeRabbit round-11 fixes; CI GREEN run 33161152644 — inner vsix sha256 016e930f…, PR body on that green run; no visible UI change) + addendum 8 (re-merge of Zoo-Code-Org#1411 67d8525 — the trial-review audit found the edit and search_replace tools wrote with no per-write checkpoint / journal entry / change card; both now wired with the same checkpointSave call site as the other four write tools + focused 8-test spec; CI running); fork PR #34 (→ trial) still open, CI 15/15 green, re-verified mergeable against the new trial tip d6d08e8; round 13 (2026-08-29): the CodeRabbit review round was fixed in the introducing PRs — Zoo-Code-Org#1410 → 39afe1b (a card whose file was re-written in a later step is now refused instead of overwriting the newer write, and an unreadable journal fails loudly instead of a false no-op), Zoo-Code-Org#1411 → c202745 + 8c5c53b (typed settings test doubles + nl changeCardDetail sentence), Zoo-Code-Org#1412 → a0693e9 + ea90ea8 (correlated webview failure results, localized no-task results, focusable change-card error states, openFile path labels, es/hi/it/ko locale corrections, schema-invalid spec coverage); all 18 CodeRabbit findings replied (15 original + 3 round-2 on Zoo-Code-Org#1412) and CR confirmed each fix; all four heads merge-checked clean vs upstream/main (efc30cf) and ubuntu CI green (Zoo-Code-Org#1410 6/6, Zoo-Code-Org#1411 6/6 — the round-1 mocked-E2E failure was a history-write/restart race flake, green on re-run, Zoo-Code-Org#1412 8/8); trial re-merged via addendum 9 (779cb4b, empty CI-retry commit for the flake) + addendum 10 (22a3f33, 46 files +496/-99) — CI GREEN run 33257633298 (inner vsix sha256 aaf90d5e…), PR Zoo-Code-Org#1413 body updated to that green run (artifact 9716305621); fork PR #34 re-verified MERGEABLE/CLEAN against the new trial tip 22a3f33; round 14 (2026-08-30): upstream/main moved efc30cf → 147147c (5 commits: Zoo-Code-Org#1434 the subtask-approval mocked-E2E timeout fix — the exact flake that failed Zoo-Code-Org#1411's round-1 run, Zoo-Code-Org#1426 Extension Host visual regression rework incl. new electron visual suite + playwright harness restructure, Zoo-Code-Org#1445 label clipping, Zoo-Code-Org#1446 typed host message capture, Zoo-Code-Org#1449 partial-ask e2e ignore); all 16 series heads re-verified merge-tree CLEAN against the new tip 147147c; required checks per the main ruleset = 6 (check-translations, platform-unit-test ubuntu + windows, compile, knip, e2e-mock) — all green on every head; the org is rolling out a new PR review gate (Zoo-Code-Org#1437: required CI → CodeRabbit current-head review → human maintainer approval, SQUASH merge queue ALLGREEN) — its 'PR review gate' status is advisory for fork PRs (hard enforcement = native required checks + review protections) and no code change is needed for it; Zoo-Code-Org#1412 carried a stale CodeRabbit CHANGES_REQUESTED (all findings fixed + individually confirmed by CR) so it was labeled awaiting-author — /approve requested on Zoo-Code-Org#1412 and /review requested on Zoo-Code-Org#1411 + Zoo-Code-Org#1413 at 03:49Z but CodeRabbit picked up no manual command within 30 min (the new gate flow owns CR triggering; the org CR config may restrict fork users), so empty commits were pushed to re-trigger the native synchronize flow: Zoo-Code-Org#1411 8c5c53b → 0d31ea0, Zoo-Code-Org#1412 ea90ea8 → 63dcd24 (the push also dismisses the two stale CodeRabbit CHANGES_REQUESTED reviews per the ruleset dismiss_stale_reviews_on_push), Zoo-Code-Org#1413 22a3f33 → 5cb1d96 (addendum 11); zero code change (empty commits), all three re-verified merge-tree CLEAN against main 147147c; NOTE: every one of the 16 series PRs is currently mergeable=true / mergeStateStatus=blocked — the block is the ruleset requiring 1 approving review + code-owner review, i.e. awaiting maintainer review, not CI; CI (6 required checks) is green on all 16 heads; the only remaining automation work is CodeRabbit current-head reviews on the three re-pushed PRs (watching), after which the series is fully at the human-approval stage; round 15 (2026-08-30, after inspecting Zoo-Code-Org#1440 which cleared the bot stage): the gate flow per Zoo-Code-Org#1440's process comment = (1) required CI green, (2) workflow starts CodeRabbit automatically, (3) CodeRabbit APPROVES the latest commit (native review state — COMMENTED/acked findings do NOT count), (4) human maintainer approval; Zoo-Code-Org#1440 cleared step 3 with a clean re-review (zero new comments → APPROVED in 2 min) and sits at awaiting-maintainer; our 16 PRs were all at the gate step "Wait for CodeRabbit to approve the latest commit" (CR states were COMMENTED), so all 16 got the same empty-commit re-trigger: 1380→046b78cad, 1381→309ffd644, 1382→b67e57312, 1383→2a42f886f, 1384→d03877438, 1394→a00eef8e0, 1395→7fd49bcea, 1403→cb1360613, 1404→abc10b43e, 1405→be894d9e0, 1406→7b83143cf, 1408→e96df628a, 1410→87471b558 (plus the earlier 1411→0d31ea062, 1412→63dcd24e2, 1413→5cb1d9674); zero code change in all 16 (empty commits), all 16 re-verified merge-tree CLEAN vs main 147147c, all 16 bodies carry a review-gate re-trigger note (code heads unchanged); the "PR review gate" status stays pending by design until a maintainer approves (advisory — the hard gates are the 6 required checks [all green] + 1 approving review + code-owner); watching CI + CR approvals on the 16 new heads; round 16 (2026-08-30): gate-mechanism forensics + @coderabbitai canary result + 1412 CI clear. (a) The review gate (guide comment + "Zoo Code / PR review gate" status + awaiting-coderabbit/awaiting-maintainer labels) is produced by the NEW label-pr-review-state.yml which exists ONLY on Zoo-Code-Org#1437 gate branch feature/human-pr-review-gate-0shw6cv6mcu (main still runs the old label-only reconciler, last touched 07-13); it executes when zoomote[bot] workflow_dispatches it from that branch — dispatch run 33280010451 (08-29 23:02Z) batch-posted Zoo-Code-Org#1440 plus 13 of our guide comments in a single run. Zoo-Code-Org#1440 bot pass, end to end: real-diff push (18:10Z) → CodeRabbit native APPROVED review on the exact head commit within ~2 min (zero findings) → workflow phase "maintainer" (awaiting-maintainer label + gate step "A human maintainer must now review and approve it"); the gate status is advisory — the hard gates are the 6 required CI checks + 1 approving review + code-owner review (re-verified against ruleset 15494382 "Protect Main", which also has a merge_queue rule with ALLGREEN grouping). (b) CANARY NEGATIVE: @coderabbitai review mention on Zoo-Code-Org#1381 (05:58Z) received a CodeRabbit reply within 7 seconds: "Action not completed — No files to review. CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused." → no review object can be created on an empty-commit head; consistent with the 90-min watcher showing zero fresh CR reviews on all 16 heads → the mention path cannot clear the CR stage; no rollout performed. (c) 1412 addendum 12 (empty commit ab281f5) cleared the flaky ubuntu unit run (the vitest EnvironmentTeardownError x9 was an infra false positive, no assertion failures) → 1412 now 6/6 required green and stable. ALL 16 PRs now: 6/6 required checks green, mergeable, zero mechanical blockers; the only remaining gates are the two human rules (1 approving review + code-owner review), plus — for the merge-queue path only — the advisory extension-host-visual on the 6 B1-stack PRs, whose root cause (B1 task-start baseline checkpoint → SeeNewChangesButtons rendering below the completion row against the pre-FWS Zoo-Code-Org#1426 baseline; deterministic 432-px delta per Playwright / 973 px in the raw PNG diff; pixel-identical across 1404/1406/1410/1411/1412/1413) is documented in those PR bodies for maintainer disposition. The CR stage clears automatically if Zoo-Code-Org#1437 merges (its .coderabbit.yaml gates auto-review on the coderabbit-review-active label all 16 carry); otherwise the path to merge is direct maintainer approval (the gate comment is advisory). |# [ENHANCEMENT] File-write safety series (upstream epic Zoo-Code-Org#1375): version-guarded atomic writes + per-step visibility/rollback — PR series plan
1. Goal
Make agent file writes fast, loud-failing, self-healing, atomic, and auditable — in that order.
2. Series overview
3. PR specs
S1 — A1 Version token
src/utils/versionToken.ts:computeVersionToken(filePath): Promise<string>+ pureversionTokenOfStat(stats)for testability.dev:ino:size:mtimeNs:ctimeNsfrom onefs.stat; ns fields as decimal strings from BigInt (no float precision loss).S2 — A2 Observation registry
src/core/task/observationRegistry.ts: per-taskMap<absolutePath, { version, observedAt }>; owner = task, so parent and subtask observations are independent.Taskowns an instance;ReadFileToolrecords an observation after a successful read (one extra stat, same call the write guard will reuse).S3 — A4 Atomic publish
src/services/file-safety/safeWriteText.ts: temp file in a private per-write staging dir → write → fsync → close → atomic rename; on Windows: ReplaceFile with DACL copy, rename fallback. Generalizes the staging/backup/rollback logic currently insidesafeWriteJson(which is refactored to call this).saveDirectly(all five write tools + write_to_file) switches from rawfs.writeFiletosafeWriteText.safeWriteJsonexisting suite still 100% (no regression); patch coverage 100%.saveDirectlyno longer calls rawfs.writeFile;safeWriteJsonbehavior unchanged.S4 — A3 Guarded write/edit (the behavior change)
src/core/tools/guardedWrite.ts: compare-and-swap on the write path:createIfAbsent: new file succeeds; existing file fails (forces a read first — the model re-reads and retries with the observed version);createIfAbsent;replaceIfVersion(version): mismatch fails with "stale version — re-read the file, then retry";S5 — A5 Zoo-Code-Org#920 cross-instance regression test
updateTaskHistoryon the locked-merge write; asserts the second instance's merge preserves the first's fields (no clobbered history item).fix/920-concurrent-task-history-cross-instance(5 commits, prior work).S6 — A6 Zoo-Code-Org#1221 truncated tool-call parser
NativeToolCallParser: a finalization failure must not reuse the streaming-phasenativeArgs(partial JSON). Fail the tool call with an explicit "arguments were truncated" error instead of writing truncated content to disk.L1 — Async post-save diagnostics
saveDirectlytail: instead of awaiting LSP diagnostics, emit them as an asynchronous follow-up event once settled.L2 — Chat-diff default approval path
PREVENT_FOCUS_DISRUPTIONdefaultfalse→true(src/shared/experiments.ts:22); toggle kept as escape hatch, storage key unchanged (§6).B1 — Per-write checkpoint
<globalStorage>/tasks/<taskId>/checkpoints.git-in-shadow can show that step's snapshot; task start is cheap (no worktree).perWriteCheckpoints(boolean, defaulttrue) — master switch for the B cluster; full Persisted Setting Checklist round trip ships in this PR (§6).B2 — Per-task change journal
changes.jsonlunder the task dir; one entry per file write: path, operation, checkpoint id, diff stats (additions/deletions from the already-computed approval diff).B3 — Per-step change cards + rollback
changeCardDetail("full" | "summary", default"summary") — round trip ships in B3a (§6).4. Order
5. Risks
6. Configuration / user settings
Principle: the safety core (A1–A6) is not configurable — an off-switch would reintroduce the corruption this epic fixes. Configurable surface = the B cluster (visibility/rollback convenience) + existing behavior toggles (speed, approval surface). Every new setting follows the AGENTS.md Persisted Setting Checklist — full round trip:
global-settings.ts(definition + shared default constant) →ExtensionState/message types →SettingsViewbinding to localcachedState(never live state) →updateSettingspayload →webviewMessageHandlerpersistence via ContextProxy →ClineProvider.getState()with default →getStateToPostToWebview()(destructure + return) → every consumer same default semantics → import/export schema → focused tests (UI binding/save, persisted + unset via posted state).New settings
perWriteCheckpointstruechangeCardDetail"summary"summary= one line ("N files changed +X −Y") + per-file list + rollback, diff rendered lazily on expand;full= inline unified diff rendered by default. Auto-approval steps always get the compact card regardless.Existing settings (reused — no new key)
writeDelayMsDEFAULT_WRITE_DELAY_MS); setting, UI, round trip unchanged.PREVENT_FOCUS_DISRUPTIONfalse→true(experimental map,src/shared/experiments.ts:22). Keep the toggle as the escape hatch for users who prefer diff-editor approval. Decision inside the L2 PR: if the experimental settings section is hard to discover, surface it as a first-class approval-surface setting — move/rename in the UI only; the storage key stays stable so existing user values carry over.Deliberately NOT settings (decision record)
Round-trip cost and diff budget
Each new setting touches ~8–10 files (types, extension host, provider, webview settings, schema, tests) ≈ 100–300 changed lines, and counts toward the diff budget (800-line target / 1000 hard cap):
perWriteCheckpointsround trip ships inside the B1 PR (not a separate PR) — it is part of the 2d B1 estimate.changeCardDetailround trip ships inside B3a (extension-host side); if B3a + round trip exceeds budget, B3a keeps setting + payload and B3b is UI-only.experiments.ts+ its spec update — trivial.updateSettings; posted state asserts both persisted and default; import/export round-trips.check-translationsCI covers it.7. E2e plan (apps/vscode-e2e, aimock)
Principle (AGENTS.md test pyramid): e2e only for real extension-host boundaries and full-workflow smoke; detailed assertions stay at unit/spec. Existing base suites to extend:
suite/tools/write-to-file.test.ts(real task → write_to_file → disk assertions, aimock-replayed) andsuite/subtasks.test.ts(+fixtures/subtasks.ts). New fixtures follow the aimock workflow:TEST_FILE-filtered record runs,toolCallIdmatching for turn 2+, match strings without timestamps/paths, verify withpnpm --filter @roo-code/vscode-e2e test:ci:mock.write_to_fileon an existing file the model never read → expect the tool-call failure step ("file not read yet") →read_file(matched by toolCallId) →write_to_fileretry →attempt_completion; assert final on-disk content = new content and the chat step shows the failure + success. E2 concurrent subtask writers (ships with S4b or as a follow-up small PR): extend the subtasks suite — two subtasks write the same file; assert final content is one complete version (no torn/interleaved file); keep ordering assertions loose to avoid CI flake8. Per-PR execution rules (AGENTS.md)
pnpm --dir src exec vitest run <path>; suite green before push.pnpm --dir src exec eslint --max-warnings=0 <files>; suppression counts never increase..changesetfiles, no CHANGELOG edits (per AGENTS.md).9. Acceptance criteria (series complete)
pnpm --filter @roo-code/vscode-e2e test:ci:mock), including E1 (stale-write self-heal), E2 (concurrent subtask writers), E3 (truncated tool call writes nothing), and E4 (step rollback end-to-end).perWriteCheckpointsandchangeCardDetailcomplete the full Persisted Setting Checklist (UI save via cachedState, posted-state persisted + default assertions, import/export round trip, all locales);PREVENT_FOCUS_DISRUPTIONdefaults totruewith the existing toggle preserved.Execution status (synced 2026-08-29 (round 13): UI/UX review of the change-card rollback flow found a P1 semantic bug - the card rollback restored a file to the step's own POST-write checkpoint (a no-op for the newest step; contradicts the epic AC "undo what the agent did" and the card's own confirm copy). User chose Option A + forward restore, shipped as: (1) Zoo-Code-Org#1410 head 630f273 - rollback now resolves the restore target from the B2 change journal to the file's PRE-step state (preceding journal entry's checkpoint; task-start baseline for the file's first change; undoing a create deletes it again, undoing a delete restores it) + new restoreLatestFile (forward direction; clean no-op success when the task never wrote the file); 20-test spec; all local gates green (vitest 20/20, tsc 0, eslint 0 with suppression unchanged, 100% changed-line coverage); merge-tree clean vs upstream/main efc30cf. (2) Zoo-Code-Org#1412 head 9a430d6 (stacked on 630f273) - per-file "Restore latest version" control (two-step confirm + warning), new checkpointRestoreLatestFile webview message + handler case (results reuse checkpointRollbackResult carrying kind rollback/restore-latest; legacy no-kind results still route to the rollback control), per-file rollback confirm now shows the warning text, 5 new/updated chat:changeCard.* keys x 18 locales, +5 ChangeCard tests + +4 handler tests; merge-tree clean vs efc30cf. New upstream tracking issue Zoo-Code-Org#1435 documents the semantic correction; both PR bodies refilled. Trial addendum 9: head 7591bc8 (cherry-picks f0f3313 + 7591bc8 applied cleanly onto d6d08e8; local gates: tsc 0 both dirs, eslint 0, vitest 31+22 green, merge-tree clean; Code QA CI running). Fork PR #34 (-> trial) remains open, CI 15/15, head 7efbf33 merge-tree clean vs the new trial tip 7591bc8. The fuzzy-match observability/perf suggestions stay tracked as upstream Zoo-Code-Org#1418; all other previously-green heads unchanged)
Blockers to watch: Zoo-Code-Org#1066 (S6), Zoo-Code-Org#1319 (S5), Zoo-Code-Org#1046 landing (raises A3 urgency), Zoo-Code-Org#1379 merge (Task.ts rebase).