Docker Publish as a job DAG with registry-atomic :latest¶
Release image publish used to be one serial GitHub Actions job that pushed each of backend, frontend, and worker with both :${IMAGE_TAG} and :latest, so a mid-run failure left Hub with a mixed floating-tag set and made it hard to see which image failed. We split .github/workflows/docker-publish.yml into prepare → matrix build (fail-fast: false) → promote-latest → hub-descriptions: builds push only the version tag; :latest is advanced with docker buildx imagetools create only after all shards succeed; Hub README updates soft-fail after promote; concurrency is per-tag with cancel-in-progress: false. Partial version tags on a failed run are accepted for now (candidate-tag promote deferred). Design detail: docs/research/docker-publish-job-split.md; tracked in #97.