Repository navigation
ci(release): fall back to generated notes, and add the changelog rotation script - #237
Conversation
…tion script The release workflow ships the CHANGELOG section matching the tag's base version, but nothing at the train cut ever rotates `[Unreleased]` into that section, so every tag from ant-cli-v0.3.7 to ant-cli-v0.3.12-rc.1 shipped the "No changelog entry was recorded" placeholder. - scripts/rotate-changelog.sh X.Y.Z moves `[Unreleased]` into `## [X.Y.Z] - <date>`, for the cut tooling to run on the rc branch. It is idempotent across rc re-cuts and never fails on an empty `[Unreleased]`. - With no matching section, the workflow now publishes GitHub's generated list of PRs merged since the previous stable ant-cli tag instead of the placeholder, still with the `::warning::` annotation. The placeholder is kept only for when that API call fails. - CLAUDE.md says where entries go and who writes the versioned heading. Refs V2-1107. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sees it CLAUDE.md imports it with @AGENTS.md, so Claude Code still loads it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaidSafe-Hermes
left a comment
There was a problem hiding this comment.
REQUEST_CHANGES
The missing-section fallback can publish GitHub's API error response as the release notes instead of using the placeholder. The inline comment gives the failure path and minimum fix.
Coverage: 2/2 review seats: sol-1 (gpt-6.1-sol, xhigh) and glm-1 (z-ai/glm-5.3). The seats disagreed; I verified the error-handling defect in the reviewed workflow and GitHub CLI behavior.
Verification: bash -n scripts/rotate-changelog.sh passed. A local rotation and idempotent re-run passed. On this PR head, all listed checks passed except Security Audit. Cargo.lock is unchanged from the PR base, so that audit failure is inherited, not introduced by this PR; update the branch and rerun checks. No end-to-end release-tag run was performed.
| if [ -n "$previous_tag" ]; then | ||
| generated=$(gh api "repos/${GITHUB_REPOSITORY}/releases/generate-notes" \ | ||
| -f tag_name="$tag" -f previous_tag_name="$previous_tag" \ | ||
| --jq .body || true) |
There was a problem hiding this comment.
P2 — failed API requests can be published as release notes. When gh api receives a JSON HTTP error response, --jq .body does not filter it; gh writes the raw error JSON to stdout and exits non-zero. The trailing || true suppresses that exit but preserves stdout in generated. The non-empty check at line 402 then selects this branch and publishes the error payload instead of the placeholder. Capture success explicitly and set generated='' on failure (for example, if generated=$(gh api ...); then :; else generated=''; fi). Add a regression test with a mocked gh that emits a JSON error body and exits non-zero; assert the placeholder path is used.
|
Review posted on the exact head I also noted one non-blocking hardening point: write the rotated changelog atomically so an interrupted copy cannot leave it truncated. For the |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…iting it rotate-changelog.sh wrote the rotated text back with `cat "$tmp" > "$changelog"`, so an interrupted run could leave CHANGELOG.md truncated. It now writes a temporary file next to the changelog, gives it the changelog's mode with `cp -p`, and renames it over the original. An interruption leaves the old file whole. scripts/test-rotate-changelog.sh covers: - the rotation, including an rc suffix; - an idempotent re-run; - exit 2 when both sections have entries; - an empty hand-written heading; - an empty [Unreleased]; - bad input; - the rename and the kept mode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On an HTTP error `gh api ... --jq .body` skips the filter, prints the raw JSON error body and exits non-zero. The step's `|| true` kept that body in `generated`, which then passed the non-empty check and would have been published as the release notes instead of the placeholder. Its output now counts only when the call succeeds. The notes logic moves from the workflow step into scripts/release-notes.sh, unchanged otherwise, so it can be tested: the changelog section, else the generated notes since the previous stable tag, else the placeholder. scripts/test-release-notes.sh runs it against fake `git` and `gh`, including a failed call that prints a JSON error body. A new `Release scripts` job in ci.yml runs that test and the rotation test on every PR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CLAUDE.md's guide moves into AGENTS.md unchanged, next to the changelog rule, so agents that read only AGENTS.md (Codex, OpenCode and others) get the whole guide and not just that one rule. CLAUDE.md is now `@AGENTS.md`, which Claude Code expands, so Claude still loads the same text and there is a single copy to keep current. The Claude Code E2E skill section is labelled as Claude-only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Addressed in 94b01ec. The branch is updated with P2: failed API requests can be published as release notes (ant-cli-release.yml:400). You're right. I reproduced it with a 404:
With the step's original Non-blocking: write the rotated changelog atomically. Done.
Against the old
Verification (Linux, dev2, bash 5.2):
CI on 94b01ec: all jobs pass except Security Audit. It fails only on RUSTSEC-2026-0387 (crypto-bigint 0.5.5), which is inherited from |
MaidSafe-Hermes
left a comment
There was a problem hiding this comment.
Focused re-review of the prior blockers on 94b01ec046cf974c47916c3bccf69c797be73104 (not a fresh full-panel review).
Coverage and adjudication: 2/2 seats returned: sol-1 (gpt-6.1-sol xhigh) was uncertain pending evidence on the audit failure; glm-1 (z-ai/glm-5.3) approved but asked to verify the tag output and API permission. I checked those points against the exact workflow and GitHub's REST permission requirements: the meta step writes tag to $GITHUB_OUTPUT, and this workflow grants contents: write, which the generate-release-notes endpoint requires. The Security Audit log identifies crypto-bigint 0.5.5 (RUSTSEC-2026-0387); the PR-base, PR-head and current-main Cargo.lock hashes are identical. The audit objection is therefore resolved as inherited, not PR-introduced. The seats differed initially; I resolved their stated uncertainties with this direct evidence rather than treating that as consensus.
The release-notes fix now discards captured output when gh api exits non-zero, and its regression test supplies a JSON error body and checks that placeholder notes are used. The changelog script stages beside the source and renames into place; tests cover repeat-run, failure-without-change and mode preservation. CLAUDE.md imports the full shared AGENTS.md guide. I found no remaining blocker in these changes.
Verification: bash -n passed; release-notes tests passed 10/10; changelog-rotation tests passed 10/10; the exact-head Release scripts CI job passed. Required checks linear-link and pr-template passed. The non-required Security Audit remains red for the inherited advisory above. No end-to-end tag release was run. This approval is a code-review verdict, not merge authorization.
Every
antrelease since 0.3.6 has shipped with empty notes. #183 made the release workflow ship theCHANGELOG.mdsection matching the tag's base version. Since #187 (V2-1142), a missing section publishes a placeholder instead of failing. But nothing at the train cut ever rotates[Unreleased]into## [X.Y.Z]. So all nine tags fromant-cli-v0.3.7to today'sant-cli-v0.3.12-rc.1(five of them stable) shipped "No changelog entry was recorded for …".This PR fixes the process in two places, and makes
AGENTS.mdthe guide for every coding agent. The content backfill for 0.3.7–0.3.12 is a separate PR.1.
scripts/rotate-changelog.sh X.Y.Z [date] [changelog]: the rotation step the cut tooling never had. For the RM: please wire it into the rc.1 cut, on the rc branch, before tagging. It moves the[Unreleased]entries unchanged under a new## [X.Y.Z] - <date>and keeps an empty[Unreleased]heading above it.X.Y.Z-rc.Nand drops the suffix.[X.Y.Z]already filled and does nothing.## [X.Y.Z]heading is replaced, not duplicated.[Unreleased]. It warns instead, and the workflow fallback below covers that release.[X.Y.Z]and[Unreleased]have entries (entries landed after the rotation), so a human places them. Per V2-1142, the cut tooling should report that, not abort on it.2. Workflow fallback. With no matching section, the notes are now GitHub's generated list of PRs merged since the previous stable
ant-cli-v*tag (releases/generate-notes). Before, they were the placeholder. A missed rotation now ships the right list of changes rather than none. The::warning::annotation stays, and the placeholder remains only as the last resort: no previous stable tag, an empty result, or a failed API call. A failed call's output is discarded. On an HTTP errorgh api --jqprints the raw JSON error body and exits non-zero, and that body must never be published as the notes. The previous tag is the highest stable tag below the base version, soX.Y.Z-rc.NandX.Y.Zboth list the whole train. Nothing about the success path changes. The logic lives inscripts/release-notes.sh, which the step calls, soscripts/test-release-notes.shcan test it against fakeghandgit. A newRelease scriptsjob inci.ymlruns that test andscripts/test-rotate-changelog.shon every PR.3.
AGENTS.mdis the agent guide, andCLAUDE.mdimports it.CLAUDE.md's guide moves into a new rootAGENTS.mdunchanged. The Claude Code E2E skill section is labelled Claude-only. The changelog rule is added: user-facing PRs add entries under[Unreleased], and only the cut writes versioned headings.CLAUDE.mdis now just@AGENTS.md, which Claude Code expands, so Claude loads the same text and there is one copy to keep current. Agents that read onlyAGENTS.md, such as Codex and OpenCode, now get the whole guide.This settles the open question an earlier version of this PR asked reviewers, about where the changelog rule should live. The answer is option B, done here rather than in a follow-up: an
AGENTS.mdholding only this rule would have given those agents one rule and none of the build, architecture or PR guidance. saorsa-transport, saorsa-core and evmlib already keep a fullAGENTS.md.Linear issue
Closes V2-1107
Risk tier
Compatibility
scripts/release-notes.sh), the rotation script, their tests and CI job, and the agent guide (AGENTS.md, withCLAUDE.mdimporting it) change.Semver impact
Test evidence
Review fixes (2026-10-27), on
mainat5e01699:scripts/test-release-notes.sh, 10 checks, andscripts/test-rotate-changelog.sh, 10 checks, pass on Linux (dev2, bash 5.2) and in the newRelease scriptsCI job on94b01ec(10 passed, 0 failed each).|| truerestored, the failed-API test fails, and the published notes are the prefix plus{"message":"Not Found","documentation_url":…,"status":"404"}. All 10 pass on the fix.rotate-changelog.sh(cat "$tmp" > "$changelog"), the rename test fails, because the inode is unchanged. All 10 pass on the fix.release-notes.shgives byte-identical notes to the old step's extraction for0.3.6,0.3.5and0.3.6-rc.1onmain'sCHANGELOG.md.shellcheckis clean on all four scripts.actionlintwith shellcheck finds nothing inci.ymlor in the changed step. Its 20 info and style notes on other steps ofant-cli-release.ymlare the same 20 it reports onmain's copy.AGENTS.mdholds every line ofmain'sCLAUDE.mdguide in order, checked line by line. The only change is the E2E heading's "(Claude Code)" label.Earlier evidence:
rotate-changelog.shagainst copies ofmain'sCHANGELOG.md:0.3.12-rc.1inserts only## [0.3.12] - 2026-10-22under## [Unreleased]. The diff is exactly the two added lines.[Unreleased]after the rotation makes it exit 2 with instructions.[Unreleased]makes it warn and exit 0.## [0.3.7]heading is replaced rather than duplicated.0.3.1does not match an existing## [0.3.12].0.3.12-rc.1 → ant-cli-v0.3.11,0.3.11 → ant-cli-v0.3.10,0.3.7 → ant-cli-v0.3.6and0.3.0 → ant-cli-v0.2.11.0.1.1has no previous stable, so it gets the placeholder.gh api …/releases/generate-notesforant-cli-v0.3.11..ant-cli-v0.3.12-rc.1returns feat(read): spread hot-record reads across the close group, and back off briefly from nodes that decline #225, feat(upload, manifest): report the uploaded file's SHA-256 and record it on manifest entries (V2-1564) #229, feat(browser): export manifests and recover pointer payments #230, chore(release): promote rc-2026.10.2 #231 and chore(deps): move rustls to 0.23.46 for four RustSec advisories #232.ant-cli-v*tag that lacks its section.New dependency
none
ADR
n/a
Mitigation / rollback
Revert. The workflow goes back to placeholder notes, and the script is unused until the cut tooling calls it.
🤖 Generated with Claude Code