From 2ca86bb5d808be122bcef129fcb69e2cc6d1da1a Mon Sep 17 00:00:00 2001 From: Josh Knapp Date: Sun, 23 Aug 2026 08:37:56 -0700 Subject: [PATCH] Scrub the writable layer on the migration path too, and de-duplicate CLAUDE_JSON Two integration fixes after merging the three feature branches. `scrub_writable_layer` is a `docker exec`, so it only works while the container runs. `migrate_project_to_base` stops its container one line before the pre-swap commit, which meant the single largest snapshot Triple-C ever takes was the one path that committed unscrubbed. Call the scrub explicitly before the stop instead of relying on the call inside `commit_container_snapshot`. Also drop a duplicate `CLAUDE_JSON=` assignment in entrypoint.sh. The Shift+Enter block re-declared it defensively to avoid a merge conflict with the awsAuthRefresh block; the conflict did not materialise. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01GBq2rGum6GX7xXgsas1fDc --- .gitignore | 7 +++++++ app/src-tauri/src/commands/migration_commands.rs | 7 +++++++ app/src-tauri/src/docker/container.rs | 7 ++++--- container/entrypoint.sh | 3 ++- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 3043098..470d651 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,10 @@ app/dist/ app/src-tauri/target/ Screenshot*.png code-review.md + +# Windows NTFS alternate-data-stream artifacts, created when files arrive +# through the WSL/host bind mount. +*:Zone.Identifier + +# Local bug-report screenshots, same spirit as Screenshot*.png above. +screenshot_for_fix/ diff --git a/app/src-tauri/src/commands/migration_commands.rs b/app/src-tauri/src/commands/migration_commands.rs index 907b0ed..1327f15 100644 --- a/app/src-tauri/src/commands/migration_commands.rs +++ b/app/src-tauri/src/commands/migration_commands.rs @@ -460,6 +460,13 @@ async fn fresh_migration( } } + // Scrub *before* the stop, not inside the commit below. `scrub_writable_layer` + // is a `docker exec`, which only works on a running container — and the + // pre-swap commit is the single largest snapshot Triple-C ever takes, so + // letting this one path commit unscrubbed is what the scrub exists to + // prevent. Failure is swallowed inside; it must never block a migration. + docker::scrub_writable_layer(&container_id).await; + emit_progress(&app_handle, &project_id, "Stopping the container..."); let _ = state .projects_store diff --git a/app/src-tauri/src/docker/container.rs b/app/src-tauri/src/docker/container.rs index 0799b44..9bdc011 100644 --- a/app/src-tauri/src/docker/container.rs +++ b/app/src-tauri/src/docker/container.rs @@ -2047,9 +2047,10 @@ fn parse_scrub_total(output: &str) -> Option { /// **Never fails the caller, by design.** A scrub is an optimisation; a commit /// is the only copy of the user's system layer. Losing some disk is a strictly /// better outcome than refusing to snapshot, so every failure here is a log -/// line and nothing more. Note that one caller (the pre-swap commit in -/// `migrate_project_to_base`) has already *stopped* the container, so `docker -/// exec` legitimately fails there — that path simply commits unscrubbed. +/// line and nothing more. Note that this is a `docker exec` and therefore only +/// works while the container runs: `migrate_project_to_base` stops its +/// container before the pre-swap commit, so it calls this itself beforehand +/// rather than relying on the call inside [`commit_container_snapshot`]. pub async fn scrub_writable_layer(container_id: &str) -> u64 { let script = snapshot_scrub_script(); let cmd = vec!["/bin/sh".to_string(), "-c".to_string(), script]; diff --git a/container/entrypoint.sh b/container/entrypoint.sh index 8c71fc9..6dd22c7 100644 --- a/container/entrypoint.sh +++ b/container/entrypoint.sh @@ -480,7 +480,8 @@ fi # already in place, so it keeps printing its "run /terminal-setup" tip. This # flag is what that tip is gated on: purely cosmetic, and it changes nothing # about how the sequence is decoded. -CLAUDE_JSON="/home/claude/.claude.json" +# +# $CLAUDE_JSON is already set by the awsAuthRefresh block above. if [ -f "$CLAUDE_JSON" ]; then # Only rewrite when the value isn't already true, to avoid a needless jq # reformat of ~/.claude.json on every single start.