fix(tools): classify no-op file mutations#97000
Closed
wuqxuan wants to merge 1 commit into
Closed
Conversation
Merged
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
What Problem This Solves
Fixes #96983.
No-op file mutations in agent-controlled tools could previously look like successful progress or ordinary retryable failures. In particular, identical
writecalls rewrote the file and returned success, identicaleditcalls threw only an unstructured no-change error, andapply_patchupdate hunks could report a modified file even when the patch produced identical content.Why This Change Was Made
This change classifies no-op file mutations at the built-in file-tool boundary instead of adding broader loop policy:
writenow returns a terminalblockedresult withreason: "no-op-write"when precheck proves the target already has identical content, without calling the write operation.editnow converts its no-change edit condition into a terminalblockedresult withreason: "no-op-edit", without writing the file.apply_patchnow skips no-op update hunks and returns a terminalblockedresult withreason: "no-op-patch"when the entire patch changes nothing.Real write, edit, add, delete, move, and patch mutation paths remain unchanged.
User Impact
Agents get a machine-readable terminal result for no-op file mutations instead of false progress or a generic retryable error. This should reduce repeated identical write/edit/patch loops while preserving normal file mutation behavior.
Evidence
Behavior addressed: no-op file mutations should be terminal blocked tool results, not successful writes or unstructured retryable failures.
Real environment tested: local OpenClaw source checkout on branch
codex/no-op-file-mutations, commitc74a317888f201ea73797002b222dbf0ac7d7c4a.Exact steps or command run after this patch:
node scripts/run-vitest.mjs src/agents/sessions/tools/write.test.ts src/agents/sessions/tools/edit.test.ts src/agents/apply-patch.test.ts node scripts/run-vitest.mjs src/agents/tool-loop-detection.test.ts src/agents/embedded-agent-subscribe.handlers.tools.test.ts src/agents/embedded-agent-runner/run/payloads.errors.test.ts git diff --check node scripts/run-tsgo.mjs -p tsconfig.core.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/core.tsbuildinfo node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.core.test.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/core-test.tsbuildinfo node scripts/run-oxlint-shards.mjs --only=core --split-core .agents/skills/autoreview/scripts/autoreview --mode local --base origin/mainEvidence after fix:
git diff --check: passed.tsgocore production: passed.tsgocore tests: passed.Observed result after fix:
writereturnsdetails: { ok: false, status: "blocked", reason: "no-op-write" }, setsterminate: true, and does not call the write operation.editreturnsdetails: { ok: false, status: "blocked", reason: "no-op-edit" }, setsterminate: true, and does not call the write operation.apply_patchreturnsdetails: { ok: false, status: "blocked", reason: "no-op-patch" }, setsterminate: true, and leaves file content unchanged.What was not tested:
node scripts/check-changed.mjscould not start remote Testbox proof because the localcrabboxbinary failed its sanity check before provisioning. The touched core surface was covered with focused Vitest, coretsgo, core oxlint,git diff --check, and autoreview instead.AI-assisted: This PR was authored with AI assistance. The behavior and verification above were checked in the local repository.