Repository navigation
feat(bundle): keep what an interrupted run wrote, and rebuild an image when a file's permissions change - #71
Merged
Conversation
…e when an executable bit changes Three changes to the bundle ledger, which envs written from env.toml will build on. A version an interrupted run wrote is kept. The ledger now stamps a write's version on its pending row as soon as the write returns, before it re-checks what the write held, which can take seconds. The next run of the bundle keeps that version, rather than writing it again, when it's still the store's latest and its inputs haven't changed. The run and the dry run report it as "unchanged (written by an interrupted run that didn't record it)". A version someone else wrote since is written over, as before. A built image's executable bits are inputs. A build copies each file's mode into the image, so a chmod alone now rebuilds it. A file with no executable bit is hashed as before, so the images a bundle built without one are still reused. One helper, unpinned_store_refs, names the store refs a write gives no version. Its writer pins them and the ledger hashes them from the same mapping, for every kind whose writer pins, not only envs and agents. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… file is executable A build copies each file's permission bits into the image, so 0755 to 0700 changes what runs in it, but both were hashed as executable. A file's mode now joins its digest whenever it isn't the usual 0644, so images built from such files are reused as before. Co-Authored-By: Claude Opus 5.5 (1M context) <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.
Three changes to the bundle ledger. Envs written from
env.tomlwill build on them, and agents and file artifacts use them today.A version an interrupted run wrote is kept
A pending row marked each write until it was recorded, but nothing read it back. If a run was interrupted after its write had landed, while the ledger was re-checking what the write held, the next run found a version it hadn't recorded and wrote it again:
the store's latest, v1, wasn't recorded by this bundle. For a built image, that's a rebuild. The re-check hashes the write's files again, so for a large context it takes seconds.record()stamps the version on the pending row as soon as the write returns, before the re-check.check()keeps the stamped version when it's still the store's latest and the inputs hash the same.Ledger.adopt()then records it, with no write.v1, unchanged (written by an interrupted run that didn't record it).A built image's permission bits are inputs
A build copies each file's permission bits into the image, but the ledger hashed contents only, so a
chmodalone reused the old image. A file's digest now carries its mode (+755,+700, …) whenever the mode isn't the usual 0644, for built images only.One helper for the store refs a write gives no version
plan.unpinned_store_refs(plan, write)returns the version the plan read for each store entity a write names without one.materialize._pinnedpins from it, andLedger.digesthashes it. The ledger now hashes these for every kind whose writer pins, so artifacts as well as envs and agents. No artifact type has store refs today, so no existing digest changes.Tests
End to end
Real runs on macOS arm64 with Docker, from fresh state roots, on the local stores.
run.shfiles changed: run.sh); both deploy and replychmod700, then 644, then 755statin the image follows: 700, 644, 755Aborted!; the pending row holds v1; the next dry run and run keep v1, and the run takes 3.5 s; one version in the store🤖 Generated with Claude Code
The PR appears safe to merge; no new actionable issue or outstanding previous finding remains.
What we checked:
build_context_files, and the build writer copies those same files withshutil.copy2.Summary
Bundle runs can keep a version written just before an interruption, and built images now rebuild when their files’ permission modes change. Planned store versions are also used consistently when writes pin and hash unversioned references.
Diagram
%%{init: {'theme': 'neutral'}}%% flowchart LR A[Check bundle inputs] --> B{Stamped pending version<br/>is latest and inputs match?} B -- Yes --> C[Reuse version] C --> D[Record it on a real run] B -- No --> E[Write a new version] E --> F[Stamp pending row] F --> G[Check inputs again] G --> H[Record completed write]Reviews (2) · Last reviewed commit: "fix(bundle): hash a built image's permis..."