Repository navigation
feat(bundle): pull the shipped agent's published image instead of building it - #293
Merged
Merged
Conversation
…lding it
An agent image a bundle builds from a shipped agent's sources (the Claude Code agent's folder, unchanged) is now
registered as the image agent-env publishes for those sources, by its digest, with the build context beside it.
Each deploy pulls it by the digest and builds it from the context only when it can't, so a run no longer builds the
default agent's image on every VM, and a provider that runs images by name can run it.
- The planner looks the publication up once (Plan.published), from the record the wheel carries or the registry, so
preflight, the ledger and the writer all see the same answer.
- The ledger hashes the publication, so a version written before the image was published is written again as the
published one ("published as <ref>"), and reused after that.
- Preflight checks a published image as a registry image: it isn't built on this machine first, and a provider that
runs images by name takes it.
- The local provider's prepare_image pulls an image with a digest by it, onto this machine's Docker, building it from
its context when the pull fails.
- Bootstrap names each infra Dockerfile by its whole path: a bare name was looked up from the current folder first,
so a run started in a folder holding a Dockerfile registered or built the wrong one.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
earakely-scale
marked this pull request as ready for review
October 21, 2026 22:03
…anged since planning - The local provider pulled a published image by its digest in prepare_image, then the container's start pulled its tag again, which fails with the registry unreachable though Docker holds the image. A local sandbox now skips pulling an image Docker holds under a name that never moves: one pinned to a digest, or a source tag agent-env publishes (published_images.is_source_tag). - The plan keeps the source digest it found the publication for (Publication.sources), and the write checks the staged context against it, so an agent folder edited after planning isn't registered as the published image with a build context of other sources; the run asks to be run again instead. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
Acceptance on admin VMs, at 59be254.
Notes
|
…d by its digest The skip covered only agent-env's published source tags; an image registered with a digest under another tag was still pulled again by name when its container started. host_images.pull now records each image it had Docker hold, and a local sandbox skips any of those it still holds, so the provider no longer needs to know agent-env's registry. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ication, and digest-checked host pulls - host_images.pull takes an image Docker holds under its name only when it holds it as the image the digest names (pulled by that digest, or built here from its sources), and otherwise pulls the digest and moves the tag: a tag two versions share can name another image. holds() is never true for an image with no sources. - The images prepare_image pulled are the local provider's own record, which it hands each sandbox it creates (LocalSandbox.prepared), in place of a module-level set that tied the two through call order. - StagedContext carries the staged copy's path, the Dockerfile's whole path and its identity (put_context's, built for each sandbox's platform), so the planner and both writers stage and hash a build context the same way. - Publication.unwritten is the image as the run will store it, which preflight checks providers against, beside the fields the writer stores. 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.
Why. #288 publishes the Claude Code agent's image. This PR makes a run use it: a deploy of the default agent pulls the published image by its digest instead of building it, on every VM, from its folder. A remote agent deploy spent 51–58 s of its 56–63 s on that build. It also lets a provider that runs images by name run the agent, which it refused before because the image was only a build context.
Change
published_images.published_agent_image(sources)finds the image published for a shipped agent whose build context has those sources. The digest comes from the record the wheel carries, or from the registry when there is no record.Plan.publishedrecords the lookup once, for each agent image a bundle builds. Preflight, the ledger and the writer all read that one answer.put_ref: the published tag, itsimage_digest, and the staged build context beside it as the fallback. Nothing is built on this machine.published as <ref>), and its agent is rewritten with it; the next run reuses both. Unpublished images hash exactly as before, so no scheme bump.prepare_imagenow pulls an image that has animage_digest, by that digest, onto this machine's Docker (host_images.pull). It builds the image from its context when the pull fails. It skips the pull only when Docker already holds the image as the one its digest names: pulled by that digest, or built here from its sources. A tag that two versions share can name another image. When the container then starts, the local sandbox doesn't pull the tag again over an image its provider prepared, or one held under a digest-pinned name. The VM providers and Modal already handle such images (feat(env): set up the infra envs from their published images, building only what isn't published #277).<ref>, published for its sources, which each deploy pulls by its digest, or builds from its build context when it can't.prepare_imagerow.A bug fix in #277's bootstrap.
_resolve_dockerfilelooks a relative Dockerfile name up from the current folder first. Bootstrap passed bare names (Dockerfile), so a run started in a folder holding aDockerfileof its own picked up the wrong file:… is outside the build context;Bootstrap now passes each Dockerfile's whole path. That records the same
dockerfile_pathand the samesource_digest.Verified
prepare_image;localandmodal, the Claude Code image is registered fromghcr.io/scaleapi/agentenv-claude-code:src-…, and nothing is built here.🤖 Generated with Claude Code


Confidence Score: 5/5
The PR appears safe to merge; no new blocking issues remain.Summary
Bundles now register shipped agents’ published images by digest, with their build contexts kept for fallback builds.
Diagram
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Stage agent sources] --> B{Published image found?} B -->|Yes| C[Record publication in plan] C --> D[Check staged sources before write] D --> E[Store digest and fallback context] E --> F{Docker holds requested image?} F -->|Yes| I[Start prepared image] F -->|No| G[Pull by digest] G -->|Success| I G -->|Failure| H[Build fallback context] H --> I B -->|No| J[Store build context]Reviews (4) · Last reviewed commit: "refactor(bundle): one staged context, th..." · Reviewed by Greptile