Follow-up to pinpredict/trading#1967 (build-once Dockerfile, PR pinpredict/trading#1968) and part of the #38 migration. Captures the rollout plan and the findings from the 2026-07-05 verification session so they don't get lost.
Context — what's already done
- trading Dockerfile restructured build-once (trading#1968, 3 commits):
build stage restores+publishes all 12 .NET services with no build-arg references → byte-identical layers for every image; production stage stays PROJECT-parameterized and final. Verified: /app trees sha256-identical to the old per-service build (oms/reconciler/nadex-oegw); image sizes unchanged (±~25 B on ~124 MB); second image assembles in ~2 s from cache; full local stevedore build of the 13-image catalog passes with the publish layer CACHED on every image.
- trading
ci/stevedore-config branch ready (4 commits, rebased, local): includes two bugs found by actually building — missing RUNTIME_BASE (fixed by defaulting the Dockerfile to aspnet:10.0; every consumer already passed it) and settlements-csv needing context: . (its Dockerfile COPYs repo-root-relative paths).
- Measured baseline: full 13-job docker-release matrix averages ~69 s/job ≈ ~15 runner-min and ~1.5 min wall per shared release (GHA cache warm).
Why the CI shape matters
Trading's 13 images cannot form a stevedore build group (grouping = identical image, many repos — e.g. dis's 5-way group; trading's images differ by content). So:
- Matrix mode fan-out (plan → one runner per group): 13 cold BuildKit caches → the shared build stage compiles 13× (each a union compile) ≈ ~40–50 runner-min. Worse than today.
- Single-job shape (the current draft
stevedore-release.yml): one runner, one BuildKit; the solver dedups the shared build stage even across concurrent builds (parallel: 4 still compiles once) → ~4–5 runner-min per shared release.
Plan
Immediate wins that need no further work once #1968 merges
scripts/chaos-rig-*.sh and docker-compose builds (5–6 images serially from a developer checkout) drop from N full compiles to one compile + seconds per image, and builds are now hermetic from dirty checkouts (**/bin/**/obj excluded from the context).
Follow-up to pinpredict/trading#1967 (build-once Dockerfile, PR pinpredict/trading#1968) and part of the #38 migration. Captures the rollout plan and the findings from the 2026-07-05 verification session so they don't get lost.
Context — what's already done
buildstage restores+publishes all 12 .NET services with no build-arg references → byte-identical layers for every image;productionstage staysPROJECT-parameterized and final. Verified:/apptrees sha256-identical to the old per-service build (oms/reconciler/nadex-oegw); image sizes unchanged (±~25 B on ~124 MB); second image assembles in ~2 s from cache; full localstevedore buildof the 13-image catalog passes with the publish layerCACHEDon every image.ci/stevedore-configbranch ready (4 commits, rebased, local): includes two bugs found by actually building — missingRUNTIME_BASE(fixed by defaulting the Dockerfile toaspnet:10.0; every consumer already passed it) andsettlements-csvneedingcontext: .(its Dockerfile COPYs repo-root-relative paths).Why the CI shape matters
Trading's 13 images cannot form a stevedore build group (grouping = identical image, many repos — e.g. dis's 5-way group; trading's images differ by content). So:
stevedore-release.yml): one runner, one BuildKit; the solver dedups the shared build stage even across concurrent builds (parallel: 4still compiles once) → ~4–5 runner-min per shared release.Plan
type=ghascope now serves the shared build stage for all trading images (vs 13 per-service scopes thrashing the 10 GB repo cache limit under docker-release).notify-dispatch.ci/stevedore-configPR, flip trading'sci.ymloffdocker-release.yml@pre-stevedore, drop the pin.xp-<name>-gha-pushroles): cache affinity — coalesce plan groups that share(dockerfile, context)into one matrix entry with combinedonly:+ merged pins, so same-Dockerfile images land on one runner while dis-style repos still fan out. File on blairham/stevedore if pursued..stevedore.yamlfor the settlements-csv bug class (context:vs Dockerfile COPY paths) — it only surfaces when stevedore actually builds, so runstevedore buildlocally per repo before opening each config PR.Immediate wins that need no further work once #1968 merges
scripts/chaos-rig-*.shand docker-compose builds (5–6 images serially from a developer checkout) drop from N full compiles to one compile + seconds per image, and builds are now hermetic from dirty checkouts (**/bin/**/objexcluded from the context).