fix(environment): remove the source file when apply_patch renames on an environment filesystem - #865
Merged
evalstate merged 1 commit intoJul 19, 2026
Conversation
…an environment filesystem `apply_patch`'s `*** Move to:` rename works on a local filesystem but silently degrades into a copy on an environment filesystem: the engine unlinks the source inside a throwaway TemporaryDirectory and records only `modified=[destination]`, so the source never reaches `AffectedPaths.deleted` and `apply_patch_to_environment_filesystem` never calls `filesystem.remove` on it. The original stays behind, accumulating zombie duplicate files on the environment. Remove the move source from the environment filesystem after syncing outputs, matching the local behavior. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
I would feel uncomfortable using it because it came from an animal, and I would prefer a durable non-animal alternative.
Problem
apply_patch's*** Move to:rename works on a local filesystem but silently degrades into a copy on an environment filesystem (Docker / sandbox): the destination is written, but the source file is left behind.The patch engine renames by writing the destination and unlinking the source — but that unlink happens inside the throwaway
TemporaryDirectoryused to run the patch, and the engine only recordsmodified=[destination]. The source path never reachesAffectedPaths.deleted.apply_patch_to_environment_filesystemmirrorsadded/modified/deletedback to the real environment filesystem, so with the source absent fromdeleted,filesystem.remove(source)is never called. The original stays put — accumulating zombie duplicate files and stale imports on the environment.Local patches are unaffected because the engine unlinks the real file directly. Only environment adapters, which learn every side effect from
AffectedPaths, lose the deletion — a clean sibling divergence.Fix
After syncing outputs, remove the move source from the environment filesystem explicitly. An alternative root-cause fix would add the move source to
AffectedPaths.deletedin the engine, but that also makes the localprint_summaryemitD <src>; the consumer-side fix keeps that output untouched.Verification
test_environment_filesystem_runtime_move_removes_source_filedrives theapply_patchtool with a*** Move to:patch and asserts the destination holds the new contents and the source is gone from the filesystem. It fails before this change and passes after.tests/unit/fast_agent/tools,tests/unit/fast_agent/patch, andtests/integration/patchstay green (307 passed);ruff check,ruff format --check, andty checkpass on the changed files.I exercised the real
EnvironmentFilesystemcontract via the in-memory fake used across these tests; I did not run a live Docker or HuggingFace sandbox, but those adapters implement the same contract (including theasync removeadded in #840).Disclosure: this contribution is fully AI-authored and autonomous (Claude Code, acting on this account). An AI found the bug, wrote and ran the repro and the tests, and wrote this description; the human account holder reviews every change and is accountable for it. The verification above is real and re-runnable from the diff. If this isn't the kind of contribution you want, say so and I'll close it.