Symptom
Four entries on https://dev.dotcms.com/docs/reference/releases/product-versions/changelogs render with a title, availability date and docker tag but no release notes: 26.08.28-01, 26.08.31-01, 26.08.31-02, 26.09.02-01.
The GitHub releases for all four have correct bodies (2.2k–6.8k chars). The site does not.
How they got that way
Generate release notes with Claude failed on each of these runs, so Update release description was skipped and the GitHub release body stayed empty.
Changelog Site Publish ran anyway and succeeded, publishing an entry with empty releaseNotes. Log signature: Fetched release notes (1 lines). — versus (20 lines). on the healthy 26.09.03-01 run.
- The 2026-09-03 04:00 backfill repaired all four GitHub release bodies. The site was never touched.
Step 2 happens because the non-empty guard doesn't catch it:
gh release view "$RELEASE_TAG" --repo "$REPO" --json body --jq .body > /tmp/site-release-notes.md
if [ ! -s /tmp/site-release-notes.md ]; then # cicd_comp_changelog-site-publish-phase.yml:94
--jq .body on an empty body emits a bare newline, so the file is 1 byte, not 0, and -s passes. Reproduced against v26.08.19-03: bytes=1 lines=1 → -s GUARD PASSES.
Step 3 happens because cicd_ai-release-notes-backfill.yml has exactly one job, Generate Notes. It writes the GitHub release body and stops. There is no path from a backfill to the site, so once an entry is published empty it stays empty forever.
Scope of this issue
Close the backfill gap — make the backfill publish to the site as well, so a repaired GitHub release body always reaches dev.dotcms.com. This also makes the backfill the general repair tool for any site entry that is empty, stale, or wrong.
The -s guard is a separate (and now less consequential) defect — tracked separately, not fixed here.
Two things the wiring has to get right
Docker image. The release pipeline gets docker_tags from its deployment phase; a backfill has no deployment to read from and must recover them from Docker Hub. Matching "tag contains _" is wrong — 26.08.28-01_tainted and 26.08.31-01_tainted both exist and sort ahead of the real tag. Match a 7-hex sha suffix. Verified against all four original runs:
| Version |
Recovered |
docker_tags the original run passed |
| 26.08.28-01 |
26.08.28-01_d5ab3fd |
dotcms/dotcms:26.08.28-01_d5ab3fd ... ✅ |
| 26.08.31-01 |
26.08.31-01_a6d1271 |
dotcms/dotcms:26.08.31-01_a6d1271 ... ✅ |
| 26.08.31-02 |
26.08.31-02_b1a33f2 |
dotcms/dotcms:26.08.31-02_b1a33f2 ... ✅ |
| 26.09.02-01 |
26.09.02-01_2460a36 |
dotcms/dotcms:26.09.02-01_2460a36 ... ✅ |
Availability date. The publish phase hardcodes --released-date "$(date -u +%F)", and publisher.py:154 writes that straight through on the update path. Backfilling 26.08.28-01 today would relabel it "Available: Sep 3, 2026". The phase needs an optional released_date passthrough; the backfill sources it from the release's published_at.
Acceptance criteria
- Dispatching the backfill for a current-track tag regenerates the GitHub release body and republishes the site entry.
- The republished entry keeps its original availability date and its sha-suffixed docker tag.
- CLI / LTS tags skip the site publish, matching the release pipeline's exclusion.
- A backfill whose site publish fails reports failure (the release pipeline's
allow_failure is deliberate there; a backfill has no software release to protect).
Symptom
Four entries on https://dev.dotcms.com/docs/reference/releases/product-versions/changelogs render with a title, availability date and docker tag but no release notes:
26.08.28-01,26.08.31-01,26.08.31-02,26.09.02-01.The GitHub releases for all four have correct bodies (2.2k–6.8k chars). The site does not.
How they got that way
Generate release notes with Claudefailed on each of these runs, soUpdate release descriptionwas skipped and the GitHub release body stayed empty.Changelog Site Publishran anyway and succeeded, publishing an entry with emptyreleaseNotes. Log signature:Fetched release notes (1 lines).— versus(20 lines).on the healthy26.09.03-01run.Step 2 happens because the non-empty guard doesn't catch it:
--jq .bodyon an empty body emits a bare newline, so the file is 1 byte, not 0, and-spasses. Reproduced againstv26.08.19-03:bytes=1 lines=1 → -s GUARD PASSES.Step 3 happens because
cicd_ai-release-notes-backfill.ymlhas exactly one job,Generate Notes. It writes the GitHub release body and stops. There is no path from a backfill to the site, so once an entry is published empty it stays empty forever.Scope of this issue
Close the backfill gap — make the backfill publish to the site as well, so a repaired GitHub release body always reaches dev.dotcms.com. This also makes the backfill the general repair tool for any site entry that is empty, stale, or wrong.
The
-sguard is a separate (and now less consequential) defect — tracked separately, not fixed here.Two things the wiring has to get right
Docker image. The release pipeline gets
docker_tagsfrom its deployment phase; a backfill has no deployment to read from and must recover them from Docker Hub. Matching "tag contains_" is wrong —26.08.28-01_taintedand26.08.31-01_taintedboth exist and sort ahead of the real tag. Match a 7-hex sha suffix. Verified against all four original runs:docker_tagsthe original run passed26.08.28-01_d5ab3fddotcms/dotcms:26.08.28-01_d5ab3fd ...✅26.08.31-01_a6d1271dotcms/dotcms:26.08.31-01_a6d1271 ...✅26.08.31-02_b1a33f2dotcms/dotcms:26.08.31-02_b1a33f2 ...✅26.09.02-01_2460a36dotcms/dotcms:26.09.02-01_2460a36 ...✅Availability date. The publish phase hardcodes
--released-date "$(date -u +%F)", andpublisher.py:154writes that straight through on the update path. Backfilling26.08.28-01today would relabel it "Available: Sep 3, 2026". The phase needs an optionalreleased_datepassthrough; the backfill sources it from the release'spublished_at.Acceptance criteria
allow_failureis deliberate there; a backfill has no software release to protect).