feat(repos): per-repo dev image — repos.image_ref, digest-pinned on save, pull-if-missing at spawn #217
No reviewers
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/coding-lab!217
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "afk/207"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #207
Each container repo can now pick the dev image its sessions run in, per the any-image + injected-tools design: nullable
repos.image_refend to end (migration → store → reposvc → API → repo-settings UI), with NULL inheriting the global--container-image— demoted from "required" to an optional default (ADR-0053).What landed
0018_repo_image_ref.sql(both dialects, goose, parity-checked);Repo.ImageRef *stringfollowing thecontainer_memorynullable pattern exactly.internal/imageref(new, stdlib-only): fully-qualified ref parsing (docker.iolibrary/shorthand normalized, podman-style short names rejected with actionable messages) and tag→digest resolution against the registry v2 API — anonymous, HTTPS-only (no http fallback, token realms must be https, redirects may not leave https),Docker-Content-Digestwith a GET+sha256 fallback capped at 4 MiB. Stored form keeps the tag:host/path:tag@sha256:….PinImageRef(production:imageref.Resolver.Pin; nil fails closed — an unpinned ref is never stored). Resolution failures surface verbatim as 400s in the settings form banner.refuseContainerSpawnresolves the effective image (repo override else global; neither set → refusal naming both knobs) and newpodmanx.EnsureImagerunsimage exists→ onepullon miss — both pre-claim, so a bad or unpullable image refuses the spawn as an actionable 400 and never parks an AFK issue. The pane'sRunSpec.Imagecarries the effective image.--container-imageis no longer a startup failure (per-repo-only deployments are valid); the no-effective-image case moved to spawn, where the repo is known.Acceptance criteria → proof
TestRepoImageRefColumnRoundTrip(both dialects),TestRepoImageRef(httpapi),Runner.test.tsx; fallback inTestRefuseContainerSpawn_effectiveImage.internal/imagerefsuite (58 tests, incl. Bearer token flow, GET+hash fallback, https-realm rejection),TestUpdateSettingsImageRefPinsOnSave.TestStart_containerImageResolution(override drives pane argv + EnsureImage; pull failure →BadRequestErrorpre-claim, no worktree/run row),TestEnsureImage.Verification
go test ./...(44 pkgs),go vet,gofmt,golangci-lint run(0 issues),go build ./...; web:tsc --noEmit,eslintclean,vitest run933/933,vite build. The full container loop remains maintainer-validated on the dev host (ADR-0052 §consequences — the sandbox has no rootless podman).Notes for review
/opt/lab) and future features will import additional content the same way, so images must treat lab's mount points as reserved and never depend on writing to them (ops.md + ADR-0053 pin 7). Flag if this meant something else.image exists.🤖 Generated with Claude Code
[autoland] verdict: pass
PASS. CI green (ci/native success, 7m51s). Conventional Commits title + Closes #207 present. Diff matches issue #207 end to end: nullable repos.image_ref (migration both dialects → store → reposvc pin-on-save → httpapi → Runner UI), internal/imageref https-only anonymous tag→digest resolver, spawn-time EnsureImage pull-if-missing pre-claim, preflight relaxation, ADR-0053 + docs. Fail-closed nil-pinner, pre-claim 400 refusals, dialect-parity migrations. No out-of-scope changes.