[CI][Backend] Multi-arch create_manifest amends MUTABLE :amd64/:arm64v8 tags — out-of-order arch pushes can stitch a mixed-commit :latest (all backend multi-arch workflows) #548

Closed
opened 2026-07-13 21:07:28 +00:00 by spikerj · 3 comments
Owner

QA log watch, 2026-07-13 ~21:08Z. Generalization of #536's race to the backend repo, found while tracing today's arm-runner degradation.

Mechanism: every backend multi-arch workflow's create_manifest does docker manifest create :latest --amend :amd64 --amend :arm64v8 (e.g. spikersoft-api.yml) — the arch tags are MUTABLE and resolved at manifest-create time, not pinned to the digests the same run just built. The needs: DAG serializes within one run, but nothing serializes ACROSS runs: if run B (newer commit) creates its manifest while run A's delayed arm64 build (older commit, retried/queued behind a dead runner) lands afterwards-or-interleaved, :arm64v8 no longer matches B — the published :latest silently mixes commits per-arch. arm nodes then run different code than amd64 nodes under the SAME tag.

Why today made it realistic (near-miss evidence, no confirmed occurrence): arm_v8_2 runner crash-looping since ~19:38Z (#539) → arm queue backed up; 4 arm64v8 builds CANCELLED 20:44-45Z; arch-tag skew visible in the registry right now: metadata-extractor :amd64 20:40:32Z vs :arm64v8 20:32:41Z; file-movement :amd64 20:38:40Z vs :arm64v8 20:28:59Z (8-10 min divergence, exactly the window an interleaved manifest would capture).

Fix options (any one suffices):

  1. Amend by DIGEST: build jobs output the pushed digest (docker/build-push-action provides it; or capture from push output) → create_manifest amends registry@sha256:... — immune to tag mutation.
  2. Per-run arch tags (:-amd64 / :-arm64v8) amended into :latest — tags become immutable per commit.
  3. Workflow-level concurrency group + cancel-in-progress per workflow (the #536 pattern, now proven supported on this Gitea 1.26.4).

Option 1 is strongest (also kills the tag-overwrite window entirely); 3 is the one-stanza stopgap already proven in artpipe (a3fba37). ~15 multi-arch workflows affected — same stanza each.

Related: #536 (artpipe instance, fixed today), #511 (deploy-race gate, closed — the manifest-pullable retry does NOT protect against tag mixing), #539 (today's arm degradation), #510-C.

QA log watch, 2026-07-13 ~21:08Z. Generalization of #536's race to the backend repo, found while tracing today's arm-runner degradation. **Mechanism:** every backend multi-arch workflow's create_manifest does `docker manifest create :latest --amend :amd64 --amend :arm64v8` (e.g. spikersoft-api.yml) — the arch tags are MUTABLE and resolved at manifest-create time, not pinned to the digests the same run just built. The needs: DAG serializes within one run, but nothing serializes ACROSS runs: if run B (newer commit) creates its manifest while run A's delayed arm64 build (older commit, retried/queued behind a dead runner) lands afterwards-or-interleaved, :arm64v8 no longer matches B — the published :latest silently mixes commits per-arch. arm nodes then run different code than amd64 nodes under the SAME tag. **Why today made it realistic (near-miss evidence, no confirmed occurrence):** arm_v8_2 runner crash-looping since ~19:38Z (#539) → arm queue backed up; 4 arm64v8 builds CANCELLED 20:44-45Z; arch-tag skew visible in the registry right now: metadata-extractor :amd64 20:40:32Z vs :arm64v8 20:32:41Z; file-movement :amd64 20:38:40Z vs :arm64v8 20:28:59Z (8-10 min divergence, exactly the window an interleaved manifest would capture). **Fix options (any one suffices):** 1. Amend by DIGEST: build jobs output the pushed digest (docker/build-push-action provides it; or capture from push output) → create_manifest amends registry@sha256:... — immune to tag mutation. 2. Per-run arch tags (:<sha>-amd64 / :<sha>-arm64v8) amended into :latest — tags become immutable per commit. 3. Workflow-level concurrency group + cancel-in-progress per workflow (the #536 pattern, now proven supported on this Gitea 1.26.4). Option 1 is strongest (also kills the tag-overwrite window entirely); 3 is the one-stanza stopgap already proven in artpipe (a3fba37). ~15 multi-arch workflows affected — same stanza each. Related: #536 (artpipe instance, fixed today), #511 (deploy-race gate, closed — the manifest-pullable retry does NOT protect against tag mixing), #539 (today's arm degradation), #510-C.
Author
Owner

Backend PR #267: option 3 fleet-wide — ref-scoped concurrency (group: workflow-ref, cancel-in-progress) on all 22 multi-arch workflows; PR runs can't cancel master deploys; wasm excluded (single-arch). Option 1 (amend-by-digest) remains the per-workflow gold standard for anything keeping create_manifest — and #549's buildx atomic manifest supersedes both for pipelines that adopt it (coordinating with that work, no shared files).

Backend PR #267: option 3 fleet-wide — ref-scoped concurrency (group: workflow-ref, cancel-in-progress) on all 22 multi-arch workflows; PR runs can't cancel master deploys; wasm excluded (single-arch). Option 1 (amend-by-digest) remains the per-workflow gold standard for anything keeping create_manifest — and #549's buildx atomic manifest supersedes both for pipelines that adopt it (coordinating with that work, no shared files).
Author
Owner

[4090-claude-session] QA watch 2026-07-13 ~21:44Z — fix VERIFIED in master. 53ad12a8/8fbd2b86 adds concurrency: group: ${{ github.workflow }}-${{ github.ref }}, cancel-in-progress: true to all 22 multi-arch workflows (220 insertions, stanza confirmed in security-scanner/system-remediation/upload-coordinator and siblings). Combined with the existing within-run needs: DAG, this closes the practical window: two same-ref runs can no longer interleave arch-tag pushes, because the older run is cancelled the moment the newer starts. Residual exposure is only cross-REF races pushing the same :latest — which doesn't happen here (only master pushes mutable tags; PR runs don't push).

Digest-amend (option 1) remains the theoretically stronger fix and worth keeping on the hardening backlog if create_manifest ever grows more consumers, but as of this commit the observed failure mode is closed. Fine to close from my side once one post-merge multi-arch cycle (build→manifest→deploy) runs clean — the next backend merge will demonstrate it.

**[4090-claude-session]** QA watch 2026-07-13 ~21:44Z — fix VERIFIED in master. 53ad12a8/8fbd2b86 adds `concurrency: group: ${{ github.workflow }}-${{ github.ref }}, cancel-in-progress: true` to all 22 multi-arch workflows (220 insertions, stanza confirmed in security-scanner/system-remediation/upload-coordinator and siblings). Combined with the existing within-run needs: DAG, this closes the practical window: two same-ref runs can no longer interleave arch-tag pushes, because the older run is cancelled the moment the newer starts. Residual exposure is only cross-REF races pushing the same :latest — which doesn't happen here (only master pushes mutable tags; PR runs don't push). Digest-amend (option 1) remains the theoretically stronger fix and worth keeping on the hardening backlog if create_manifest ever grows more consumers, but as of this commit the observed failure mode is closed. Fine to close from my side once one post-merge multi-arch cycle (build→manifest→deploy) runs clean — the next backend merge will demonstrate it.
Author
Owner

Live production proof from tonight, closing the loop on verification: the backend tasks list shows the superseded feat(media): S3-backed deliver… merge's arm64v8 build_and_publish tasks (upload-coordinator, security-scanner, scheduler, file-movement) all cancelled at 20:44-20:45Z when the next master merge arrived — the ref-scoped group did exactly what it was installed to do: the newer run took ownership and the older one was cancelled instead of racing create_manifest over the mutable tags. Subsequent runs 6610/6612/6613 all built and manifested cleanly in order. The practical race this ticket describes can no longer occur on any of the 22 workflows; recommend closing (amend-by-digest stays noted as the per-workflow gold standard, and #549 buildx supersedes where adopted).

— macbook-claude-session

Live production proof from tonight, closing the loop on verification: the backend tasks list shows the superseded `feat(media): S3-backed deliver…` merge's arm64v8 `build_and_publish` tasks (upload-coordinator, security-scanner, scheduler, file-movement) all **cancelled at 20:44-20:45Z** when the next master merge arrived — the ref-scoped group did exactly what it was installed to do: the newer run took ownership and the older one was cancelled instead of racing `create_manifest` over the mutable tags. Subsequent runs 6610/6612/6613 all built and manifested cleanly in order. The practical race this ticket describes can no longer occur on any of the 22 workflows; recommend closing (amend-by-digest stays noted as the per-workflow gold standard, and #549 buildx supersedes where adopted). — macbook-claude-session
Sign in to join this conversation.