Repository navigation
feat(bundle)!: bundle init writes every env, agent and data artifact a task uses into the folder - #259
Merged
earakely-scale merged 14 commits intoOct 20, 2026
Conversation
…its type's to_toml `bundle init` now writes every env, agent and artifact a task references into a folder of its own, so the folder runs with no store. Each type writes itself out through a `to_toml` hook, the inverse of the `from_toml` a bundle reads it with, given an `ExportContext` (agent_env.entity.export) that names what it references, its images and its files. - mcp_server, website and multi envs, a2a_agent, and the file, file_artifact_universe, environment, environment_universe, cli and skill artifacts define it. - An image is written from its stored build context, pruned to what its Dockerfile reads (write_pruned); contexts stored as one object (a new ObjectMetadata.etag, from S3's ETag and GCS's MD5) are downloaded once. - A reference to something written into the folder names it there, unpinned. - What a folder can't hold fails the import, unless --include-scope user leaves it in the store, pinned. - provenance.json records each written entity's store id and version; the README says how to run and share the folder. - The environment-universe, file-artifact-universe, cli and skill `get --output-dir` commands write through the same to_toml, so they refuse a path that leaves the folder and read a pinned version's own files. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… held it, and what a folder can't hold - A round trip per core type: a task using it is imported, the folder materialized into a namespace of its own, and the entity read back compared with the stored one on every field its toml carries, and every byte of its files. - What a folder can't hold (an image stored only as a tarball, a context holding a link a bundle can't follow, a type with no to_toml, a path leaving the folder, files differing only in case, a skill's taken name) fails the import, and stays in the store, pinned, with --include-scope user. - Contexts stored as one object are downloaded once; an image that records no Dockerfile builds the one its context holds; provenance names ids and versions and no store location. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ersions its stored image holds A constraints.txt beside the Dockerfile, read by each stage running pip as PIP_CONSTRAINT and UV_CONSTRAINT build args, and exact versions in npm -g installs. A project lock file, a version the build asks for that the image ended past, a path or URL install, and a version pip can't read are left unpinned and reported. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…l endpoint before a run starts A gateway stack on this machine needs docker compose, an image built here needs docker buildx, and an agent or agent judge whose step gives it no endpoint needs the configured one; each refusal says how to install or set it. VM and local builds extract a stored context at extraction times, so BuildKit can't reuse an older context's file of the same path, size and time. The integration tests follow bundle init writing what a task uses; small sizes show in B or KB. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…a field naming what it can't take A hard link in a stored context is written as a file of its own holding what it links to, as tar extracts it, so pinning a Dockerfile never changes a twin. A step field naming an entity of another type than it takes, which the store holds but the step can't read, is left out and said so. The run preflight takes the model variables an agent declares for itself, and the README names the registries pulled images need. Integration tests for pinned rebuilds and for two stored contexts built on one host as a VM builds them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…d context can't be written A context the folder can't hold, such as one naming no Dockerfile and holding several, falls back to the image's registry ref rather than failing the import, and the report says why it's pulled. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…pins elsewhere A folder holds one version of each name, and a step that names an env with no version means the one an earlier step deployed, as build_mcp_cli does; it took the latest instead, written as a second folder the run never deployed. The report says which version it took. The broken-agent integration test declares the model variables its siblings do. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…onfigured image store agent-env checks a pulled image's ref with its image store's credentials only, so a docker login doesn't reach it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…act, and says so A file universe holding __pycache__ or .DS_Store failed the import, though a bundle leaves those out of every folder it reads and an image's pruned context drops them already. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ask pins elsewhere A step naming an env with no version means the one an earlier step deployed. An artifact or agent named with no version keeps the latest, written as a folder of its own, as the store run reads it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… and the tests share their helpers The import's results and their text live in bundle/_import_report.py, so importer.py is the walk and the writes. A written image holds its Pruned result rather than copies of its fields, and provenance is told from the finished import. The bundle tests share one context_image and one docker save builder. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…with a context the store no longer holds - Pinning rewrites a Dockerfile, which another image built from the same context into the same folder may hold too, so a website's frontend was refused for differing from its pinned backend. Pinning now waits until every image is placed, and a Dockerfile two images share is pinned once. - A project lock disabled pinning wherever it sat in the folder. Constraints only bind pip and uv pip, so their stages are pinned regardless, and a lock is reported only where a command the Dockerfile runs installs from it. - npm -g packages written in quotes are pinned in their quotes, and one written in a form that isn't rewritten is reported rather than left silently. - The constraints file takes a name nothing in the folder has, and starts with a line marking it as agent-env's, so pinning a second image there doesn't read the first's pins as asked for. - A stored context the store no longer holds becomes that image's problem: its registry ref, a user-scope pin, or an import refused naming it. Neither it nor an unreadable saved image puts a store location in the report. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…images hold the same versions The second image took the first one's pins, though its own saved image could hold other versions. Each saved image is read first; one Dockerfile is pinned for all only when they agree, else left as the build resolves it and each image says why. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…nce and pinning rules are named - The README's Docker, model endpoint and constraints lines are built from what the preflight and pinning use: a DockerPlugin table in utils/docker/build.py, the model variables made public in config/model.py, and the constraints' name in the image from utils/docker/lock.py, so the two can't drift. - _Importer._ref takes its task's pins as an argument, and its env version rule, its wrong-type rule and the clearing of a pin are _version_for, _not_taken and _unpin. - lock() is its npm half and its Python half, each returning what it pinned and what it left. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
earakely-scale
deleted the
edgararakelyan/fd-3994-pr-2-agent-env-bundle-init-write-every-env-agent-and-data
branch
October 20, 2026 04:30
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.
agent-env bundle initnow writes every env, agent and data artifact a task uses into the folder, so the folder runs with no store. Before, it wrote the task and pinned each reference to the store.What the folder holds
envs/<name>/: each env'senv.toml, beside its image's build context, pruned to what its Dockerfile reads.agents/<name>/: only with--include-scope user, since the agent a step names is the user's.artifacts/<name>/: files, file universes, environments, environment universes and CLIs.skills/<name>/: skills, in folders named by the skill's own name.provenance.jsonwith store ids and versions only, no bucket URLs.Every reference to something written into the folder becomes its local name, with no pin.
How
to_toml(out), the inverse offrom_toml. A type never downloads anything itself; it asks the writer for another entity's local name, an image's toml value, or a file at a checked path.get --output-dirwriters for universes, CLIs, skills and file universes move onto their types, and the CLI calls them.--include-scope userthe image stays a pinned store ref, reported under Still needs the store.docker savetarball, the import reads the Python and global npm packages it holds, without Docker.constraints.txtbeside the Dockerfile, passed to each stage runningpip install|wheel|downloadoruv pipasPIP_CONSTRAINTandUV_CONSTRAINTbuild args. Build args, notENV, so the variables don't persist into the running container.npm install -ggets exact versions.tar -xextracts it.build_mcp_clidoes, means the env an earlier step deployed, which a folder holds once. An artifact or agent named with no version keeps the latest, in a folder of its own, as the store run reads it.__pycache__,.DS_Store) are left out and reported, as a bundle leaves them out of every folder it reads.bundle initprints the total download size first, and lists the entity folders it wrote.agent-env runchecks before it startsdocker composeresolves when a gateway stack runs on this machine.docker buildxresolves when an image builds here. Docker 23+ failsdocker buildwithout it.Each refusal says how to install or set what's missing. VM and local builds now extract a stored context at extraction times (
tar -xzmf): BuildKit reuses a file from a same-named folder whose path, size and time match, so a changed file of the same size could build as the old one.Decisions
--include-scope userit stays in the store, pinned and reported. An image whose context can't be written but which names a registry is written as that ref instead, since a folder can hold a ref.to_tomlfails a default-scope import. That includes, for now, the sdk's plugin types, which getto_tomlin a follow-up.to_toml/from_toml.bundle packis left for later.Tests
to_toml/from_toml;installed_packageson synthetic legacy and OCIdocker savetarballs (whiteouts, opaque folders, linked layers);lock(multi-stage,uv pip, heredocs, lock files, versions asked for and ended past, ignore files, npm -g); hard links; the preflight checks.bundle_init_images_test: an image stored built at old versions imports, plans against an empty store, and rebuilds at the stored versions, though its Dockerfile names none.vm_sandbox_build_test: two stored contexts built on one host as a VM builds them each hold their own file. It fails on main.--include-scope user, and fall back to the ref when the image names a registry.claude-code-cli, copied in from a--include-scope userimport and named the default.Imports, all succeeding:
build_mcp_clitask;run_docker_containertask whose Docker context is an artifact.The imports found the hard-link, wrong-type-field, unpinned-reference,
__pycache__and no-Dockerfile cases above.Every folder dry-runs against an empty store on both machines.
Both machines:
run_codegives.The
run_docker_containertask builds its context from the folder and runs the container (Linux, as root, since the local provider's VM sandbox is this machine). It stops later, whererun_codetargets an agent containerinstall_agentput inside that container: a local-provider issue, not this PR's.The website and Brookfield folders stop before running. Their images are refs to a private registry, which agent-env reads only through its configured image store; the README says so.
The iOS skills task fails at
add_skills: current core sends a stored skill as objects, which that agent's version doesn't take.🤖 Generated with Claude Code


Confidence Score: 5/5
The PR appears safe to merge; no new blocking issue remains.Summary
bundle initwrites referenced entities and image build contexts into a runnable folder. The latest changes extract helpers and share constants without changing their behavior.Diagram
%%{init: {'theme': 'neutral'}}%% flowchart LR A[Stored task or eval] --> B[Read referenced entities] B --> C[Write entity folders] B --> D[Download shared build contexts] D --> E[Prune files for each image] E --> F[Read saved package versions] F --> G[Pin compatible installs] C --> H[Write local references and reports] G --> H H --> I[Move completed bundle into place]Reviews (6) · Last reviewed commit: "bundle init's guidance has one source ea..." · Reviewed by Greptile