[Bug][CI][Registry] #548 was closed but its fix NEVER LANDED — all 23 of 23 multi-arch workflows still --amend mutable :amd64/:arm64v8 tags, and the cancelled-run window from #590 makes mixed-commit :latest reachable today
#596
Filed by: QA Team — referencing #548, which is CLOSED but whose fix never landed in a single workflow.
Summary
#548 ("Multi-arch create_manifest amends MUTABLE :amd64/:arm64v8 tags — out-of-order arch pushes can stitch a mixed-commit :latest") was closed on 2026-07-14. The described fix is not present in the repository. Every multi-arch workflow still uses the exact pattern the ticket flagged.
Evidence
Audited every workflow in .gitea/workflows/ that calls docker manifest create:
23 of 23 multi-arch workflows still --amend mutable arch tags
Zero workflows were converted to digest-pinned stitching. The ticket appears to have been closed without the change being made — or the change was reverted and nobody noticed, because nothing verifies it.
It compounds #587/#590. The confirmed root cause of #587 is that cancel-in-progress: true can cancel a run after the arch tags are pushed but beforecreate_manifest stitches :latest. Because the arch tags are mutable, this leaves the registry in a state where:
:amd64 may be from commit A (pushed, then the run was cancelled), and
:arm64v8 may be from commit B (a later run that completed),
and the next successful create_manifest will happily --amend those two into a :latest that is a mixed-commit chimera — an amd64 image from one commit and an arm64 image from another, under one tag, with no error and no warning.
That is exactly the failure #548 predicted. The conditions to trigger it (cancelled runs mid-push) are demonstrably occurring right now — see #587, where 10 images are sitting with healthy arch tags and no :latest.
Fix
Stitch from immutable digests, not mutable tags. Capture each arch push's digest as a job output and use docker buildx imagetools create against the digests:
Best combined with the #590 fix — make the arch push and the manifest stitch atomic (single job, or excluded from cancellation), so the tags and the index either both land or neither does. That closes the mixed-commit window and the missing-:latest window in one change.
Suggested process note
#548 was closed with no verification that the change existed. A one-line CI guard would prevent a recurrence:
#548 (closed) — the original ticket; fix never landed.
#587 (open) — 10 images with healthy arch tags and no :latest; the cancelled-run window that makes this exploitable.
#590 (open) — cancel-in-progress is what creates that window.
**Filed by: QA Team** — referencing **#548**, which is **CLOSED but whose fix never landed in a single workflow.**
## Summary
#548 ("Multi-arch `create_manifest` amends MUTABLE `:amd64`/`:arm64v8` tags — out-of-order arch pushes can stitch a mixed-commit `:latest`") was closed on 2026-07-14. **The described fix is not present in the repository.** Every multi-arch workflow still uses the exact pattern the ticket flagged.
## Evidence
Audited every workflow in `.gitea/workflows/` that calls `docker manifest create`:
```
23 of 23 multi-arch workflows still --amend mutable arch tags
```
```
spikersoft-api.yml spikersoft-influx-dashboard.yml
spikersoft-artstudio-metrics.yml spikersoft-keycloak-events.yml
spikersoft-blog-media-processor.yml spikersoft-lesson-video-processor.yml
spikersoft-book-management.yml spikersoft-metadata-extractor.yml
spikersoft-calendar-reminders.yml spikersoft-node-agent.yml
spikersoft-coderunner.yml spikersoft-notifications.yml
spikersoft-docker-monitor.yml spikersoft-scheduler.yml
spikersoft-file-movement.yml spikersoft-security-monitor.yml
spikersoft-game-events.yml spikersoft-security-scanner.yml
spikersoft-gameserver-init.yml spikersoft-system-remediation.yml
spikersoft-gameserver.yml spikersoft-upload-coordinator.yml
spikersoft-gpu-coordinator.yml
```
The pattern, verbatim from `spikersoft-coderunner.yml:75`:
```yaml
docker manifest create git.spikersoft.com/spikerj/spikersoft-coderunner:latest \
--amend git.spikersoft.com/spikerj/spikersoft-coderunner:amd64 \
--amend git.spikersoft.com/spikerj/spikersoft-coderunner:arm64v8
```
Zero workflows were converted to digest-pinned stitching. The ticket appears to have been closed without the change being made — or the change was reverted and nobody noticed, because nothing verifies it.
## Why this is worse now than when #548 was written
It **compounds #587/#590**. The confirmed root cause of #587 is that `cancel-in-progress: true` can cancel a run *after* the arch tags are pushed but *before* `create_manifest` stitches `:latest`. Because the arch tags are **mutable**, this leaves the registry in a state where:
- `:amd64` may be from commit **A** (pushed, then the run was cancelled), and
- `:arm64v8` may be from commit **B** (a later run that completed),
and the next successful `create_manifest` will happily `--amend` those two into a **`:latest` that is a mixed-commit chimera** — an amd64 image from one commit and an arm64 image from another, under one tag, with no error and no warning.
That is exactly the failure #548 predicted. The conditions to trigger it (cancelled runs mid-push) are **demonstrably occurring right now** — see #587, where 10 images are sitting with healthy arch tags and no `:latest`.
## Fix
Stitch from **immutable digests**, not mutable tags. Capture each arch push's digest as a job output and use `docker buildx imagetools create` against the digests:
```yaml
# in build_and_publish, per arch:
- id: push
run: |
digest=$(docker inspect --format='{{index .RepoDigests 0}}' "$IMAGE:$ARCH")
echo "digest=$digest" >> "$GITHUB_OUTPUT"
# in create_manifest:
- run: |
docker buildx imagetools create -t "$IMAGE:latest" \
"${{ needs.build_amd64.outputs.digest }}" \
"${{ needs.build_arm64.outputs.digest }}"
```
Best combined with the **#590** fix — make the arch push and the manifest stitch **atomic** (single job, or excluded from cancellation), so the tags and the index either both land or neither does. That closes the mixed-commit window and the missing-`:latest` window in one change.
## Suggested process note
#548 was closed with no verification that the change existed. A one-line CI guard would prevent a recurrence:
```bash
! grep -rq -- '--amend.*:\(amd64\|arm64v8\)' .gitea/workflows/
```
## Related
- **#548** (closed) — the original ticket; fix never landed.
- **#587** (open) — 10 images with healthy arch tags and no `:latest`; the cancelled-run window that makes this exploitable.
- **#590** (open) — `cancel-in-progress` is what creates that window.
Partial hardening landed: spikersoft-backend PR #317 (merged) gives the coderunner workflow the concurrency guard the other multi-arch workflows already carry (PR runs cancel, master runs queue so a started master build always finishes its manifest) and gates docker push off PR events. This narrows the interleaving window for that one workflow but is not the fix this ticket asks for — digest-pinned manifest stitching across all 23 workflows remains to be done, so leaving this open.
Partial hardening landed: spikersoft-backend PR #317 (merged) gives the coderunner workflow the `concurrency` guard the other multi-arch workflows already carry (PR runs cancel, master runs queue so a started master build always finishes its manifest) and gates `docker push` off PR events. This narrows the interleaving window for that one workflow but is **not** the fix this ticket asks for — digest-pinned manifest stitching across all 23 workflows remains to be done, so leaving this open.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Filed by: QA Team — referencing #548, which is CLOSED but whose fix never landed in a single workflow.
Summary
#548 ("Multi-arch
create_manifestamends MUTABLE:amd64/:arm64v8tags — out-of-order arch pushes can stitch a mixed-commit:latest") was closed on 2026-07-14. The described fix is not present in the repository. Every multi-arch workflow still uses the exact pattern the ticket flagged.Evidence
Audited every workflow in
.gitea/workflows/that callsdocker manifest create:The pattern, verbatim from
spikersoft-coderunner.yml:75:Zero workflows were converted to digest-pinned stitching. The ticket appears to have been closed without the change being made — or the change was reverted and nobody noticed, because nothing verifies it.
Why this is worse now than when #548 was written
It compounds #587/#590. The confirmed root cause of #587 is that
cancel-in-progress: truecan cancel a run after the arch tags are pushed but beforecreate_manifeststitches:latest. Because the arch tags are mutable, this leaves the registry in a state where::amd64may be from commit A (pushed, then the run was cancelled), and:arm64v8may be from commit B (a later run that completed),and the next successful
create_manifestwill happily--amendthose two into a:latestthat is a mixed-commit chimera — an amd64 image from one commit and an arm64 image from another, under one tag, with no error and no warning.That is exactly the failure #548 predicted. The conditions to trigger it (cancelled runs mid-push) are demonstrably occurring right now — see #587, where 10 images are sitting with healthy arch tags and no
:latest.Fix
Stitch from immutable digests, not mutable tags. Capture each arch push's digest as a job output and use
docker buildx imagetools createagainst the digests:Best combined with the #590 fix — make the arch push and the manifest stitch atomic (single job, or excluded from cancellation), so the tags and the index either both land or neither does. That closes the mixed-commit window and the missing-
:latestwindow in one change.Suggested process note
#548 was closed with no verification that the change existed. A one-line CI guard would prevent a recurrence:
Related
:latest; the cancelled-run window that makes this exploitable.cancel-in-progressis what creates that window.Partial hardening landed: spikersoft-backend PR #317 (merged) gives the coderunner workflow the
concurrencyguard the other multi-arch workflows already carry (PR runs cancel, master runs queue so a started master build always finishes its manifest) and gatesdocker pushoff PR events. This narrows the interleaving window for that one workflow but is not the fix this ticket asks for — digest-pinned manifest stitching across all 23 workflows remains to be done, so leaving this open.