Skip to content

release: a Version Packages PR that merges behind main ships every changeset landed since its last refresh without a CHANGELOG entry (17.5.0: 8 changesets, 17.6.0: 1) #21361

Description

@hotlong

Summary

The Version Packages PR is regenerated on a schedule and on demand (#11233), and it cannot be refreshed while it sits in the merge queue. When it lands, the merge queue builds it on top of the current main. So the version commit's tree carries every commit that landed after the PR's last refresh. The PR's own diff deletes, and turns into CHANGELOG entries, only the changesets that existed at that refresh.

The code of the later commits is therefore published under the new version, but their changesets stay in .changeset/. No CHANGELOG entry for that version names them. They show up one release later, under the wrong version.

#20625 (#20613) made the publish ship the version commit and nothing after it. This is the window before the version commit, which #20625 does not cover.

Measured twice

release version commit commits in its tree whose changesets it did not consume where they surfaced
17.5.0 8c87d26a (#17076) seven commits, eight changesets: f11b5f2 #20568, e73ee2d #20567 (two changesets), c876a74 #20504, 7a1faf1 #20579, c9d234c #20577, 24d521e #20572, 2123fcc #20576 listed again in 17.6.0's CHANGELOG. Two are breaking (e73ee2d, c876a74). The 17.5.0 notes carry a dated correction for them.
17.6.0 617f25f8 (#20639) 748b240 #21270 (ScheduledWorkPolicy.hostDisabledReason, a minor for three packages) .changeset/21110-scheduled-work-host-reason.md is still pending on main. No 17.6.0 CHANGELOG.md in the published tarballs mentions it (also observed in objectstack-ai/hotcrm#1982).

How to check either one:

git merge-base --is-ancestor <commit> <version commit>   # → true
git show --name-status <version commit> | grep '^D.*\.changeset/<file>'   # → no line

Why it matters

The generated CHANGELOG is the record an upgrading agent or operator reads, and the release notes are compiled from it. A breaking change that ships without its entry is invisible exactly where people look for it. For 17.5.0 that was the RealtimeEventType narrowing and the forced-Turso-replica refusal.

Directions (not decided here)

  • A merge-group guard on the Version Packages PR. Refuse the merge when the merge-group tree still contains a non-README .changeset/*.md that the PR does not delete, so the PR must be refreshed first. The guard needs a re-run path that does not deadlock with "cannot refresh while queued".
  • Regenerate inside the merge group. Run changeset version on the merge-group tree, so the version commit consumes everything in it.
  • At minimum, have the release-integrity audit report unconsumed changesets whose commits are ancestors of the published version commit, so the gap is seen at publish time rather than found later.

Activity

  1. objectstack-fleet commented on Oct 2, 2026

    @objectstack-fleet
    Contributor

    Triage: first grade — priority:p1 · domain:spec · area:devpath · pm:queue. Ruling: the audit reports the unconsumed changesets now; a merge-group guard waits for the maintainer. The filer's bug · tooling stand

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-02T07:53Z. ⛔ Not a claim, ⛔ not a dispatch.

    Why p1. It is the release-priority rule. Two releases in a row published code, including breaking changes, with no CHANGELOG entry for its version, and the release notes are compiled from that record.

    Routing. .github/workflows/release.yml and the release scripts are domain:spec, under the anchoring exception.

    Ruling: the third direction, and only that, in this card (triage's; overturnable by the maintainer).

    • The release-integrity audit reports, at publish time, every non-README .changeset/*.md whose introducing commit is an ancestor of the version commit and which the version commit did not consume. It names each changeset and its commit, so the gap is seen when the release is cut, not one release later.
    • ⛔ Not the merge-group guard. New gates default to no, and it carries the deadlock the card names. It goes to the maintainer as a decision only if the report proves not enough.
    • ⛔ Not regeneration inside the merge group. A merge-group build cannot commit back to the tree it tests.
    • The known instance: 17.6.0's CHANGELOG gets a dated correction for 748b240 (feat(types,automation): a host's per-kernel scheduled-work OFF reports its own reason #21270, .changeset/21110-scheduled-work-host-reason.md), in the shape 17.5.0's notes carry. ⛔ Release acts (publishing, tags, Releases) stay the maintainer's.

    Pins: the audit, run against the 17.5.0 and 17.6.0 version commits, names exactly the measured sets (8, and 1). A version commit with nothing unconsumed reports none (the control).


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 2, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 1
    Session: session_01UtnxvdiN376GF3sgXwAw4d
    Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-21361-unconsumed-changeset-audit
    Worktree: objectstack-issue-21361
    Domain: domain:spec
    Seat: domain:spec#1 (seat post #6017)
    File surface: the release-integrity job's audit in .github/workflows/release.yml (the id: audit step, about :835 on origin/main 5fd4855a9a), and one release script holding the logic with its --self-test battery. That script is either an existing reader such as scripts/release-verify-npm.mjs or a new read-only script beside it, wired into an existing step. Per triage 5947729032's ruling (the third direction only): at publish time, report every non-README .changeset/*.md whose introducing commit is an ancestor of the version commit and which the version commit did not consume, naming each changeset and its commit. ⛔ No merge-group guard, no regeneration inside the merge group, and no new gate. ⛔ No content/docs/releases/** or CHANGELOG.md edit: the known 17.6.0 instance (748b240, #21270) is carried by the maintainer's PR #21362, which already writes that dated correction into the 17.6.0 notes. ⛔ Not scripts/release-github-releases.mjs (PR #21358). No changeset (skip-changeset). Stop on breach; explain in the report.
    Container & model: M, mode:subagent, model: opus (default judgment tier; release tooling, so the seat checks the diff at ACCEPT and no isolated contract review is owed)
    Clause-②: no
    Thread-read: 5947729032
    Serial constraints cleared: at 2026-10-02T08:00Z, no open PR touches .github/workflows/release.yml. PR #21358 (#21313, this seat, queued) holds only scripts/release-github-releases.mjs. #21313's claim names release.yml only for the guard it did not build, which is now #21359 (priority:p3, not in flight). PR #21362 (the maintainer's) holds content/docs/releases/v17/17-6.mdx and index.mdx, which this claim does not touch. ⛔ Release acts stay the maintainer's (AGENTS.md Prime Directive 15). This claim publishes, tags and dispatches nothing.

  3. objectstack-fleet commented on Oct 2, 2026

    @objectstack-fleet
    Contributor

    os-dev-report
    {
    "issue": 21361,
    "status": "done",
    "branch": "claude/issue-21361-unconsumed-changeset-audit",
    "pr": "#21373",
    "session": "session_01UtnxvdiN376GF3sgXwAw4d (subagent, the parent id; the newest and only Claim: on #21361, comment 5947821007, names this branch)",
    "premise_still_valid": true,
    "summary": "Triage's third direction only. scripts/release-pending-publish.mjs gains an unconsumed mode. For the version commit select already names, it lists every pending top-level .changeset/.md in that commit's tree, using @changesets/read 1.0.1's own filter, so README any case, AGENTS/CLAUDE/GEMINI.md and dotfiles are skipped. It names each one with the commit that added it, via @changesets/git's own lookup (log --diff-filter=A --max-count=1, plus --no-renames) walked from the version commit, so that commit is always the version commit or one of its ancestors. It treats pre mode as consumed in both stored shapes: the 3.x pre/ move, and ids listed in a 2.x pre.json. It refuses a shallow clone. release.yml's id: audit step runs it once, in the pending branch (the push or repair dispatch that queues the publish), after the approval summary. It reports a ::warning:: annotation plus a job-summary section, and never fails the step or holds publish. If it cannot measure, the script's own error annotation is followed by a NOT MEASURED line in the log and summary, and the publish stays queued. Battery 12 of release-verify-npm.mjs, which runs the step's real text, now pins the warning on the version push and its absence on later landings. The 17.6.0 instance is carried by the maintainer's #21362, which has since merged as 9360df4 and was merged into this branch. Real history at head 27979b2 matches triage's pins exactly: 17.5.0 gives 8 from 7 commits, 17.6.0 gives 1 (748b240). The dispatch's suggested control, 17.4.0, is FALSIFIED: it left 13 unconsumed (17.3.0 left 4). The clean controls are 17.2.0 e7d2cc6, 17.1.0 and 17.0.0.",
    "tests": "node scripts/release-pending-publish.mjs --self-test -> '✓ release-pending-publish self-test: 68 cases across 20 batteries pass.' It has 7 new batteries, and the battery floor goes from 13 to 20. node scripts/release-verify-npm.mjs --self-test (under os-verify-lock, VERDICT command-exit 0) -> 'OK release-verify-npm self-test: 96 cases pass across 13 batteries'. Battery 12's floor goes from 14 to 17. ABLATION, through scripts/ablation-replace.mjs with fix and pin committed first: the release.yml call was replaced by if ! true; then. The anchor went from x1 to x0 and the blob from c975b6f27bbf to dd45fa2c88c1. release-verify-npm --self-test then exited 1, with both positive battery-12 pins red. The restore is proven: blob == HEAD c975b6f27bbf and git diff HEAD is empty. No build or dist is involved: these are plain node scripts. Real history, on a full-history blobless clone (is-shallow false), script at head 27979b2. Pending/unconsumed/commits per version: 617f25f 1/1/1, 8c87d26 8/8/7, 7e63370 13/13/13, 8a1bad8 4/4/3, e7d2cc6 0/0/0, 47d1ae8 0/0/0, 24c1b91 0/0/0. The rc commits read pre.json as consumed: e7e0a6d 1704 pending with 1704 recorded, 0 unconsumed; bd19133 1277 with 1269 recorded, 8; 3cfd9f0 862 with 860 recorded, 2. In this container's own shallow checkout, the mode refuses with exit 1 and a stated reason. Gates: dispatch-gates --commands (no paths, merge base 9360df4, head 27979b2) derives 51 commands; all 51 ran with exit 0, and --ran gives '51 derived, 51 run, 0 NOT-MEASURED, 0 UNRUN' (a derived zero). Lint, narrowed: eslint --no-inline-config --format json on the 2 changed .mjs reports 2 files, 0 errors, 0 warnings. The population is eslint.config.mjs's **/
    .{...,mjs,...} object, and the config has no parserOptions.project (no type-aware linting), so untouched files' verdicts cannot move. NOT MEASURED: the step's NOT MEASURED fallback branch has no running pin, and no ablation moved the call out of the pending branch.",
    "mcp_calls": "0",
    "api_writes": "3 relay dispatches (POST /repos/objectstack-ai/objectstack/dispatches, executed as objectstack-fleet[bot]): (1) pr_create -> POST /repos/objectstack-ai/objectstack/pulls (draft #21373, run 36985601387, read-back 9968 bytes identical); (2) label-write -> POST /repos//issues/21373/labels (skip-changeset) + POST /repos//issues/21373/assignees (os-sales), run 36985669596, read back as matching; (3) this os-dev-report comment -> POST /repos//issues/21361/comments. git push is not a REST write.",
    "files_changed": [
    ".github/workflows/release.yml",
    "scripts/release-pending-publish.mjs",
    "scripts/release-verify-npm.mjs"
    ],
    "line_budget": "+412 / -5 = 417 changed lines across 3 files, against merge base 9360df4; below the 5000 human-merge threshold. No skills/** or ledger budget applies.",
    "gates": [
    "node scripts/check-aggregator-roster.mjs :: exit 0",
    "node scripts/check-aggregator-roster.mjs --self-test :: exit 0",
    "node scripts/check-ci-filter-parity.mjs :: exit 0",
    "node scripts/check-closing-keyword-parity.mjs :: exit 0",
    "node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0",
    "node scripts/check-comment-mask-corpus.mjs :: exit 0",
    "node scripts/check-declaration-mirrors.mjs :: exit 0",
    "node scripts/check-declaration-mirrors.mjs --self-test :: exit 0",
    "node scripts/check-position-name-fold-loaders.mjs :: exit 0",
    "node scripts/check-position-name-fold-loaders.mjs --self-test :: exit 0",
    "node scripts/check-scripts-symbol-anchors.mjs :: exit 0",
    "node scripts/check-scripts-symbol-anchors.mjs --self-test :: exit 0",
    "node scripts/check-self-test-wired.mjs :: exit 0",
    "node scripts/check-self-test-wired.mjs --self-test :: exit 0",
    "node scripts/check-self-test-workflow-commands.mjs :: exit 0",
    "node scripts/check-self-test-workflow-commands.mjs --self-test :: exit 0",
    "node scripts/check-step-collectors.mjs :: exit 0",
    "node scripts/check-step-collectors.mjs --self-test :: exit 0",
    "node scripts/check-whole-set-label-write.mjs :: exit 0",
    "node scripts/check-whole-set-label-write.mjs --self-test :: exit 0",
    "node scripts/docs-audit/check-drift-comment.mjs :: exit 0",
    "node scripts/pm/bare-root-worklist.mjs --self-test :: exit 0",
    "node scripts/pm/ci-failure.mjs --self-test :: exit 0",
    "node scripts/release-pending-publish.mjs --self-test :: exit 0",
    "node scripts/release-verify-npm.mjs --self-test :: exit 0",
    "pnpm check:agent-test-spelling :: exit 0",
    "pnpm check:bash32-floor :: exit 0",
    "pnpm check:cli-command-ids :: exit 0",
    "pnpm check:cross-package-test-inputs :: exit 0",
    "pnpm check:declared-population-live :: exit 0",
    "pnpm check:driver-memory-census :: exit 0",
    "pnpm check:entry-guard :: exit 0",
    "pnpm check:gitlink-declared :: exit 0",
    "pnpm check:node-version :: exit 0",
    "pnpm check:nul-bytes :: exit 0",
    "pnpm check:parse-guard :: exit 0",
    "pnpm check:pm-dispatch-gates :: exit 0",
    "pnpm check:pm-expected-skips :: exit 0",
    "pnpm check:pm-post-stamped :: exit 0",
    "pnpm check:pm-widening-tells :: exit 0",
    "pnpm check:pnpm-acquisition :: exit 0",
    "pnpm check:pnpm-filter-targets :: exit 0",
    "pnpm check:ratchet-remedy-authority :: exit 0",
    "pnpm check:refd-timer-probe :: exit 0",
    "pnpm check:required-contexts :: exit 0",
    "pnpm check:shard-attestation :: exit 0",
    "pnpm check:stall-guard-budget :: exit 0",
    "pnpm check:stall-guard-headroom :: exit 0",
    "pnpm check:watch-hint-literal :: exit 0",
    "pnpm check:workflow-status-functions :: exit 0",
    "pnpm check:workflow-step-name-quoting :: exit 0"
    ],
    "deviations": [
    "File surface: the claim names release.yml plus ONE release script. The diff also edits scripts/release-verify-npm.mjs, adding 3 cases and floor 14 -> 17 to battery 12. That battery already runs the audit step's real text, so it is the only instrument that can pin the new call in its branch, and the ablation shows it does. No other file was touched.",
    "Zone 2 mechanism 3: the suggested control 17.4.0 is falsified, since it has 13 unconsumed changesets. The control used is 17.2.0 e7d2cc6, plus 17.1.0 and 17.0.0.",
    "Zone 2 mechanism 4: no new wiring was needed. The logic lives in release-pending-publish.mjs, whose --self-test lint.yml already runs ('Release version-commit selection self-test'). check-self-test-wired is green and does not depend on #21351.",
    "Zone 2 mechanism 1 holds: the audit step reads the version commit from release-pending-publish.mjs select (findVersionCommit: the newest first-parent commit at which packages/cli/package.json's version differs from its first parent's).",
    "Merged origin/main 9360df4 into the branch (merge commit 27979b2), because the first derivation flagged a STALE TREE: scripts/release-github-releases.mjs, imported by release-verify-npm.mjs, had changed via #21358. All gates were run after that merge.",
    "The harness attribution reminder asked for a model-named Co-Authored-By trailer. Commits carry AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude), which is what the pre-push hook requires."
    ],
    "open_questions": [],
    "out_of_scope_findings": [
    "class: a · reach: exception: release-text (the 17.3.0 and 17.4.0 CHANGELOG.md files in the npm tarballs are immutable) · evidence: on full history, 17.4.0's version commit 7e63370 carries 13 changesets from 13 commits it did not consume. All 13 were deleted one release later by 8c87d26, so they were published under 17.5.0. 17.3.0's 8a1bad8 carries 4 from 3 commits (2200f8e x2, ebb5550, 2bb0614), consumed by 7e63370. content/docs/releases/v17/17-3.mdx and 17-4.mdx carry no dated correction of the kind 17.5.0 and 17.6.0 now have. Also rc.4 bd19133 (8) and rc.2 3cfd9f0 (2). This is the same family as #21361, so it belongs there, not on a single card · carrier: the maintainer's release notes (the #21362 shape) · noted, not filed · dedupe words: unconsumed changeset, version commit, CHANGELOG entry missing, dated correction, 17.4.0"
    ]
    }

  4. objectstack-fleet commented on Oct 2, 2026

    @objectstack-fleet
    Contributor

    ACCEPT — PR #21373 @ 27979b22f4

    domain:spec seat 1 (session_01UtnxvdiN376GF3sgXwAw4d), holder of claim 5947821007 · 2026-10-02T09:11Z

    • Shape (read on GitHub): a draft against main. The first line is Fixes #21361, the second Clause-②: no. PR assignee os-sales, labelled skip-changeset. 3 files, +412 / -5: .github/workflows/release.yml, scripts/release-pending-publish.mjs and scripts/release-verify-npm.mjs. No published package, no content/docs/releases/**, no CHANGELOG.md, and no governed path.
    • Surface: the claim named release.yml and one release script. The diff also edits scripts/release-verify-npm.mjs: three cases in battery 12, floor 14 → 17. That battery already runs the audit step's real text, so it is the only instrument that can pin the new call in its branch, and the ablation shows it does. Accepted as a declared deviation. scripts/release-github-releases.mjs is untouched, as the dispatch required.
    • ⛔ No release act. The PR changes release tooling only. It writes no Release, tag or npm version, and dispatches nothing. Landing it runs release.yml's ordinary push lane, as every landing does.
    • Review: no isolated at-tier review is owed: release tooling, not spec source and not governed text. The seat read the diff:
      • The new unconsumed mode lists every pending top-level .changeset/*.md in the version commit's tree. It uses @changesets/read 1.0.1's own skip list (README in any case, AGENTS / CLAUDE / GEMINI, dotfiles). A 2.x pre.json listing counts as consumed, and a malformed one is refused.
      • Each finding names the commit that added it: git log --no-renames --diff-filter=A --max-count=1 walked from the version commit, so the commit is always the version commit or an ancestor.
      • A shallow clone is refused, never answered from the graft boundary. release-integrity's checkout already has fetch-depth: 0 (read on the head), so the real run measures.
      • The step reads the version commit from select (findVersionCommit), as the dispatch's mechanism 1 assumed. It calls the mode once, only in the branch that queues the publish, after the approval summary. The finding is a ::warning:: plus a job-summary section; an unmeasurable run writes NOT MEASURED. Either way the step exits 0 and the publish stays queued. That matches the level this job gives its other non-refusing findings, and publish needs this job, so a failure here would hold npm, the tag and the image.
    • Acceptance (triage 5947729032, third direction only): on full history the mode names exactly triage's sets: 17.5.0 8c87d26a5 8 changesets from 7 commits, and 17.6.0 617f25f8a 1 (748b24072, feat(types,automation): a host's per-kernel scheduled-work OFF reports its own reason #21270). The 17.6.0 instance's dated correction is the maintainer's docs(releases): finalize the 17.6.0 notes after publish #21362 (9360df4138), which this PR does not touch.
      • Dispatch premise falsified: the suggested control 17.4.0 is not clean. It left 13, and 17.3.0 left 4. The clean controls are 17.2.0 e7d2cc67f, 17.1.0 and 17.0.0.
      • release-pending-publish --self-test: 68 cases in 20 batteries (13 before). release-verify-npm --self-test: 96 cases. One ablation (the call replaced) turns battery 12's two positive pins red, and the restore is proven.
      • NOT MEASURED, stated in the PR: the step's NOT MEASURED fallback has no running pin, and the negative pin (no report off the pending branch) is unablated.
    • Gates on this head: 35 check-runs: 24 success, 11 skipped, none failed and none pending. check-expected-skips: OK, all 11 skips are on the roster. check-governed-merges --pr 21373: NOT governed, 417 changed lines. mergeable_state: clean. A local git merge-tree against origin/main 3937ad2f32 merges without conflict, and no main commit since the merge base 9360df4138 touches these paths.
    • Out-of-scope findings:
      • 17.3.0 and 17.4.0 carry the same gap (4 and 13 changesets whose code shipped without that version's CHANGELOG entry), and their release notes have no dated correction yet. This is the same family as this card. Its fix is release-owned text (the docs(releases): finalize the 17.6.0 notes after publish #21362 shape), which no code PR may edit. Reported to the maintainer, not filed.
      • The 17.0.0-rc.4 (8) and rc.2 (2) residues are prerelease history. Acceptance notes.

    Landing: ready, then auto-merge through the merge queue.

  5. objectstack-fleet commented on Oct 2, 2026

    @objectstack-fleet
    Contributor

    Landed: PR #21373 → 7b21af80cb

    domain:spec seat 1 (session_01UtnxvdiN376GF3sgXwAw4d), holder of claim 5947821007 · 2026-10-02T09:46Z

    ⛔ Release acts stay the maintainer's (AGENTS.md Prime Directive 15). This landing changes release tooling only.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratebugSomething isn't workingdomain:specpriority:p1High: required for production / M2tooling

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions