Skip to content

Build and review in per-run container workspaces - #845

Merged
Jack-Edwards merged 2 commits into
stablefrom
chore/ephemeral-container-workspace
Aug 6, 2026
Merged

Build and review in per-run container workspaces#845
Jack-Edwards merged 2 commits into
stablefrom
chore/ephemeral-container-workspace

Conversation

@Jack-Edwards

@Jack-Edwards Jack-Edwards commented Aug 5, 2026

Copy link
Copy Markdown
Member

The container kept a long-lived clone of the org repository in a named volume, and nothing ever advanced its working tree. The pipeline's own skills and agents live in that tree, so they were frozen at whatever commit the volume was created with. The rename in #842 made it fatal: the host now invokes crypter-devcontainer-implement while the container still only knows crypter-implement, so /crypter-change and /crypter-triage-review both fail at their first docker exec.

Workspaces are now created per run at /work/{run-id} and removed when the run ends, cloned from /host-git — a read-only mount of the host's .git. Nothing holding a copy of the code outlives the run that made it, so there is nothing left to drift. Read-only is what makes sharing history safe: the container reads commits and cannot move a ref or add an object. The host working tree is not mounted, so uncommitted work stays invisible to the agents.

The orchestrators own the workspace lifecycle and the container skills assume one exists, mirroring how /runs already works. That retires the worktree add/worktree remove pairing in every container skill and the stale worktree a crashed run used to leave behind.

The devcontainer image is now local only. It is built from .devcontainer/Dockerfile, tagged crypter-devcontainer:local, and carries tooling rather than source, so nothing outside the machine that builds it ever consumes it. build-and-push-devcontainer.yml is deleted along with the GHCR publish it performed.

Things worth knowing as a reviewer:

  • pr-build-devcontainer stays. It pushes nothing and catches a Dockerfile that does not build.
  • The Dockerfile adds git config --system --add safe.directory /host-git. Git refuses to read a repository owned by another uid, and the mount is owned by the host user by design.
  • .devcontainer/clone-upstream.sh is replaced by .devcontainer/workspace.sh.
  • Pull request heads now reach the container through the host: the orchestrator fetches refs/pull/{n}/head into its own repository and the workspace clone brings it across, since the container no longer has a network remote.
  • NuGet and pnpm caches move to named volumes, because ephemeral workspaces would otherwise restore from nothing every run.

Verified by building the image and running a workspace end to end: clone, upstream/stable resolution, branch and commit, wasm-tools restore of Crypter.Test.Web, refusal of writes to /host-git, and teardown.

n and others added 2 commits August 5, 2026 14:11
The container kept a long-lived clone of the org repository in a named volume.
Nothing ever advanced its working tree, so the pipeline's own skills and agents
were frozen at whatever commit the volume was created with. The rename in #842
made that visible: the host began invoking crypter-devcontainer-implement while
the container still only knew crypter-implement, and both orchestrators broke.

Workspaces are now created per run at /work/{run-id} and deleted when the run
ends, cloned from a read-only mount of the host's .git. A copy of the code no
longer outlives the run that made it, so it cannot drift. The mount being
read-only is what makes sharing history safe: the container reads commits and
cannot move a ref or add an object. The host working tree is not mounted, so
uncommitted work stays invisible to the agents.

The orchestrator owns the lifecycle and the container skills assume a workspace
exists, mirroring how /runs already works. This also retires the worktree
add/remove pairing in every container skill, along with the stale worktree a
crashed run used to leave behind.

The image is built locally instead of pulled from GHCR. It carries tooling and
no source, so a change to workspace.sh or the Dockerfile is a rebuild rather
than a publish waiting on an approval.

NuGet and pnpm caches move to named volumes. Workspaces are ephemeral, so
without them every run would restore from nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The image is built locally and carries tooling rather than source, so nothing
consumes the published copy. Publishing it on every merge to stable left a
registry image nobody pulled and an approval gate on changes that only ever
affect the machine making them.

pr-build-devcontainer stays. It pushes nothing and catches a Dockerfile that
does not build, which is the only part of the publish path that was earning its
keep.

The devcontainer environment on the repository has no jobs left referencing it
and can be deleted from the repository settings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Jack-Edwards
Jack-Edwards merged commit de18bcb into stable Aug 6, 2026
13 checks passed
@Jack-Edwards
Jack-Edwards deleted the chore/ephemeral-container-workspace branch August 7, 2026 01:38
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