Fix/no op file mutations#97008
Conversation
|
Codex review: needs changes before merge. Reviewed June 26, 2026, 6:09 AM ET / 10:09 UTC. Summary PR surface: Source +27, Tests +10. Total +37 across 3 files. Reproducibility: yes. at source level: current main can detect same-content writes but still writes and returns success, and edit no-change currently throws before any structured terminal no-op result is produced. I did not run a live agent-loop replay in this read-only review. Review metrics: 1 noteworthy metric.
Stored data model Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Copy recommended automerge instructionNext step before merge
Security Review findings
Review detailsBest possible solution: Land a narrow file-tool fix that preserves valid multi-edit siblings, classifies helper-thrown no-change edits through the same terminal contract, and either covers Do we have a high-confidence way to reproduce the issue? Yes, at source level: current main can detect same-content writes but still writes and returns success, and edit no-change currently throws before any structured terminal no-op result is produced. I did not run a live agent-loop replay in this read-only review. Is this the best way to solve the issue? No. The PR is in the right built-in file-tool boundary, but the edit no-op check is too broad for multi-edit calls and the post-apply no-diff check misses the helper-thrown no-change path. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against ec737ee74d9b. Label changesLabel justifications:
Evidence reviewedPR surface: Source +27, Tests +10. Total +37 across 3 files. View PR surface stats
Acceptance criteria:
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
6a68a63 to
c4c7b21
Compare
What Problem This Solves
Fixes an issue where agents can repeatedly call
writewith identical content oreditwith identical old/new text, and the tool reports success each time. The agent interprets this as progress and keeps retrying the same no-op mutation indefinitely, wasting tokens and inflating the tool loop without making any actual change.Why This Change Was Made
The
writetool already had a content comparison precheck (readOriginalWriteState) that could detect identical content, but it was not used to short-circuit the write. Even whenprecheck.state === "same", the tool would write the file again and return "Successfully wrote N bytes".Similarly, the
edittool had no check for edits whereoldText === newText, and no detection for edits that produce no diff after application.The fix returns a terminal result with
terminate: true. This ensures:terminatefield is already used for this purpose atagent-tool-definition-adapter.ts:560AgentToolResult.terminatesignal is usedThree no-op paths are covered:
writereadOriginalWriteStatereturns"same"editoldText === newTextin any edit entryeditbaseContent === newContentafter applyNon-goals:
apply_patchno-change detection is left as follow-up work.User Impact
Agents will no longer waste tokens re-writing identical file content or re-editing with identical old/new text. When a write or edit would produce no change, the tool returns a structured terminal
{ status: "blocked", reason: "no-op-write" }result, allowing the runtime to classify it properly and the agent to move on.Evidence
Structured result shape
No-op results now include:
This uses the

textResulthelper fromsrc/agents/tools/common.tsand addsterminate: true(already used by the tool adapter for client-side tools atagent-tool-definition-adapter.ts:560).Real behavior proof
Live tool invocation output (Node.js, Linux):
writewith identical content:{ "content": [{ "type": "text", "text": "No changes: content is identical to existing /tmp/openclaw-proof-xxx/demo.txt" }], "details": undefined, "terminate": true }File mtime confirmed unchanged after the call — the file was not rewritten.
editwith identical oldText/newText:{ "content": [{ "type": "text", "text": "No changes: one or more edits have identical old and new text in /tmp/openclaw-proof-xxx/demo.ts." }], "details": undefined, "terminate": true }Regression —
writewith different content still writes:Regression —
writeto new file still creates:Regression —
editwith non-matching oldText still errors (not no-opped):Unit test coverage
Regression coverage preserved
writewith different content still writes successfullywriteto a new file still creates itwritepost-write timeout recovery (recoverSuccessfulWrite) is unchangededitwith real changes still applies and returns diffChanges
src/agents/sessions/tools/write.ts{ terminate: true }on identical content (usestextResult+terminate)src/agents/sessions/tools/edit.tsoldText === newTextpre-apply ANDbaseContent === newContentpost-apply; returnterminate: truesrc/agents/sessions/tools/write.test.tsdetailsandterminatefields on no-op resultCloses #96983