ci(release): the release-integrity audit names the changesets a version commit did not consume - #21373
Merged
objectstack-fleet[bot] merged 4 commits intoOct 2, 2026
Conversation
… commit did not consume The unconsumed mode lists every pending .changeset/*.md in the version commit's tree, each with the commit that added it, and refuses a shallow clone. Seven self-test batteries pin it on throwaway repositories. Claude-Session: https://claude.ai/code/session_01UtnxvdiN376GF3sgXwAw4d Co-authored-by: Claude <noreply@anthropic.com>
…ersion commit did not consume In the run that queues the publish, the audit step runs release-pending-publish.mjs unconsumed and prints a warning plus a job summary section naming each changeset and the commit that added it. It never fails the step or holds the publish job. Battery 12 of release-verify-npm.mjs pins it on the step's real text. Claude-Session: https://claude.ai/code/session_01UtnxvdiN376GF3sgXwAw4d Co-authored-by: Claude <noreply@anthropic.com>
… and spell no command token in a comment Claude-Session: https://claude.ai/code/session_01UtnxvdiN376GF3sgXwAw4d Co-authored-by: Claude <noreply@anthropic.com>
…consumed-changeset-audit Claude-Session: https://claude.ai/code/session_01UtnxvdiN376GF3sgXwAw4d Co-authored-by: Claude <noreply@anthropic.com>
objectstack-fleet
Bot
deleted the
claude/issue-21361-unconsumed-changeset-audit
branch
October 2, 2026 09:46
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #21361
Clause-②: no
What this does
Triage's third direction, and only that one. When the
release-integrityaudit queues a publish, it now reports every pending.changeset/*.mdin the version commit's tree. These are the changesets the version commit did not consume. The report names each one with the commit that added it, so the gap shows up when the release is cut, not one release later.unconsumedmode inscripts/release-pending-publish.mjs. That script already finds the version commit (select) and already refuses a shallow clone. It gets seven new--self-testbatteries on throwaway repositories.id: auditstep of.github/workflows/release.yml. The call sits in the branch that queues the publish (pending == true, on a push or the repair dispatch), right after the "waiting for your approval" summary. There is no new job and no new gate.scripts/release-verify-npm.mjsalready runs the audit step's real text, taken fromrelease.yml, in a throwaway repository. Its fixture now carries one consumed changeset, one that landed behind the Version Packages PR, and aREADME.md. The version push must warn about exactly the one that landed behind, and must still queue the publish.The 17.6.0 instance (
748b240, #21270,.changeset/21110-scheduled-work-host-reason.md) is carried by the maintainer's #21362. That PR has since merged as9360df4138and put the dated correction intocontent/docs/releases/v17/17-6.mdx. This PR edits nocontent/docs/releases/**and noCHANGELOG.md.How the version commit is found, and what "unconsumed" means
The version commit is the one
selectalready names forpublishto check out. It is the newest first-parent commit at whichpackages/cli/package.json's version differs from its first parent's (findVersionCommit). The step passes that sha to the new mode."Pending" means whatever
changeset versionitself would read:.changeset/*.mdthat@changesets/read1.0.1 (the version pnpm-lock pins) does not skip;README.md(any case),AGENTS.md,CLAUDE.mdorGEMINI.md.Pre mode is read in both of the shapes it has been stored in:
.changeset/pre/, which is not top-level;.changeset/pre.json. An id listed there counts as consumed.The add commit comes from
@changesets/git's own lookup,git log --diff-filter=A --max-count=1, with--no-renamesadded and the walk started at the version commit. So the named commit is always the version commit or one of its ancestors. A changeset that lands after the version commit is in a different tree.A shallow clone is refused, never answered
--diff-filter=Acannot be answered in a shallow clone. Below the graft boundary, every older changeset reads as added by the boundary commit, which is a confident wrong commit. The mode refuses a shallow clone in the same wayselectdoes. Measured in this container's own shallow checkout:The job's checkout already uses
fetch-depth: 0, andselectwould refuse a shallow clone before this point anyway. If the report still cannot be measured, the script prints its own error annotation with the reason. The step then writes NOT MEASURED to the log and the job summary, and leaves the publish queued. The report never comes back empty without saying so.Level: a warning, never a refusal
The finding is a
::warning::plus a job-summary section. That is the level this job already uses for every finding that leaves a release incomplete without stopping it:This step uses
::error::only together withexit 1, for refusals. The report must not fail the step:publishneedsthis job, so a failure here would hold the npm publish, the tag and the image. That is why it reports and never refuses.It runs only on the event that queues the publish, because that is the run the approver opens. A later landing during the approval window, and every audit of an already published version, print nothing. Otherwise the same warning would appear on every push to main for a whole release cycle. Battery 12 pins this as well.
Real history (a full-history clone,
--is-shallow-repositoryfalse; the script at head27979b22f4)617f25f8a748b24072(#21270)8c87d26a5f11b5f20ae73ee2ddc×2c876a74267a1faf1a5c9d234c4024d521e532123fcca37e63370078a1bad8b8e7d2cc67f47d1ae89e24c1b91e4e7e0a6dd9pre.json)bd191338e3cfd9f0b1748b240) for 17.6.0.git show --name-statusshows each set deleted by the NEXT version commit: 17.4.0's by8c87d26a5, 17.3.0's by7e6337007. So those entries were published one release late, under 17.5.0 and 17.4.0. The clean controls are 17.2.0, 17.1.0 and 17.0.0. See the acceptance notes.Tests
node scripts/release-pending-publish.mjs --self-test:✓ release-pending-publish self-test: 68 cases across 20 batteries pass., up from 13 batteries. The seven new batteries and their floors:changeset versionreads (2);pre.json(2);The battery-count floor goes from 13 to 20.
node scripts/release-verify-npm.mjs --self-test:OK release-verify-npm self-test: 96 cases pass across 13 batteries. Battery 12's floor goes from 14 to 17, with the three new cases.scripts/ablation-replace.mjs, the call inrelease.ymlwas replaced withif ! true; then:c975b6f27bbftodd45fa2c88c1;release-verify-npm --self-testthen exited 1, with the two positive battery-12 pins red ("the version push warns of the changeset its version commit did not consume…" and "…the job summary carries the section");c975b6f27bbf) andgit diff HEADbeing empty.27979b22f4,node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands(no paths, change set from the merge base) derives 51 commands. All 51 were run and exited 0, and--ranreconciles:51 derived, 51 run, 0 NOT-MEASURED, 0 UNRUN(a derived zero, since every row recorded its exit code). That set includescheck:self-test-wired(every one of the 232 script(s) CI runs that ship a --self-test has that self-test run by CI),check-self-test-workflow-commands(no self-test prints a line the runner would parse as a workflow command),check:nul-bytesandcheck:bash32-floor. Lint: CI owns the repo-wide run. The narrowed runeslint --no-inline-config --format jsonon the two changed scripts reported 2 files, 0 errors and 0 warnings. The population comes fromeslint.config.mjs: its**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}object coversscripts/*.mjs. That config never enables type-aware linting (noparserOptions.project), so this diff cannot move a verdict on any untouched file. No TypeScript package is touched, so notypecheckis owed.Acceptance notes
selectsucceeded. The mode's own refusals are pinned in its self-test.changeset versionskips on purpose, meaning one whose every package is inignoreor is private withprivatePackages.version: false..changeset/config.jsonconfigures neither today. This is noted in the script's docblock.skip-changeset): the diff touches.github/workflows/and two repository-rootscripts/*.mjs, and neither is in any package'sfiles[].Generated by Claude Code