diff --git a/crates/arc-attractor/src/engine.rs b/crates/arc-attractor/src/engine.rs index f49c9e8ea..ed6933467 100644 --- a/crates/arc-attractor/src/engine.rs +++ b/crates/arc-attractor/src/engine.rs @@ -1211,6 +1211,21 @@ impl PipelineEngine { final_git_commit_sha: last_git_sha.clone(), }); + // Write final.patch: comprehensive diff from base_sha to HEAD + if let (Some(ref mode), Some(ref base)) = (&config.git_checkpoint, &config.base_sha) { + let patch = match mode { + GitCheckpointMode::Host(work_dir) => { + git_diff_host(work_dir.clone(), base.clone()).await + } + GitCheckpointMode::Remote => { + git_diff_remote(&*self.services.execution_env, base).await + } + }; + if let Some(patch) = patch { + let _ = std::fs::write(config.logs_root.join("final.patch"), patch); + } + } + // Return last outcome, or success if no outcomes recorded let last_outcome = node_outcomes .get(completed_nodes.last().unwrap_or(&String::new())) diff --git a/crates/arc-attractor/tests/daytona_integration.rs b/crates/arc-attractor/tests/daytona_integration.rs index 8f8527716..985148d4d 100644 --- a/crates/arc-attractor/tests/daytona_integration.rs +++ b/crates/arc-attractor/tests/daytona_integration.rs @@ -474,6 +474,12 @@ async fn daytona_git_checkpoint_remote_emits_events() { "checkpoint should have git_commit_sha" ); + // Assert final.patch exists and contains changes from the run + let final_patch = dir.path().join("final.patch"); + assert!(final_patch.exists(), "final.patch should exist in logs_root"); + let patch_content = std::fs::read_to_string(&final_patch).unwrap(); + assert!(!patch_content.is_empty(), "final.patch should not be empty"); + env.cleanup().await.unwrap(); } diff --git a/crates/arc-attractor/tests/integration.rs b/crates/arc-attractor/tests/integration.rs index 3058e4b61..cc59fbf91 100644 --- a/crates/arc-attractor/tests/integration.rs +++ b/crates/arc-attractor/tests/integration.rs @@ -8395,6 +8395,12 @@ async fn git_checkpoint_host_emits_events_and_diff_patch() { "checkpoint should have git_commit_sha" ); + // 9. Assert final.patch exists and contains the changes + let final_patch = logs_dir.path().join("final.patch"); + assert!(final_patch.exists(), "final.patch should exist in logs_root"); + let patch_content = std::fs::read_to_string(&final_patch).unwrap(); + assert!(patch_content.contains("hello.txt"), "final.patch should contain hello.txt changes"); + // Cleanup worktree let _ = std::process::Command::new("git") .args(["worktree", "remove", "--force"])