Package-level Image Build/Rebuild Lifecycle
Context
The lifecycle-tasks ADR defines the task vocabulary. It places two "build" concerns in tension:
augr:buildmeans environment config aggregation (env.d/*->.env.project), not Docker images.augr:rebuildmeans force a Docker image rebuild (--no-cache) and recreate containers.
That ADR explicitly accepted the build (env) vs rebuild (image) naming as a
trade-off ("non-obvious but consistent within the autograph domain"). In
practice this surfaces several problems:
- Lexical ambiguity.
buildandrebuildlook like a base/re-pair but belong to different domains (config vs image).rebuildis not "build again". - Coupling. The global
augr:rebuildran a stack-widedownbefore a no-cache image build -- a surgical image concern tied to a full teardown. - Wrong compose project. The package task
augr:pkg:agent:buildbuilt without--project-name, so Docker derived the project from the task directory and produced a differently-named image that the running stack (started byaugr:upwith an explicit project name) never adopted. - Shared image, two consumers. The agent image is used by both
dev-agentandmetalab, which run in separate compose projects. A single global rebuild risks both agents at once -- including the agent driving the work.
We want: an unambiguous vocabulary; the Docker image build integrated
coherently into the lifecycle (not conflated with env build); and a safe way
to roll a new shared image without taking the whole stack (and both agents)
down simultaneously.
Options considered
Option A - Keep the global `augr:rebuild`, accept the ambiguity
Status quo. Document the build/rebuild distinction harder.
- Advantage: Zero change. Symmetric with
up/down/ps/logsat theaugr:level. - Disadvantage: The ambiguity and the stack-wide-
downcoupling remain. The wrong-project package build stays broken. No safe shared-image staging.
Option B - Rename env `augr:build` to free `build` for Docker
Make build mean "docker image build" (compose-consistent) and rename the env
step (e.g. augr:config).
- Advantage:
buildwould match the docker-compose meaning operators expect. - Disadvantage: Large churn --
augr:buildis established across docs, ADRs, the bootstrap sequence, and muscle memory. High blast radius for a naming preference.
Option C - Image build/rebuild at the package level; drop the global rebuild (chosen)
Keep augr:build = env config. Keep the Docker image build integrated inside
augr:up (idempotent, cached). Remove the global augr:rebuild. Expose image
build/rebuild per package, where "build" already means "build image".
- Advantage: Removes the ambiguity by elimination (no global
rebuild);buildis unambiguously env at theaugr:level and image at the package level (compose-consistent). Enables a staged shared-image rollout. Fixes the project-name bug. - Disadvantage: Operators must learn that image rebuild moved to
augr:pkg:agent:rebuild. Mitigated by a pointer note in the lifecycle Taskfile header.
Decision
Option C.
augr:buildstays env config aggregation (unchanged).Docker image build stays integrated in
augr:up(idempotent, cached) as part of starting the stack. It runs in its own Task invocation, so it reads a freshly aggregated.env.project-- respecting the dotenv-at-parse-time constraint (config regeneration and image build must not share one invocation).The global
augr:rebuildis removed. The only built image is the agent image, replaced byaugr:pkg:agent:rebuild.Package-level image verbs. Three verbs, scoped by whether the package owns the image (holds the Dockerfile) or reuses a shared one:
build-- build the image, idempotent (layer cache; cheap when up to date). Image owner only.rebuild-- force a from-scratch build (--no-cache), then recreate the package's container. The owner rebuilds its own image; a consumer of a shared image rebuilds that shared image through the owner's compose/build context, with a disclaimer in the task that the Dockerfile belongs to the owner.reup-- recreate the package's container on the current image, no build. Owner and consumers. All MUST pass the engine's compose--project-nameso images and containers align with whataugr:upcreates.
Staged rollout for a shared image. When an image is shared across compose projects (the agent image serves
dev-agentandmetalab): (1)<owner>:rebuildrebuilds the image and recreates the owner's container (augr:pkg:agent:rebuild-> dev-agent); (2) validate; (3)<consumer>:reuprecreates each other consumer on the proven image, no rebuild (augr:pkg:metalab:reup). Consumers keep running on the old image until theirreup, so the agent driving the work is never torn down together with its target.Builds go through
docker compose build(Compose V2 -> BuildKit by default), never a baredocker build. This keeps BuildKit features (notably build secrets) working on engines where BuildKit is not the default builder.
Convention (build / rebuild / reup)
| Verb | Semantics | Exposed by |
|---|---|---|
build |
Idempotent "ensure built" -- uses the cache, no-op if fresh | image owner |
rebuild |
Force "build again from scratch" (--no-cache) + recreate container |
owner and consumers |
reup |
Recreate the container on the current image, no build | owner and consumers |
The image owner is the package holding the Dockerfile. A consumer reuses
a shared image: its rebuild rebuilds that shared image through the owner's
build context (with a disclaimer) and recreates only its own container; it has
no build (the idempotent ensure-built is the owner's concern, also covered by
augr:up).
Consequences
- The
build/rebuildambiguity disappears at theaugr:level (no globalrebuild;buildunambiguously = env). At the package level the pair is docker-compose-consistent. - Amends the lifecycle-tasks ADR:
rebuildmoves fromaugr:toaugr:pkg:<name>:and gains a siblingreup; the verb matrix and package-task table gainbuild/rebuild/reupat the package level. That ADR's Status is annotated to point here. It also updates the safe-metalab-contribution-workflow ADR, whose shared-image and staged-rebuild references now useaugr:pkg:agent:rebuild+augr:pkg:metalab:reup. --project-namecorrectness becomes mandatory for package image tasks (fixes the previously mis-projected agent build).proxynow builds its own image from the Ubuntu 26.04 archive to receive supported Squid security updates. It exposesrebuild,reup, andwait:rebuildforces a fresh package install,reupadopts the current image, andwaitmakes a startup failure visible before dependent agents start.metalabreuses the agent image: it exposesrebuild(rebuilds the shared image via pkg.agent, disclaimered) andreup, but notbuild.reupmakes image rollback correct: after retagging a backup image,augr:pkg:agent:reuprecreates the container on the restored image without a rebuild -- whereasaugr:upwould rebuild from the current (possibly broken) sources and undo the restore.- Portability: forcing all image builds through
docker compose buildkeeps the npm-registry build secret (a BuildKit--mount=type=secret) working on older engines where a baredocker buildwould not enable BuildKit by default. - Easier: safe shared-image refresh, clear vocabulary, portable builds. Harder:
the
augr:rebuild->augr:pkg:agent:rebuildrelearn (mitigated by the header pointer).
Follow-ups
- Surface the build/rebuild convention in the package interface documentation and the seed/project docs.
- Apply the convention to any future package that builds its own image.