Repository navigation
feat(tools): wire guarded writes into the diff-view save paths (S4b, #1375) - #1408
Open
easonLiangWorldedtech wants to merge 79 commits into
Open
easonLiangWorldedtech wants to merge 79 commits into
easonLiangWorldedtech wants to merge 79 commits into
Conversation
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related GitHub Issue
Closes: #1400 — "Tracking S4b: wire guarded writes into the diff-view save paths" (this unit's tracking issue in this repository).
Description
Split unit S4b of the guarded-write chain: wires the S4a compare-and-swap guard into every write tool and the diff-view save paths. What this head actually does:
write_to_file/edit_file/search_replace/apply_diff/apply_patchroute their publish throughguardedWrite: an unobserved write to an existing file fails loudly ("read the file first, then retry"), a stale-version write fails with the re-read-then-retry remediation, and the model self-heals through its standard read-retry loop — no silent overwrite path remains. Failures surface as tool-call errors with a step event in chat.edit_filekeeps its existing literal-match check and adds the version guard on top.apply_patchin-place publish records the read observation for the hunk target (stat before/after, observe when the version is unchanged) so the guarded publish is not rejected as an unobserved write; the move publish carries the source's completeness to the destination and publishes throughsaveDirectly(..., "create").guardedWritecarries the S4a CAS core (createIfAbsent/replaceIfVersion, per-path FIFO chain) plus a disposal guard: a queued guarded write checks the task's disposed flag at the head of its queue link and throwsCancelledTaskWriteErrorbefore publishing anything.DiffViewProvider.saveDirectly/saveChangespublish through the guard; a refused guarded publish rolls the editor buffer back.ReadFileToolcaptures the S2 observation on read. The unit carries its own copies of the S1–S4a primitives (observationRegistry,guardedWrite,safeWriteText,safeWriteJson) because unit branches are not cumulative; the declared merge order lands S1–S4a first and this branch's copies reconcile against main at rebase.Honest contract: the version check and the commit rename are separate syscalls — the window is one syscall wide, documented at
guardedWrite.tsandsafeWriteText.ts. The chain-level owed changes (canonical lock / compare-and-publish, publish-target confinement, backup cleanup policy) are registered on the chain tracking issue (record 6106959976).Content source of record: the S4b unit of the guarded-write chain declared on tracking issue 1991; branch
feat/guarded-write-wiring-s4bcarries the unit content on the current org main tip. Trial review context: org PR 1413.Budget (GitHub view against the base): +4931/−179 across 26 files. The bulk is the carried primitive copies and their specs; a primitive copy and the spec that tests it cannot be separated without breaking the fidelity contract. Documented deviation: the mutation-diff gate reports 504 changed executable lines against the 500 cap (4 over); the maintainer declares this a budget-cap deviation, not a split trigger — this unit is superseded content that reconciles at rebase once the U-chain primitives land in main (chain tracking issue 1991).
Test Procedure
Two
DiffViewProvider.spec.tstests fail only on the Windows junction donor, where@roo-code/typesresolves to a stale donor build: "should use default values when no parameters provided" (known local-only) and "pins the default write delay to zero" (this branch definesDEFAULT_WRITE_DELAY_MS = 0inpackages/types/src/global-settings.ts; the donor dist exports 1000). CIplatform-unit-testpasses both on ubuntu and windows.Pre-Submission Checklist
Documentation Updates
No user-facing documentation updates are required.
Verification at this head (aab1cb9)
--max-warnings=0clean on all 25 changed files;tsc --noEmitlocal baseline carries 137 donor-junction artifacts in files outside this PR's diff (the donor@roo-code/typesdist predates main commits this branch merged) — CI compile is green;src/eslint-suppressions.jsonunchanged.Updated this round
Additional Notes
Merge order: S1 (PR 1383) → S2 (PR 1394) → S3 (PR 1395) → S4a (PR 1405) → this unit (S4b). Unit branches are not cumulative; this branch carries copies of the earlier primitives by design, and the mutation-diff budget row (504 changed executable lines against the 500 cap) is a maintainer-declared documented deviation (see Budget).
Main refresh (7c11826)
Refreshed onto main
4f1dee18e(merge314209a11, conflict resolutions: read-token pairing blocks kept alongside main'scatch {narrowing in EditTool/SearchReplaceTool, spec import union; lint-gate commit drops imports unused after the merge and adds the now-requiredtoolCallIdto the branch specs' ToolCallbacks literals). tsc drift-only, 297/303 affected specs, mergeable true. Body dispositions unchanged.CI round final head (7ad72aa)
Lint-gate commit drops the
SafeWriteTextOptionsimport main's side orphaned. All six required checks green at7ad72aab2.Corrections, 2026-11-09 — measured at head
9fa7737142fcf25d254d6af9bca838eaf7d3f459The sections above are left exactly as written; this section adds the numbers that hold now.
+4931 / -179git diff --numstat 73931fe8be253b93ec11103a0672c615e849f895 9fa7737142fcf25d254d6af9bca838eaf7d3f459check-translations,compile,knip,e2e-mock,platform-unit-test (ubuntu-latest),platform-unit-test (windows-latest); all six were success at the previous headff4686cd18…, four are success herevitest.core.config.ts→ 200 files, 4351 passed | 9 skipped, exit 0.vitest.unit.config.ts→ 578 files, 12514 passed | 37 skipped, 11 failed, exit 1.tspaths (0 messages, exit 0); the diff carries 26 paths and the 26th issrc/eslint-suppressions.json, which is not a lintable source fileeslint --max-warnings=0over the changed filescompleted / failureatff4686cd18…,queuedat this head — advisory, not chasedGET /commits/{sha}/check-runsDEFAULT_WRITE_DELAY_MS = 0is real (packages/types/src/global-settings.ts:26) but that file is not in this unit's diff, so it is a statement about main rather than about this change. The unit-lane failures named above (MarketplaceManager×4,rulessymlink rows ×5,parser×2TelemetryService not initialized) reproduce identically at the parentff4686cd18…, so they are this Windows box's donor-node_modulesclass, not this head's.Arm status at this head:
check-translations,compile,knip,e2e-mocksuccess;platform-unit-test (windows-latest)failure (run42565686409, job128991579779, step Run extension test lanes →pnpm run test:core,Tests 9 failed | 4354 passed | 9 skipped (4372));platform-unit-test (ubuntu-latest)cancelled by that run's fail-fast.The windows failure is 9 rows of
src/core/tools/__tests__/applyDiffTool.outsideWorkspace.spec.ts— a file that is not on this branch. Main took it in1b9cb7d35(#2343) at 2026-11-09T00:14:38Z, after the 2026-11-08T20:36:39Z run that made the previous head green, and CI checks outrefs/remotes/pull/1408/merge: it type-checks and tests the head+main merge tree, not the head. Measured in that tree (git merge-tree --write-tree 9fa7737142… e487cd7b2a…→ tree73632a8b3e57…, clean): main tip alone 11 passed (11); the merged tree 9 failed | 2 passed; with the spec'sfs/promisesdouble given astatand itsdiffViewProvider.open()double recording its re-read — 11 passed (11) and that tree's whole core lane 201 files, 4363 passed | 9 skipped, exit 0. Nothing on this branch has to change for that: the main-side fix is #2372 and the windows arm is recorded here as red pending it rather than patched around on this branch. Carrying the file across was built and measured and rejected —git merge-tree --write-treethen exits 1 with anadd/addconflict on that path (stage 2 = main'scbaa2433ca5e…, stage 3 = the carried-and-adaptedbf27e65ce6d8…), trading one red matrix arm for a conflicted pull request.Housekeeping: two review replies here initially cited
9fa775714, a typo for9fa7737142…— sorry — corrected in place (comments4509629757,4509629950, plus correction reply4509643728).