Skip to content

fix(release): the stable cut folds each prerelease section into the stable section instead of copying it - #26156

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-3p0fdh
Oct 26, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-3p0fdh

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steve · project thread
Fixes #26079
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6417011624 (batch #397 item 3, letter A′, maintainer 「同意」)

What this does

On the stable cut out of pre mode (changeset pre exit, then pnpm run version), the changelog generator now folds every X.Y.Z-next.N section of the cycle into the X.Y.Z section instead of leaving it as a second copy below it. Today that cut re-applies every changeset the prereleases consumed, so the stable section repeats each prerelease section verbatim (18.0.0 left packages/spec/CHANGELOG.md with about 4,076 duplicated long lines).

Ruled scope, kept: only scripts/changeset-changelog.mjs changes behaviour; its pins go into the self-test CI already runs for it (node scripts/release-pending-publish.mjs --self-test, lint.yml), the file surface the seat widened on the claim. ⛔ No files change in any package.json — CHANGELOG.md keeps shipping (AGENTS.md, Documentation Guardrails). ⛔ No existing CHANGELOG text is rewritten by this PR, and the generator never touches an older cycle (#25919 A 「存量不横扫」). No workflow change, no new gate.

Timing — when the fold runs, and on what

The generator API (getReleaseLine / getDependencyReleaseLine) hands out one line at a time and is told neither the package nor the version, and @changesets/apply-release-plan writes every CHANGELOG.md only after all lines are generated. So:

  1. Armed on the first generated line, and only when .changeset/pre.json says "mode": "exit" (the stable cut; the version pass itself deletes that file later). An rc cut (mode: "pre") and a normal cut (no pre.json) arm nothing.
  2. Snapshot at arming: each tracked *CHANGELOG.md whose top ## VERSION is a prerelease (read from the first 64 KiB only).
  3. Run when the changeset version process exits 0 (a process.once('exit') handler; a failed version pass folds nothing). For each snapshotted file whose top is now a new stable section sitting directly above the old top, the prerelease sections between that stable section and the next stable one below are dropped — each one only if every changeset entry it carries is found in the new stable section (compared without the commit prefix). Dependency-bump entries (Updated dependencies, pkg@X.Y.Z-next.N) are superseded by the stable section's own. A section holding an entry the stable section does not reproduce (amended in place by a docs PR, say) stays, with a ::warning:: naming it.

What may fold is fixed by the file as it stood before the cut, never by a version guess, so every stable section and every older cycle's prerelease sections are kept byte for byte. One plain line reports the count: changeset-changelog: the stable cut folded N prerelease section(s) in M CHANGELOG(s) into their stable section.

Evidence

  • node scripts/release-pending-publish.mjs --self-test at 2f44043a: ✓ release-pending-publish self-test: 136 cases across 34 batteries pass. Three new batteries (floor 31 → 34): a prerelease section folds (4 cases); the stable section is kept whole, and so is every older cycle, and a section with an entry the stable section lacks stays (5 cases); armed only on an exit-mode cut, for the files whose top was a prerelease, folded after the write (3 cases).
  • End to end through the real CLI (@changesets/cli 3.0.3, throwaway repo, two fixed packages, this generator): pre enter next → version (1.2.0-next.0) → version (1.2.0-next.1) → pre exit → version (1.2.0). Result: ## 1.2.0 (all three entries, each naming the commit that added its changeset), then the untouched ## 1.1.0 / ## 1.1.0-next.0; the run printed folded 4 prerelease section(s) in 2 CHANGELOG(s). The rc cuts in between folded nothing.
  • Control leg, same history, the generator at origin/main 95294be3: the stable cut leaves 5 sections (1.2.0, 1.2.0-next.1, 1.2.0-next.0, 1.1.0, 1.1.0-next.0), 37 + 36 lines, against 3 sections and 23 + 22 lines with this change.

Confidence gap from the card

No downstream tooling reads an installed CHANGELOG's prerelease section:

  • This repo: the CHANGELOG readers (check:release-notes, check:release-page-status, check-release-section-coverage.mjs) read ## N.x.y headings for majors / GA versions; the stable heading the fold keeps is what they count.
  • objectstack-ai/hotcrm at 9464673: no script reads node_modules/@objectstack/*/CHANGELOG.md; one code comment cites the 17.0.0 heading of installed CHANGELOGs, a stable heading.
  • objectstack-ai/cloud: NOT MEASURED — not readable from this session.

Gates

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 2f44043a derives 33 commands; all 33 exit 0, and --ran reconciles 33 derived, 33 run, 0 NOT-MEASURED, 0 UNRUN. CI families outside the derivation are read from this PR's check-runs.

Acceptance notes

  • The fold applies at the next stable cut out of pre mode; nothing in the tree changes on merge. Rollback is reverting this PR before that cut.
  • skip-changeset: the diff is repository tooling under scripts/, which no package's files ships.

Generated by Claude Code

…table section instead of copying it

Leaving pre mode re-applies every changeset the cycle's prereleases
consumed, so the stable `## X.Y.Z` section repeated each
`## X.Y.Z-next.N` section below it. The changelog generator now arms a
fold on an exit-mode cut: when `changeset version` exits 0, each
CHANGELOG whose top was a prerelease before the cut drops the cycle's
prerelease sections whose every entry the new stable section
reproduces. Stable sections and older cycles stay byte for byte.

Claude-Session: https://claude.ai/code/session_01PbYqgMPfuTjR2CMEqrjHVL
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 26, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · devx seat 2 · 2026-10-26T14:08Z

Seat domain:devx#2 (#20163) · session_01Q4MDXRbEJCNdPAP4SiXwp9 · reviewed head 2f44043aea · card #26079, claim 6417130710 (file surface widened by the seat to the CI-run self-test), ruling 6417011624 (letter A′).

  • Shape: draft → main, Fixes #26079, Clause-②: no, Metadata-protocol: none, Ruling-ref: 6417011624 at line start. One commit.
  • Ruled scope held: only the generator's behaviour changes. No package.json files entry is touched, so CHANGELOG.md keeps shipping (AGENTS.md Documentation Guardrails, as the ruling requires); no existing CHANGELOG text is rewritten (docs: narrative cost — 42.5% of source lines are comments, workflows 58.6%, AGENTS.md is 103 KB with three stale claims, ROADMAP.md still says v3.3.2 and 80 Storybook stories #25919 A).
  • Scope: exactly the two claimed files (+253/−4), neither governed: scripts/changeset-changelog.mjs (+184/−3) and scripts/release-pending-publish.mjs (+69/−1). No .changeset entry, which is right for repo tooling ⇒ skip-changeset.
  • Readings of the diff:
    • The fold is gated three ways, all read off the file as it stood before the cut: .changeset/pre.json mode must be exit; the CHANGELOG's top section must have been a prerelease (read from the head of the file only, so spec's 10 MB is not loaded to decide); and after the pass the file must open with a stable section sitting directly above that same prerelease version. A package whose top is already stable is never armed.
    • Only the prerelease sections between the new stable section and the next stable one below are considered. Everything else, older cycles included, is copied byte for byte.
    • A section folds only when every changeset entry it carries is found in the new stable section, compared without the commit prefix. A section holding an entry the stable section does not reproduce is kept whole with a ::warning:: naming it — the conservative direction.
    • Updated dependencies entries (and their indented pkg@version lines, which the parser keeps with their entry) are left out of the comparison, so a dependency bump never holds a section back. The stable section carries its own.
    • The generator API has no post-write hook, so the fold is armed on the first release line and applied from process.once('exit'), only on exit code 0.
  • Gates: I re-ran the CI self-test (node scripts/release-pending-publish.mjs --self-test, lint.yml:3329) on this head in a clean worktree: 34 batteries, floor raised 31 → 34, and all three new changelog fold batteries pass, including the armed/not-armed case over a real git fixture. @changesets/cli is not installed in my container, so I stubbed @changesets/cli/changelog to let the dynamic import resolve; the one case that fails under that stub is the pre-existing attribution battery asserting the stock line format, which the stub cannot reproduce. The dev's own run with the real dependency reports 136 cases over 34 batteries green, 33/33 derived gate families exit 0, 0 NOT-MEASURED, plus a real CLI end-to-end (pre enter → next.0 → next.1 → pre exit → 1.2.0 folds 4 sections, against a control leg on main that keeps 5). CI on this head was still running at review; the flip to ready waits for every check green or skipped.
  • Accepted deviation: the claim's file surface was widened before work began, because changeset-changelog.mjs has no self-test CI runs; the pins belong in the one that does, following fix(release): a changeset a revert restored is credited to the commit that first added it in the CHANGELOG #25669's precedent.
  • Confidence gap, recorded: hotcrm@9464673 has no script reading an installed package's CHANGELOG; cloud is NOT MEASURED. The ruling's question was the tarball copy, which this PR leaves in place, so nothing is blocked on it.
  • Out of scope: none filed.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 26, 2026 14:13
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 26, 2026 14:13
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 26, 2026
Merged via the queue into main with commit 365fe5e Oct 26, 2026
52 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-3p0fdh branch October 26, 2026 14:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants