Skip to content

[ENHANCEMENT] File-write safety series (upstream epic #1375): version-guarded atomic writes + per-step visibility/rollback — PR series plan #33

Description

@easonLiangWorldedtech

[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.

2. Series overview

PR Epic item Scope Deps Est. Status
Phase 0a A5 — Zoo-Code-Org#1371 Atomic mcp_settings.json stub creation (safeWriteJson + merge under advisory lock) done open (#1380)
Phase 0b Speed DEFAULT_WRITE_DELAY_MS 0 + remove delay(300) done open (#1381)
Phase 0c A5 — Zoo-Code-Org#1021 saveClineMessages abandoned guard done open (#1382)
S1 A1 Version token (pure function + unit tests) 0.5d OPEN (#1383)
S2 A2 Per-task observation registry (ReadFileTool populates) S1 1d OPEN (#1394)
S3 A4 Atomic publish: safeWriteText (staging + fsync + rename / Windows ReplaceFile+DACL) 2d OPEN (#1395)
S4a A3 Guarded write core: CAS (createIfAbsent / replaceIfVersion) + per-path FIFO chain S1+S2+S3 2d OPEN (#1405, tracking #1399)
S4b A3 Guarded write tool wiring (WriteToFile / EditFile / SearchReplace / ApplyPatch / ApplyDiff) S4a 1d OPEN (#1408, tracking #1400)
S5 A5 — Zoo-Code-Org#920 Cross-instance updateTaskHistory regression test — (track Zoo-Code-Org#1319) 1d planned
S6 A6 — Zoo-Code-Org#1221 Truncated tool-call parser fix (no partial-args reuse) stacked on Zoo-Code-Org#1066 1d blocked
L1 Speed Async post-save diagnostics (follow-up event, not awaited) S3 1d OPEN (#1403, tracking #1396)
L2 Speed Chat-diff (PREVENT_FOCUS_DISRUPTION) as default approval path 1d OPEN (#1384)
B1 B1 Per-write checkpoint (extend shadow git; O(1) task-start baseline) S3 2d OPEN (#1404, tracking #1397)
B2 B2 Per-task JSONL change journal (torn-tail repair) B1 1d OPEN (#1406, tracking #1398)
B3a B3 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)
B3c B3 Per-file/per-step rollback service (extension host; split from B3a) B3a 1d OPEN (#1410, tracking #1409)
B3b B3 Change cards UI + rollback buttons (webview) B3a+B3c 1d tracking (#1402)

3. PR specs

S1 — A1 Version token

  • New file src/utils/versionToken.ts: computeVersionToken(filePath): Promise<string> + pure versionTokenOfStat(stats) for testability.
  • Token format: dev:ino:size:mtimeNs:ctimeNs from one fs.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 file src/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 file 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 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 file src/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.
  • Wired into WriteToFile / EditFile / SearchReplace / ApplyPatch / ApplyDiff.
  • 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.

S5 — A5 Zoo-Code-Org#920 cross-instance regression test

  • 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).
  • Track [Fix] Task history disappears when user reopens a task Zoo-Code-Org/Zoo-Code#1319 (task-history safe-write retry + advisory-lock merge) — if it lands first, adapt or close as covered; do not double-fix.
  • Acceptance: test reproduces the clobber on the pre-fix code path (or documents why it no longer reproduces) and passes post-fix.

S6 — A6 Zoo-Code-Org#1221 truncated tool-call parser

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 falsetrue (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.
  • Setting: changeCardDetail ("full" | "summary", default "summary") — round trip ships in B3a (§6).
  • 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.

4. Order

  1. Phase 0 (open): fix(mcp): preserve concurrent MCP settings during initial creation (fixes #1371) Zoo-Code-Org/Zoo-Code#1380fix(task): guard saveClineMessages against abandoned tasks (fixes #1021) Zoo-Code-Org/Zoo-Code#1382perf(write-path): remove artificial write delays by default (part of #1375) Zoo-Code-Org/Zoo-Code#1381 land in any order; they are independent.
  2. S1 — first of the series (pure function, unblocked, unblocks S2).
  3. S3 ∥ S5 ∥ L2 ∥ S2 — S3 and S5 are independent of S1/S2; S2 needs S1. Interleave by CI availability.
  4. S4 — after S1+S2+S3 all merged.
  5. L1 — after S3 (it touches the saveDirectly tail).
  6. B1 → B2 → B3 — B chain after S3 (B1 records at the publish point; independent of S4 but sequenced after S3 to sit on the atomic-publish path).
  7. S6 — whenever fix(write-to-file): address partial filesystem error review Zoo-Code-Org/Zoo-Code#1066 is ready (stack on its head).

5. Risks

Risk Mitigation
S4 changes model-visible behavior (unobserved writes now fail) Remediation suffix makes the loop self-heal; loud+recoverable-stale is the epic's explicit model; regression suites for all five tools must stay green
Zoo-Code-Org#1379 (DTE mega-PR) touches Task.ts — rebase hazard for S2/S4/S5 Land S2 before Zoo-Code-Org#1379 merges if possible; otherwise rebase S-series on the newer head
S6 depends on Zoo-Code-Org#1066 (open since 08-23) Stack on Zoo-Code-Org#1066's head; do not cherry-pick around it
S5 overlaps Zoo-Code-Org#1319 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.

Existing settings (reused — no new key)

Key Phase Action
writeDelayMs 0b (open) Done: default 1000 → 0 (DEFAULT_WRITE_DELAY_MS); setting, UI, round trip unchanged.
PREVENT_FOCUS_DISRUPTION L2 Flip default falsetrue (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.
  • pnpm --dir src exec eslint --max-warnings=0 <files>; suppression counts never increase.
  • No .changeset files, no CHANGELOG edits (per AGENTS.md).
  • Patch coverage 100% (codecov) on every PR.
  • PR body: Summary / Changes / Tests / Provenance (when extracting from a feature branch), linking this issue + epic [EPIC] File Write Safety Prevent Concurrent Write Races Data Corruption Zoo-Code-Org/Zoo-Code#1375.

9. Acceptance criteria (series complete)

  • 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).
  • Epic [EPIC] File Write Safety Prevent Concurrent Write Races Data Corruption Zoo-Code-Org/Zoo-Code#1375 acceptance criteria met: no silent apply failures; loud + recoverable stale; per-step visibility; multi-agent safety; task start is a real baseline; write-path default latency ≈ 0; suppression counts unchanged.
  • 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-28 (round 5): B2 access-denied + failed-move partial-flush fixes + all CR sweep fixes — final SHAs b674c42 / a4e7311 / 78bd752 / 2b4a8ce / a0bb49b; S3+L1 + S4/B sweep fixes green

PR Branch State
Phase 0a Zoo-Code-Org#1380 fix/mcp-settings-stub-race-1371 open, CI green, awaiting review (tracking #1385)
Phase 0b Zoo-Code-Org#1381 fix/write-delay-default open, CI re-running (fixed a missed DiffViewProvider spec assertion), awaiting review (tracking #1386)
Phase 0c Zoo-Code-Org#1382 fix/abandoned-subtask-save-race-1021 open, CI green, awaiting review (tracking #1387)
S1 Zoo-Code-Org#1383 feat/version-token-s1 open — #1383 (3 commits; final review APPROVE; CI green on ubuntu gate; tracking #1388)
L2 Zoo-Code-Org#1384 feat/l2-chat-diff-default open — #1384 (74 lines, 1 commit; ALL checks green incl. ubuntu+windows unit + codecov/patch; CodeRabbit 3/3 findings confirmed fixed & replied in c0ef476; tracking #1389)
S3 Zoo-Code-Org#1391 feat/atomic-publish-s3 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)
S2 Zoo-Code-Org#1390 feat/observation-registry-s2 open — #1394 (stacked on Zoo-Code-Org#1383, targets main; CodeRabbit findings addressed in 2965ad1, ubuntu CI green; tracking #1390)
L1 Zoo-Code-Org#1396 feat/async-save-diagnostics-l1 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 b674c42 (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 a4e7311 (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 78bd752 (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; tracking #1401)
B3c feat/rollback-service-b3c open — #1410 (amended to 2b4a8ce on B3a head 4e5dc4b — restoreFile POSIX toPosix normalization + win32 regression test; round-2 CR fix: restoreFile now constrains the target to the workspace before the checkout/delete branch (CWE-22 containment via path.resolve + trailing-separator prefix check) with a traversal regression test; CI re-running; rollbackFile / rollbackStep + ShadowCheckpointService.restoreFile + fileExistsInCommit; 434 lines; local gates: eslint 0, tsc 0, 100% on changed lines; tracking #1409)
B3b feat/change-cards-ui-b3b open — #1412 (amended to a0bb49b on B3c head 5f88c41 — round-2 CR fixes: no-task rollback now posts a correlated checkpointRollbackResult failure (the requesting card clears its pending state), ChangeCard validates the parsed payload with changeCardSchema (a truncated/pre-series record no longer throws during render), the settings spec dirts the form via the per-write control before Save, and the handler lazy-imports the rollback module so specs that mock vscode minimally no longer execute the checkpoint/editor import graph (fixes the 16-file import-chain failure on ubuntu); CI re-running; stacked on B3c: ChangeCard chat component (summary lazy-diff / full inline / compact), per-file + per-step rollback buttons with the typed webview→extension channel (checkpointRollbackFile/Step → B3c rollbackFile/rollbackStep → checkpointRollbackResult by cardTs), the changeCardDetail settings control (pre-staged from the B3a split), chat i18n all 18 locales (en-only verified to fail check-translations parity); local gates: webview 162 files / 1781 tests, src rollback spec 6, eslint 0 all dirs, tsc 0 both dirs, 100% on changed lines; raw diff 1300 (1226 at first push; +74 round-2 fixes) — budget exception documented in the PR body; tracking #1402)
S5 blocked on upstream Zoo-Code-Org#1319 (open)
S6 blocked on upstream Zoo-Code-Org#1066 (CHANGES_REQUESTED)

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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions