Skip to content

fix(release-cut): only count changesets that declare a bump (#1068) - #1071

Merged
bdelanghe merged 1 commit into
mainfrom
claude/whats-next-6s9myh
Aug 20, 2026
Merged

fix(release-cut): only count changesets that declare a bump (#1068)#1071
bdelanghe merged 1 commit into
mainfrom
claude/whats-next-6s9myh

Conversation

@bdelanghe

@bdelanghe bdelanghe commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Summary

The first real dispatch of release-cut (run 32381357163) blocked — on the changeset added by the PR that shipped it:

::error::unconsumed changesets present — merge the Version Packages PR first:
gh1068-release-cut.md

The dry-run did its job: guards ran, nothing was created, steps 7–9 skipped. But guard 3 was wrong.

It counted every .changeset/*.md. An empty changeset (--- immediately followed by ---) is this repo's convention for a change with no package codepath — gh1062-auto-repin-single-pr, gh280-test-gpgsign, gh1039-pin-direct-deps, gh1056-org-repair-allow all use it — and carries no version intent. So by the guard's own stated rationale ("the manifest is BEHIND the intents"), it was over-firing: nothing was behind anything.

Left as-is, every release following a CI-only change would have needed a no-op Version Packages PR first, purely to clear a guard with nothing to report.

The fix

Guard 3 now parses each changeset's frontmatter and counts it only when a package bump is declared:

  • a changeset with "@bounded-systems/prx": patchblocks (unchanged)
  • an empty changeset → passes
  • both present → blocks, and the error names only the bump, so the message points at what actually needs consuming

The guard's real purpose — catching an unmerged Version Packages PR — is untouched. Guard 1 (expect-version vs the manifest) independently catches the case where a newer version has been cut.

Verified by reproducing the failure, not by reasoning about it

Run against real repo state, same working tree, both guard versions:

guard result
old (on main) exit 1 — blocks on gh1068-release-cut.md
new (this branch) exit 0 — emits version=1.0.0, tag=v1.0.0

Tests go 8 → 10, still extracting the workflow's real guard script from the YAML at test time so they cannot drift from what ships. New cases: an empty changeset does not block; a bump still blocks when an empty one sits beside it (and the empty one is not named in the error). 6 of the 10 still assert a guard blocks.

Also clean: bunx tsc --noEmit, bunx biome check, YAML parses.

Notes

  • This PR's own changeset is empty — the exact case the fix is about, so it is also a live end-to-end check of the new behaviour.
  • Unrelated but worth recording: a stale local v1.0.0 tag from the earlier refused push would have tripped guard 2 locally. Deleted; it never reached the remote.
  • Still outstanding for an actual cut: the [settings] grant letting this repo's Actions actor create v* tags (mint#18). The dry-run cannot detect that — it never pushes.

Refs #1068

Checklist

  • Independent PR — no bundled or speculative changes
  • Changed codepaths verified — old-vs-new reproduction against real state; 10 guard tests, 6 asserting a block
  • Root cause identified — guard counted intents that declare no bump, contradicting its own rationale; not papered over by relaxing the guard wholesale
  • No duplication
  • No unrelated changes
  • Generated artifacts regenerated — n/a; empty changeset per the CI-only convention
  • CI is green — 15 checks complete on 6d35f9d, 0 failures; mergeable_state: clean; coverage unchanged at 87%

The first real dispatch of release-cut blocked on its own changeset. Guard 3
counted every `.changeset/*.md`, but an EMPTY changeset ("---" immediately
followed by "---") is this repo's convention for a change with no package
codepath — gh1062, gh280, gh1039 and gh1056 all use it — and carries no version
intent. So by the guard's own stated rationale ("the manifest is BEHIND the
intents"), it was over-firing: nothing was behind anything.

Left as-is this would have forced a no-op Version Packages PR before every
release that followed a CI-only change, to clear a guard that had nothing to
report.

Guard 3 now parses each changeset's frontmatter and counts it only when a
package bump is declared. A bump still blocks; an empty changeset beside it
still blocks and is not named in the error, so the message points only at what
actually needs consuming.

Verified by reproducing the original failure rather than reasoning about it:
run against real repo state, the old guard exits 1 naming gh1068-release-cut.md
and the new guard exits 0 emitting version=1.0.0 / tag=v1.0.0. The guard tests
gain the empty-changeset case, the mixed case, and keep the bump case — 10 pass,
still extracted from the workflow YAML at test time so they cannot drift.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WiXADb8QTDCvPCoKLvGdqL
@github-code-quality

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / unit

The overall line coverage in commit 6d35f9d in the claude/whats-next-6s... branch remains at 87%, unchanged from commit c815fe4 in the main branch.

@bdelanghe
bdelanghe marked this pull request as ready for review August 20, 2026 15:28
@bdelanghe
bdelanghe requested a review from a team as a code owner August 20, 2026 15:28
@bdelanghe
bdelanghe merged commit d1b6030 into main Aug 20, 2026
15 checks passed
@bdelanghe
bdelanghe deleted the claude/whats-next-6s9myh branch August 20, 2026 15:29
@bounded-systems-front-desk bounded-systems-front-desk Bot moved this from Todo to Done in Front Desk Aug 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants