Make turn diff tracking operation backed (#21180)

## Summary
- replace filesystem-based turn diff tracking with an operation-backed
accumulator
- preserve enough verified apply_patch state to render move-overwrite
cases correctly
- keep the turn/diff/updated contract intact while removing remote-only
turn-diff test skips

This takes the assumption that no 3P services rely on the output format
of `apply_patch`

## Why
For the CCA file system isolation push

---------

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
jif-oai
2026-05-07 11:33:47 +02:00
committed by GitHub
Unverified
parent b2268999fe
commit f7e8ff8e50
15 changed files with 1050 additions and 898 deletions
+61 -1
View File
@@ -193,6 +193,7 @@ pub async fn maybe_parse_apply_patch_verified(
let ApplyPatchFileUpdate {
unified_diff,
content: contents,
..
} = match unified_diff_from_chunks(&path, &chunks, fs, sandbox).await {
Ok(diff) => diff,
Err(e) => {
@@ -707,6 +708,7 @@ PATCH"#,
"#;
let expected = ApplyPatchFileUpdate {
unified_diff: expected_diff.to_string(),
original_content: "foo\nbar\nbaz\n".to_string(),
content: "foo\nbar\nBAZ\n".to_string(),
};
assert_eq!(expected, diff);
@@ -745,6 +747,7 @@ PATCH"#,
"#;
let expected = ApplyPatchFileUpdate {
unified_diff: expected_diff.to_string(),
original_content: "foo\nbar\nbaz\n".to_string(),
content: "foo\nbar\nbaz\nquux\n".to_string(),
};
assert_eq!(expected, diff);
@@ -839,9 +842,10 @@ PATCH"#,
assert_eq!(action.cwd.as_path(), worktree_dir.as_path());
let source_path = worktree_dir.join(source_name);
let change = action
.changes()
.get(&worktree_dir.join(source_name))
.get(source_path.as_path())
.expect("source file change present");
match change {
@@ -854,4 +858,60 @@ PATCH"#,
other => panic!("expected update change, got {other:?}"),
}
}
#[tokio::test]
async fn test_unreadable_destinations_still_verify() {
let session_dir = tempdir().unwrap();
fs::write(session_dir.path().join("binary.dat"), [0xff, 0xfe, 0xfd]).unwrap();
let cwd = AbsolutePathBuf::from_absolute_path(session_dir.path()).unwrap();
let add_argv = vec![
"apply_patch".to_string(),
"*** Begin Patch\n*** Add File: binary.dat\n+text\n*** End Patch".to_string(),
];
fs::write(session_dir.path().join("source.txt"), "before\n").unwrap();
let move_argv = vec![
"apply_patch".to_string(),
"*** Begin Patch\n*** Update File: source.txt\n*** Move to: binary.dat\n@@\n-before\n+after\n*** End Patch".to_string(),
];
for argv in [add_argv, move_argv] {
let result = maybe_parse_apply_patch_verified(
&argv,
&cwd,
LOCAL_FS.as_ref(),
/*sandbox*/ None,
)
.await;
assert!(matches!(result, MaybeApplyPatchVerified::Body(_)));
}
}
#[cfg(unix)]
#[tokio::test]
async fn test_delete_symlink_still_verifies() {
use std::os::unix::fs::symlink;
let session_dir = tempdir().unwrap();
fs::write(session_dir.path().join("target.txt"), "target\n").unwrap();
symlink(
session_dir.path().join("target.txt"),
session_dir.path().join("link.txt"),
)
.unwrap();
let argv = vec![
"apply_patch".to_string(),
"*** Begin Patch\n*** Delete File: link.txt\n*** End Patch".to_string(),
];
let result = maybe_parse_apply_patch_verified(
&argv,
&AbsolutePathBuf::from_absolute_path(session_dir.path()).unwrap(),
LOCAL_FS.as_ref(),
/*sandbox*/ None,
)
.await;
assert!(matches!(result, MaybeApplyPatchVerified::Body(_)));
}
}