fix: [altimate-code] core_failure::edit — file modified since last read (100/2h) (autofix #486)#489
fix: [altimate-code] core_failure::edit — file modified since last read (100/2h) (autofix #486)#489
Conversation
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.
Tip: disable this comment in your organization's Code Review settings.
📝 WalkthroughWalkthroughThe changes implement file freshness validation by capturing both wall-clock read time and filesystem modification time as a snapshot when files are read, then comparing against the captured baseline during edit operations to prevent race conditions where externally modified files could be overwritten. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/opencode/src/file/time.ts (1)
80-91: Minor: Error message referencestime.atbut comparison usesbaseline.The error message reports
Last read: ${time.at.toISOString()}but the actual comparison baseline istime.mtime ?? time.at. Iftime.mtimediffers fromtime.at, this could confuse users debugging the error since the reported "last read" time isn't the value being compared against.Consider clarifying the error message:
💡 Suggested improvement for clarity
if (mtime && mtime.getTime() > baseline.getTime() + 50) { throw new Error( - `File ${filepath} has been modified since it was last read.\nLast modification: ${mtime.toISOString()}\nLast read: ${time.at.toISOString()}\n\nPlease read the file again before modifying it.`, + `File ${filepath} has been modified since it was last read.\nCurrent mtime: ${mtime.toISOString()}\nBaseline mtime: ${baseline.toISOString()}\nRead at: ${time.at.toISOString()}\n\nPlease read the file again before modifying it.`, ) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/opencode/src/file/time.ts` around lines 80 - 91, The error message in the overwrite-prevention block uses time.at in the "Last read" line but the actual comparison uses baseline = time.mtime ?? time.at, which can mislead users; update the thrown Error in the block around state().read[sessionID]?.[filepath] and Filesystem.stat(filepath) to report the exact baseline value used in the comparison (i.e., use baseline.toISOString() or include both time.mtime and time.at for clarity) so the reported "Last read" matches the value compared against mtime.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/opencode/src/tool/edit.ts`:
- Around line 111-113: Add the same freshness assertion used in edit.ts to the
write and apply_patch flows: after the user approval prompt (ctx.ask) and before
performing any filesystem mutation, call FileTime.assert(ctx.sessionID,
filePath) to verify the file wasn't changed during the approval delay; in
write.ts also move/replace the current FileTime.read() usage so it does not run
only after write but instead the assert runs before writeFile, and in
apply_patch.ts add the FileTime.assert call before running the patch application
logic (applyPatch / patch write function) to prevent overwriting external
modifications.
---
Nitpick comments:
In `@packages/opencode/src/file/time.ts`:
- Around line 80-91: The error message in the overwrite-prevention block uses
time.at in the "Last read" line but the actual comparison uses baseline =
time.mtime ?? time.at, which can mislead users; update the thrown Error in the
block around state().read[sessionID]?.[filepath] and Filesystem.stat(filepath)
to report the exact baseline value used in the comparison (i.e., use
baseline.toISOString() or include both time.mtime and time.at for clarity) so
the reported "Last read" matches the value compared against mtime.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 8615e09b-e885-479f-ab55-d4c67d4997cb
📒 Files selected for processing (4)
packages/opencode/src/file/time.tspackages/opencode/src/tool/edit.tspackages/opencode/test/file/time.test.tspackages/opencode/test/tool/edit.test.ts
| // altimate_change start — re-check file freshness after the permission prompt so an external save during approval cannot be overwritten with a stale edit | ||
| await FileTime.assert(ctx.sessionID, filePath) | ||
| // altimate_change end |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check if write.ts and apply_patch.ts have the same FileTime.assert pattern after ctx.ask
echo "=== write.ts pattern (ctx.ask followed by write) ==="
rg -n -A15 'ctx\.ask' packages/opencode/src/tool/write.ts | head -30
echo ""
echo "=== apply_patch.ts pattern (ctx.ask followed by write) ==="
rg -n -A30 'ctx\.ask' packages/opencode/src/tool/apply_patch.ts | head -50Repository: AltimateAI/altimate-code
Length of output: 1813
🏁 Script executed:
# Check edit.ts context around lines 111-113 to see FileTime.withLock() usage
echo "=== edit.ts context around the FileTime.assert() (lines 100-120) ==="
rg -n -B10 -A5 'FileTime.assert' packages/opencode/src/tool/edit.ts
echo ""
echo "=== Check if write.ts and apply_patch.ts use FileTime.withLock() ==="
rg -n 'FileTime.withLock\|FileTime.assert' packages/opencode/src/tool/write.ts
rg -n 'FileTime.withLock\|FileTime.assert' packages/opencode/src/tool/apply_patch.tsRepository: AltimateAI/altimate-code
Length of output: 1585
🏁 Script executed:
# Check the function signatures to see if write.ts and apply_patch.ts use FileTime.withLock
echo "=== write.ts export/function definition ==="
head -60 packages/opencode/src/tool/write.ts | tail -20
echo ""
echo "=== apply_patch.ts export/function definition ==="
head -60 packages/opencode/src/tool/apply_patch.ts | tail -20Repository: AltimateAI/altimate-code
Length of output: 1285
🏁 Script executed:
# Search for the actual function implementation in write.ts that contains ctx.ask
echo "=== write.ts full function containing ctx.ask ==="
rg -n -B20 -A20 'ctx\.ask' packages/opencode/src/tool/write.ts | head -60Repository: AltimateAI/altimate-code
Length of output: 1667
Address race condition inconsistency across edit, write, and apply_patch tools.
The fix correctly prevents file overwrites during the approval window in edit.ts by re-checking freshness after ctx.ask(). However, write.ts (line 45) and apply_patch.ts (lines 198, 203) perform the same sequence—approval prompt followed by file write—but lack an equivalent FileTime.assert() check.
In write.ts, FileTime.read() is called after the write, not before. This records the current file time rather than validating that the file hasn't been externally modified during the approval delay. Similarly, apply_patch.ts contains no freshness checks at all.
Both tools should receive the same protection that edit.ts now has: a FileTime.assert() call immediately after ctx.ask() and before writing, to prevent stale edits from overwriting external modifications made during user approval.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/opencode/src/tool/edit.ts` around lines 111 - 113, Add the same
freshness assertion used in edit.ts to the write and apply_patch flows: after
the user approval prompt (ctx.ask) and before performing any filesystem
mutation, call FileTime.assert(ctx.sessionID, filePath) to verify the file
wasn't changed during the approval delay; in write.ts also move/replace the
current FileTime.read() usage so it does not run only after write but instead
the assert runs before writeFile, and in apply_patch.ts add the FileTime.assert
call before running the patch application logic (applyPatch / patch write
function) to prevent overwriting external modifications.
|
Closing — test PR from autofix system, not intended for merge. |
Autofix PR
Closes #486
This PR was automatically generated by the autofix system using Codex CLI.
Verification
Please review carefully before merging.
Summary by CodeRabbit