ADR 023 Project-Owned Remote Image Build
Context
Section titled “Context”The development Compose file owned a local build: definition and Mutagen
started the stage with docker compose up --build. Workstation disk, daemon
state, cache contents, and CPU capacity therefore became release inputs.
GitHub-hosted image builds would add cold caches, execution limits, duplicated
workflow code, and unnecessary cost.
FractalOps already operates the company Kubernetes BuildKit plane. Using shared compute must not place Sanmopia source, images, releases, or domain decisions under FractalOps ownership.
Decision
Section titled “Decision”Sanmopia owns:
- repository source and
.fractalops/image-build.json; - the
ghcr.io/yamonco/sanmopia-modernization-assemblyimage namespace; - immutable image digest selection and deployment promotion;
- release lifecycle, rollback, and integrated evidence.
yamonco/.github owns the reusable GitHub contract
company-remote-build-v4. The assembly caller references only that contract,
validates a default-branch source revision, dispatches, links execution status,
and exits. GitHub-hosted compute does not run BuildKit, scans, or signing.
FractalOps owns only the versioned remote build executor adapter and shared
build plane. remote-build-executor-v4 validates the bounded project profile,
attaches the Kubernetes fractalops-buildx builder, uses isolated registry
cache, builds and pushes the project-owned image, scans high/critical
vulnerabilities, signs with Cosign, verifies the digest, and emits immutable
evidence. FractalOps names may appear as provider provenance but never as the
project image namespace or release authority.
Compose contains no build: definitions. Mutagen contains no --build
command. Stage startup only pulls the digest in SANMOPIA_ASSEMBLY_IMAGE.
Local Docker builds, GitHub-hosted builds, per-project BuildKit daemons, and the
BuildKit remote driver are not fallback paths.
Only image compilation belongs to Buildx. Pulling an already-built immutable
runtime image is acquisition, not a fallback build. The project must reuse the
existing self-hosted Supabase stage; supabase start and supabase stop are
not project lifecycle commands because they bypass that stage and may pull a
second runtime stack onto the workstation.
Evidence
Section titled “Evidence”pnpm stage:checkrejects local Compose builds, Mutagen--build, mutable or foreign image namespaces, unpinned Dockerfile bases, profile drift, local Buildx implementation,--load, local Supabase CLI lifecycle commands, and drift away from thefractalops-buildxKubernetes builder orbuildx-cluster-buildpreview phase.- Organization contract
company-remote-build-v4resolves toc130121a176f55ffeac15fc3f48a95e80d810ffe. - FractalOps execution adapter
remote-build-executor-v4resolves to11c43fa6d7b01907d86cd98d42dcffccecb47a6f. - Execution 29155893875
published source
86c2c0ac652969979fc4373716c30b2cb24f595dthrough the Kubernetes driver, passed the high/critical vulnerability gate, uploaded and registry-verified a Cosign signature, and retained a Sigstore v0.3 bundle. - Stage pins
ghcr.io/yamonco/sanmopia-modernization-assemblyatsha256:41ee2a09368b02f829cc8dd7bfd129eee8ba4b6059baf02b895b71d241dd632f.
Consequences
Section titled “Consequences”Developer machines and GitHub-hosted runners no longer compile Sanmopia images. Shared cache and compute reduce latency without changing project ownership. A build-plane outage fails the release visibly instead of falling back to a workstation. Contract and executor changes require separate reviewed version bumps.
Revision History
Section titled “Revision History”- 2026-07-11: accepted project-owned image releases with organization-owned GitHub contract and FractalOps remote execution adapter.
- 2026-07-13: made the FractalOps Buildx builder and preview phase executable stage-gate invariants; prohibited local Supabase CLI lifecycle fallback.