Repository navigation
fix(devcontainer): pin the image's tools and own the credential directory - #51
Merged
Merged
Conversation
…tory `npm install -g <pkg>` with no version is a layer whose hash never changes, so Docker serves the first build's answer for as long as the cache survives while the registry moves on — and the rebuild that looks like it fixed that did not. Measured: an image rebuilt the same day carried scc v0.23.0 into a workspace whose settings.json registers the UserPromptSubmit hook v0.24.0 added, so every prompt of the session answered `unknown hook stage "user-prompt"`. The three tools are pinned to build ARGs now; bumping an ARG is what invalidates the layer. SCC_VERSION is the one with a contract to the workspace it runs in — the other two answer to nobody here. And a named volume mounted onto a path the image never created is created root-owned: Docker copies an image path's ownership into a fresh volume only when that path already exists. Without `/home/node/.claude` in the image, the first write of every session was `EACCES ... mkdir '/home/node/.claude/session-env'`, and the login it could not store read back as "not logged in". The image creates and chowns it. Verified: go build, golangci-lint run (0 issues), golangci-lint fmt --diff, the full suite, and `scc validate` (10 validators, 0 findings). Two new tests in internal/cli hold both halves against the seeded files rather than against the prose explaining them.
…try file `scc launch` splices both into the entry file the agent is about to load — CodeGraph's under scc's own markers, RTK's under RTK's — and this checkout had been carrying them uncommitted. Committing them is what makes every session here start with the same guidance, rather than the first launch on a fresh clone writing them again. The bytes are the ones the binary writes; nothing in the prose above them changed.
The skill wrote a gauntlet prompt for any goal, with a named artifact as the bar — screenshot Nike, read the published piece. scc has nothing to screenshot. What it has is the question design/ponytail-port.md closed without answering: nothing measures whether the rules earn the bytes they cost in every request of every session. So the bar becomes an arm: two harness configurations running the same ticket, with the critic blind to which is which, and the control checked for leaks — the operator's global config is where the last one hid.
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.
What changed
Two failures measured in one dev-container session, both of them Docker's defaults rather than anything scc decided.
An unversioned
npm install -gis a layer whose hash never changes. Docker serves the first build's answer for as long as the cache survives while the registry and this workspace both move on — and the rebuild that looks like it fixed that did not. Measured: an image rebuilt the same day carried scc v0.23.0 into a workspace whosesettings.jsonregisters theUserPromptSubmithook v0.24.0 added, so every prompt of the session answeredunknown hook stage "user-prompt". The three tools are pinned to build ARGs now; bumping an ARG is what invalidates the layer.SCC_VERSIONis the one of the three with a contract to the workspace it runs in — scc scaffolds the harness hooks and this scc runs them — so the comment says to keep it at or above the version that scaffolded the workspace. Claude Code updates itself at runtime and codegraph answers to nobody here; those two are pinned only so a rebuild is reproducible.A named volume mounted onto a path the image never created is created root-owned. Docker copies an image path's content and its ownership into a fresh named volume only when that path already exists. Without
/home/node/.claudein the image, the agent's first write of every session wasEACCES: permission denied, mkdir '/home/node/.claude/session-env', and the login it could not store then read back as "not logged in". The image creates and chowns it.Both hunks land identically in the seeded
.devcontainer/Dockerfileand in the template it came from, andassets.Versiongoes 32 → 33 with the fingerprint updated.Two commits ride along, separated so approving the fix is not approving them:
chore(workspace)— the CodeGraph and RTK usage blocksscc launchsplices into the entry file, which this checkout had been carrying uncommitted.docs(skills)—gauntlet-loopnarrowed from "any goal, with a named artifact as the bar" to scc's own methodology, where the bar is an arm: two harness configurations on the same ticket, critic blind.Which spec or plan
Neither — a fix. No box to tick.
How it was verified
go build ./...golangci-lint fmt --diffgolangci-lint run0 issues.go test ./...scc validateTwo new tests in
internal/cli/init_test.gohold each half against the seeded files rather than against the prose that explains them:TestTheSeededImagePinsTheToolItMustMatchasserts the ARG and the@${SCC_VERSION}install, andTestTheSeededImageOwnsTheCredentialDirectoryasserts themkdir/chownand thatdevcontainer.jsonstill mounts a volume there, since neither file states the failure alone.Reviews.
scc-code-review: changes-requested — one major, "the working tree mixes unrelated changes", which is what the three-commit split above answers; its minor (there is nobuild.argsoverride path indevcontainer.json, soSCC_VERSIONis bumped by editing the Dockerfile) is accepted as-is and matches how the seed is meant to be owned.scc-security-review: clean — no reachable findings. It notes the ARGs interpolate unquoted into thesu node -cstring, and that this crosses no trust boundary: whoever passes--build-argalready controls the build host, and the same pattern already sits untouched in this file forGO_VERSIONandGOLANGCI_VERSION.Known, deliberately not done here
SCC_VERSIONstays at0.25.0rather than moving to the version this template ships in. Template 33 changes nothing but the Dockerfile — no new hook stage, no new vocabulary — so 0.25.0 is a binary that understands everything this scaffold writes, and pinning to a version that is not on npm yet would leave the image unbuildable in the window before the release publishes.