Skip to content

feat(release): publish the Claude Code agent's image - #288

Merged
earakely-scale merged 2 commits into
mainfrom
feat/publish-claude-code-image
Oct 21, 2026
Merged

earakely-scale merged 2 commits into
mainfrom
feat/publish-claude-code-image

Conversation

@earakely-scale

@earakely-scale earakely-scale commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

Why. Claude Code is the default agent. A remote agent deploy takes 56–63 s, and 51–58 s of that is building its image in the VM, on every run. This is the first of two PRs that let a deploy pull a published image instead, the way #277 does for the infra images. This one publishes the image. The next one registers it by digest when a run uses the shipped agent, with the build as the fallback.

Change

  • Published list: published_images.AGENT_IMAGES adds agentenv-claude-code, built from src/agent_env/agents/agents/claude-code/Dockerfile with its folder as the context. bootstrap.PUBLISHED_IMAGES includes it, so the wheel record and the release notes cover it.
  • Release order. A release pins the agent's requirements.txt to its agentenv-framework-protocol version. That pin is one of the image's sources, so the image's tag changes every release, and its build needs that protocol version on PyPI. So:
    • infra-plan lists the missing infra images and the missing agent images separately (infra_images.py plan --only infra|agents).
    • The infra images build and publish before protocol, as before, and protocol now waits only for them.
    • New agent-build and agent-publish jobs run once protocol is on PyPI. When the workflow is run by hand, they run once main's CI passes. They run the same steps as infra-build and infra-publish, on the agents' list, and a test keeps the two pairs equal.
    • infra-record waits for all four image jobs, and framework still waits for the record.
  • One pin: shipped_bundles.py pin replaces the framework job's sed loop. It uses only the standard library, so the jobs that push images can run it.
    • Every job that reads the shipped bundles' folders runs it first: the plan, both builds, the record and the framework. That way an image's tag, its build and the wheel all see the same sources.
    • A test finds those jobs by what they run, and checks that each one pins before it reads.
    • Another test checks that the pin changes the sources of exactly the images built after protocol.
  • Smoke check: claude --version and python -c "import a2a_server" in the image.
  • Release scripts: infra_build.py's stdlib IMAGES table lists the image too. Its existing test keeps that table equal to the published list.
  • Dependabot watches the agent's folder, whose node and python bases are already pinned by digest. The base-pin test from fix(env): pin the infra images' base images by digest, and let Dependabot bump them #283 now covers this image too.
  • Docs: the agents README says a release publishes the image, CLI included, where it used to say the image is never published. CONTRIBUTING.md and AGENTS.md describe the order. The release notes heading is now "Images published for this release".

Costs of building after protocol

  • An agent image every release. Each release builds and pushes the image for both platforms, between protocol and framework. That adds one more image to GHCR per release.
  • A failed agent image job leaves the protocol on PyPI without the framework. Re-running the failed jobs finishes the release, since the protocol's upload is skipped once it's there.
  • A run by hand on main builds the agent image against the protocol version main is at, which must already be on PyPI.

Publishing Claude Code's CLI is a deliberate decision. The image holds Anthropic's claude native build, under its Commercial Terms, and until now the image was only ever built where it ran. The maintainers decided to publish it, CLI included. The alternative was publishing everything except the CLI and downloading the CLI from Anthropic at container start.

After merge

  • First publish: the next release builds the image, records it, and ships the record in the wheel.
  • The first push creates the agentenv-claude-code package. Its access settings need the same lockdown as the other five: inherited repo access off, and Actions access for this repo only. The release's public step warns if it isn't public.
  • Bumping the CLI: Dependabot doesn't bump CLAUDE_CODE_VERSION. Changing it changes the sources, and the next release rebuilds the image as it does every release.

Verified

  • The pin, on a copy of the tree: shipped_bundles.py pin rewrites the three shipped requirements files to 0.1.358, the same lines the sed loop wrote. It changes only agentenv-claude-code's tag (src-01eb123e… to src-a9de08bf…); the other five tags don't change.
  • The wheel: one built from the pinned copy carries the agent's folder under that same tag, src-a9de08bf…, which is the tag an install computes.
  • The image: the pinned image builds and passes the smoke check, with agentenv-framework-protocol 0.1.358 installed.
  • Lint and tests: actionlint passes on the workflow, and the unit tier passes (8,943).
  • Not run: the workflow itself, which runs only on main and on release tags.

🤖 Generated with Claude Code

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge.

Summary

This PR adds the Claude Code image to release publication, including checks, recorded digests, and release notes.

  • Claude Code's image joins the published release images.
  • Releases publish missing agent images after the protocol package.
  • Shipped bundles pin the protocol version used by each release.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  CI[Main CI passes] --> Infra[Build and publish missing infra images]
  Infra --> Protocol[Publish protocol]
  Protocol --> Agent[Build and publish missing agent images]
  Agent --> Record[Record all image digests]
  Record --> Wheel[Build and publish framework wheel]
  Wheel --> Release[Create GitHub release]
Loading

Reviews (2) · Last reviewed commit: "fix(release): build the Claude Code imag..." · Reviewed by Greptile

…mages

The release workflow now publishes ghcr.io/scaleapi/agentenv-claude-code:src-<hex>, for linux/amd64 and linux/arm64,
built from the agents bundle's claude-code folder, Claude Code's CLI included. It goes through the jobs that publish
the infra images: built on a runner of each platform, checked with `claude --version` and an import of the agent's
server, tagged as one multi-platform image, and recorded in the wheel and the release notes. Dependabot watches its
digest-pinned bases. A run doesn't use it yet; that follows in core.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@earakely-scale
earakely-scale requested a review from a team as a code owner October 21, 2026 20:34
Comment thread src/agent_env/env/published_images.py
…lls is on PyPI

The agent's requirements.txt is pinned to the release's agentenv-framework-protocol, and the pin is one of the image's
sources, so its tag changes every release and its build needs that protocol on PyPI. The infra images are still
published before the protocol; new agent-build and agent-publish jobs, the infra jobs' steps on the agents' list, run
once the protocol is published, and the record, and so agentenv-framework, waits for them.

Every job that reads the shipped bundles' folders (plan, both builds, record, framework) first runs
shipped_bundles.py pin, which replaces the framework job's sed loop, so an image's tag, its build and the wheel all
see the same sources.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@earakely-scale earakely-scale changed the title feat(release): publish the Claude Code agent's image with the infra images feat(release): publish the Claude Code agent's image Oct 21, 2026
@earakely-scale
earakely-scale merged commit 79cacc1 into main Oct 21, 2026
15 checks passed
@earakely-scale
earakely-scale deleted the feat/publish-claude-code-image branch October 21, 2026 21:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant