Skip to content

chore(release): exit Changesets pre mode (next), so the next version pass cuts the 18.0.0 GA - #24555

Merged
os-zhuang merged 3 commits into
mainfrom
claude/issue-24528-changesets-pre-exit
Oct 19, 2026
Merged

os-zhuang merged 3 commits into
mainfrom
claude/issue-24528-changesets-pre-exit

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #24528

Clause-②: no

Exits Changesets pre mode on main, on the maintainer's order 「发 GA 要先退出预发布模式。立卡派发」. After this lands, the next version pass computes the GA: the 73-member lockstep (fixed) group goes from 18.0.0-next.5 to 18.0.0 and publishes to npm's latest tag. Draft, for the maintainer to merge. Its merge sets when the next Version Packages PR versions 18.0.0, and the dry-run below shows that refresh is still blocked by two release-owned doc sentences, so read "GA blockers" before merging.

⛔ No changeset version output is committed. Nothing was published or tagged, no release workflow was dispatched, and neither the Version Packages PR nor changeset-release/main was touched.

Head abec338235 = 5b14b7f3ad (base at worktree creation) + this change + origin/main 04669a467e, merged in before opening.

The change

  1. .changeset/pre.json, written by pnpm changeset pre exit (@changesets/cli 3.0.3) and committed exactly as the tool wrote it (4fe7eb6abe):

    content
    before { "mode": "pre", "tag": "next" }
    after { "mode": "exit", "tag": "next" } (2-space JSON plus a trailing newline, as before)
    • @changesets/pre 3.0.0 exitPre spreads the state it read and sets mode: "exit". Its migratePreState only rewrites a file that still carries v2's initialVersions or changesets keys. This file has neither, so it is a no-op here.
    • So under v3, pre exit neither writes nor expects initialVersions or changesets. The .changeset/pre/ layout does not change what it writes. What the layout changes is what the GA pass reads: the CLI's reader (@changesets/read 1.0.1) always lists .changeset/pre/, and in exit mode those changesets are consumed again (measured below).
    • The CLI's own exit message says: "Please also review the changesets in the .changeset/pre folder as they will be used as changelogs for the normal versions." See the acceptance notes for what that costs.
  2. content/docs/protocol/backward-compatibility.mdx, 11 sentences (1b8d66ed1c). This page is hand-written and not release-owned. Merging the mode switch makes these present-tense sentences false: "Changesets is in pre mode", "the guard stands aside", "on the v18 line it ships in the next 18.0.0-next.N".

    • Each sentence now puts the 18.0.0-next.N behaviour in the prerelease, in the past tense. Two of them also state the exit facts: the line's GA version is 18.0.0, and the guard fails a major bump again.
    • The page's own post-GA rule ("the window closes at GA, and strict SemVer resumes from that point") is left as written. No new policy prose is added.
    • Both lockstep-count anchors (would promote all 73 published packages. and all **73** packages published from this repository) are byte-unchanged, and check:lockstep-package-count is green.
    • This file is outside the claim's declared surface. It is here because the role contract makes a published statement that this change falsifies a must-fix. The release-owned pages that it also falsifies are listed under GA blockers, because content/docs/releases/ is never edited in this PR.

Dry-run of the version pass (scratch clone, never pushed)

Setup: a clone of this branch at abec338235 in the scratchpad, outside the worktree.

  • Full history (--is-shallow-repository false, root commit present) and local main at 04669a467e. release-rehearsal-clone.mjs reported "✓ FIT".
  • Push URL set to an invalid path. Install was pnpm install --frozen-lockfile --ignore-scripts, with the migration registry generated by hand, because the scratchpad path is too long for tsx's IPC socket during prepare.
  • The version-pr job's steps Render the post-version tree, Validate the post-version tree and Restore the pre-version tree were extracted verbatim from release.yml and run with RUNNER_TEMP and GITHUB_OUTPUT pointed at scratch files. The workflow itself was not run.

What the pass consumes, and what happens to pre.json

reading value
pending_before (Render step, exit mode counts .changeset/*.md plus .changeset/pre/*.md) 1029
consumed from .changeset/pre/ (deleted by the pass) 1011
consumed from .changeset/ 18
.changeset/pre.json after the pass deleted
.md files under .changeset/ after the pass README.md only (.changeset/pre/ is left empty, so git tracks nothing there)
@changesets/read 1.0.1 on the post-version tree 0 changesets
paths moved (Validate's shape assertion) 1205, "all inside the reviewed shape"
annotate-shipped (first step of pnpm run version) 6 changesets left unconsumed by the 18.0.0-next.5 version commit 820fd5aef0 annotated "Shipped in 18.0.0-next.5"
publish dist-tag after the GA commit latest. getReleaseTag (@changesets/cli 3.0.3 getPublishPlan.mjs:599-603) returns latest when there is no pre state, and the GA version commit has none. This is a reading of the code; nothing was published.

Every package, before and after

Lockstep group: all 73 members 18.0.0-next.5 → 18.0.0. The GA is 18.0.0, not 18.0.0-next.6 and not 19.0.0. The group: @objectstack/hono, @objectstack/cli, create-objectstack, @objectstack/spec and the other 69 public packages listed in .changeset/config.json. check-changeset-fixed.mjs reports "in sync with 73 public workspace packages", and the dry-run read 73 of 73 members at 18.0.0.

The 13 private workspace packages outside the group:

package before after
@objectstack/example-crm 4.0.100-next.5 4.0.100
@objectstack/example-todo 4.0.100-next.5 4.0.100
@objectstack/example-multi-package 0.0.7-next.5 0.0.7
@objectstack/example-showcase 0.3.22-next.5 0.3.22
@objectstack/example-embed-objectql 0.0.40-next.5 0.0.40
@objectstack/dogfood 0.0.48-next.5 0.0.48
@objectstack/downstream-contract 0.0.46-next.5 0.0.46
@objectstack/http-conformance 0.1.8-next.5 0.1.8
@objectstack/docs 4.2.3 unchanged
@objectstack/spec-monorepo (root) 4.0.1 unchanged
objectstack-blank (template) 0.1.0 unchanged
@objectstack/refd-timer-testkit 0.1.0 unchanged
@objectstack/vitest-filter-preflight 0.1.0 unchanged

GA blockers: the refresh cannot produce the 18.0.0 Version Packages PR yet

Leg A, the tree as this PR leaves it.

  • Render step exits 1. changeset version, sync-protocol-version, sync-template-versions and sync-docs-image-tags all succeed. Then sync-release-index-currency refuses, by design: "the v18 entry's status reads "prerelease: 18.0.0-next.N on npm's next tag, no GA date", but the newest released 18.x version is 18.0.0 … These are the shapes this rewriter deliberately refuses, because each needs a SENTENCE and not a token". Under set -e the job stops there, so neither Validate nor the PR step runs.
  • Completing the chain by hand (check-skill-compatibility-version --fix) and running Validate anyway shows the second refusal. 14 of 15 content gates are green, and check:release-page-status is red:
    • content/docs/releases/v18/index.mdx: "the release-status blockquote never states that a 18.x version is released";
    • content/docs/releases/index.mdx: the v18 entry is flagged for prerelease-word and for prerelease-version.

Leg B, the same tree with stand-in GA prose (rehearsal clone only, committed there and never pushed).

  • Two edits: the index entry's status changed to (current series: 18.0.0, released 2026-10-19), and a blockquote lead added that reads 18.0.0 was released on 2026-10-19.
  • Result: Render exits 0, and Validate exits 0 with "Post-version tree validated." (15 of 15 green, major-boundary surfaces left unvalidated: 0). The CLI's reader then sees 0 pending, and Restore exits 0 with "Pre-version tree restored: 1029 pending changeset(s), working tree clean."
  • So these two sentences are the only blockers this dry-run found.

What is needed, for the seat to card. A docs-only PR, because content/docs/releases/ is release-owned and never edited in a code PR:

  1. content/docs/releases/index.mdx: the v18 entry's status becomes (current series: 18.0.0, released DATE). Also rewrite the paragraph "The v18 line is a major in prerelease. Changesets is in pre mode…" (lines 58-62), which this PR makes false at merge.
  2. content/docs/releases/v18/index.mdx: a release-status blockquote stating that 18.0.0 is released. Also rewrite the pre-mode sentences at lines 6-11, 27 and 62. The description front matter carries the same claim.
  3. Advisory only, not blocking: check-release-section-coverage notes that no heading on the v18 page names the 18.0 series.

Timing: sync-release-index-currency refuses to re-date a field that already names the newest GA. So DATE is never corrected by the pass, and no gate checks it. Write the UTC day of the refresh whose Version Packages PR is merged. The docs PR has to land after this one and before that refresh. Until both have landed, every six-hourly refresh is red. That red is loud and publishes nothing, and the standing Version Packages PR keeps its last validated content (18.0.0-next.6).

The other route was considered and not taken: teaching sync-release-index-currency a prerelease-to-first-GA transition. That would still leave the v18 page's blockquote (blocker 2) needing a sentence, so the docs PR is needed either way. It would also reverse that rewriter's documented boundary ("An entry whose parenthetical is prose. Same reason.").

Mode-keyed gates, read on the exited tree (abec338235)

gate verdict
scripts/check-changeset-no-major.mjs Green, and it re-arms. PR-scoped --base origin/main exits 0: "This diff introduces no major bump". --list exits 0: "18 pending changeset(s), 0 declaring a major. .changeset/pre.json mode: exit". The 29 files in .changeset/pre/ that declare a major (47 major entries) are not refused: the gate judges diffs, and no PR's diff carries them. Re-arm probe (rehearsal clone only): a new "@objectstack/spec": major changeset on the exited tree gives exit 1 ("This PR introduces changeset(s) that declare a major bump"). The control, the same tree with mode: "pre", gives exit 0 (exempt). Consequence: from the merge on, an open PR that adds a major changeset goes red at Check Changeset unless it carries allow-major.
release.yml pending count (Render :511ff, Restore :861ff) Green. In exit mode it counts .changeset/*.md plus .changeset/pre/*.md = 1029, equal to what changeset version deleted. Restore reads the mode from the restored tree, because the pass deleted pre.json. Replayed verbatim, it restored 1029.
Major-boundary classes (release.yml:373ff) Green, nothing moves. PROTOCOL_VERSION already reads '18.0.0' ("✓ PROTOCOL_VERSION already 18.0.0"). The blank template already stamps ^18.0.0 and '^18' ("template stamps moved: 0"). Validate reports "major-boundary surfaces left unvalidated: 0", so the protocol-version.ts refusal branch is not reached.
hotcrm smoke (release.yml:1941ff) Unaffected by the mode. It is no longer keyed on pre.json. BLOCKING: '0' keeps it advisory in every mode (the card's "advisory while in pre mode" is out of date).
cut-rc.yml allowlist (:232-237) Unchanged. It requires mode: pre with tag: rc, so it refused before (tag next) and still refuses (mode exit). The GA lane is release.yml.
scripts/release-pending-publish.mjs Green. --self-test exits 0 (112 cases), and annotate-shipped ran in the dry-run as described above.
scripts/check-docs-image-tag.mjs Green. On the exited tree it expects the newest GA, 17.7.0, because exit still means the prerelease line. On the post-version tree it is green inside Validate, with the docs at 18.0.0.
scripts/sync-docs-image-tags.mjs Green. In the dry-run it moved 9 pins on 3 surfaces from 17.7.0 to 18.0.0, and check:docs-image-tag-sync is green on both trees.
scripts/check-published-files.mjs Green. "73 publishable package(s) of 84 workspace member(s)".
scripts/check-adr-0087-registration.mjs Green. --base origin/main exits 0 and --self-test exits 0.
pr-automation.yml Check Changeset Phase-agnostic by design. "changeset pre exit re-arms it", as measured in the probe above.
scripts/objectui-changeset-digest.mjs inPreMode Reads false after the exit. An objectui major would now be digested as minor, which mirrors the no-major guard. check:objectui-changeset is green.

Changeset and label

This PR adds no changeset, and the mode switch is not a package change. The Check Changeset job's route 0 does not apply, because no .changeset/*.md row changes (pre.json is not a .md). Route 2 applies, because the PR releases nothing: neither .changeset/pre.json nor content/docs/** is in any public package's files[] (measured over every non-private package.json). So skip-changeset is applied at creation.

Gates (local, exit codes captured before any pipe)

  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at abec338235 derives 54 commands for this two-file diff.
  • The --ran reconciliation reads "54 derived, 54 run, 0 NOT-MEASURED, 0 UNRUN", and all 54 exit 0.
  • Two families first returned exit 3 (PREREQUISITE NOT MET), and both are recorded at their re-run: check:docs-transcript-drift needs the lint dist, and check:skill-examples needs the client and client-react dists. I built @objectstack/lint... (4 packages, 121 s) and @objectstack/client-react... (39 packages, 340 s) under os-verify-lock.sh, then re-ran both, exit 0.
  • Beyond the derivation, all exit 0: check-changeset-no-major --list, check-changeset-fixed, check:docs-image-tag-sync, check:release-page-status, check-release-section-coverage --strict, check:release-index-currency-sync, check:future-spec-major, check:template-version-sync, check:skill-compatibility and check:release-notes. These are the exited, pre-version tree, which stays green until the version pass runs.
  • NOT MEASURED:
    • package tests and type-check lanes, because no TypeScript or package source is touched;
    • the changesets/action read rehearsal, because it is a GitHub Action, so the CLI's own reader was read instead;
    • check:dev-prereqs, because the diff does not touch the packages/spec build.
  • CI check-runs: see the report comment on the card.

Acceptance notes

  • The standing Version Packages PR (chore: version packages #24524) reads 18.0.0-next.6. Do not merge it after this PR expecting the GA. Until the docs blockers land, every refresh is red and it keeps next.6.
    • If it is merged after this PR anyway, it publishes 18.0.0-next.6 to next. The version commit's tree still carries pre.json with mode: "exit", so getReleaseTag returns preState.tag. The GA cut then follows on the next green refresh. This is a reading of the code, not a measurement.
  • The GA CHANGELOG is large. Measured at 4fe7eb6abe (1028 changesets):
    • packages/spec/CHANGELOG.md gains a 6,006-line ## 18.0.0 section of 983,037 bytes, added on top of the six 18.0.0-next.N sections;
    • the version diff is +58,729 / −21,347 lines.
    • The GitHub Release bodies are truncated at 125,000 characters by release-github-releases.mjs.
  • --list undercounts in exit mode. check-changeset-no-major --list and the check-adr-0087-registration audit surfaces count root stock only (18) and exclude .changeset/pre/ as consumed, while the GA pass consumes 1029. This is informational only, because both gates judge diffs. Noted, not carded.
  • The card's mechanism hypothesis that pre exit "expects initialVersions/changesets" does not hold under v3 (see The change, item 1).
  • objectstack-ai/objectui's own pre mode is out of scope and untouched. No gate was added.

Card checklist

  • Wanted: exit pre mode with the CLI, never by hand. Done: pnpm changeset pre exit, committed as written.
  • Wanted: a dry-run with every package's versions, the consumed counts and the fate of pre.json. Done: the tables above (73 members to 18.0.0; 1011 consumed from pre/ plus 18 from the root; pre.json deleted).
  • Wanted: every mode-keyed gate read on the exited tree. Done: the gate table. The two gates that break the GA Version Packages PR are the release-docs refusals, and their remedy is release-owned prose, not a small gate edit. So they are named with their remedy for the seat instead of being edited here.
  • Wanted: no publish, tag, Version Packages PR or release dispatch. Held.
  • Wanted: the PR stays draft. Held.
  • Done when ①: draft open, with the plan and the gate verdicts. This PR.
  • Done when ②: the next Version Packages PR's head carries 18.0.0 for @objectstack/spec. Not reachable until the docs-only PR above lands. Leg B shows that once it does, the refresh validates with @objectstack/spec at 18.0.0.

维护者速读(草稿)

改了什么:退出 Changesets 预发布模式。.changeset/pre.json 由 pnpm changeset pre exit 写出,模式从 pre 改为 exit。另外把 content/docs/protocol/backward-compatibility.mdx 里 11 处"v18 正在预发布"的现在时句子改成过去时,因为合并后这些句子就不再成立。没有代码、工作流或 changeset 改动。

为什么改:执行维护者的指令「发 GA 要先退出预发布模式。立卡派发」。在临时克隆里演练了版本步骤。合并后,下一次版本 PR 会把 73 个锁步包从 18.0.0-next.5 定为 18.0.0(不是 next.6,也不是 19.0.0)。这一步会消费全部 1029 条 changeset,删除 pre.json,发布时打 npm 的 latest 标签。

风险与代价(含回滚):

  • 只合并本 PR,GA 版本 PR 还出不来。 演练表明,版本 PR 的刷新会在两处按设计拒绝:content/docs/releases/ 下 v18 的发布页和发布索引仍写着"预发布、没有 GA 日期",需要人写一句 GA 文案。这两处属于发布文档,本 PR 不能改,要另开一个 docs-only PR。加上替代文案后,整条流程演练全绿。
  • docs PR 落地之前,每 6 小时一次的刷新都会变红。变红不发布任何东西,现有的版本 PR chore: version packages #24524 会停在 18.0.0-next.6,不要把它当成 GA 合并。
  • 合并后"禁止 major"守卫重新生效:在途 PR 如果带 major changeset,会在 Check Changeset 变红,除非加 allow-major。
  • 合并之后、GA 发布之前,latest 仍是 17.7.0。
  • 18.0.0 的 CHANGELOG 会把所有预发布条目再汇总一遍,约 1 MB。
  • 回滚:在 GA 版本 PR 合并之前,直接 revert 本 PR 即可(恢复 mode: "pre")。GA 发布到 npm 之后无法回滚。

席位意见:

你要做的:

  1. 决定 GA 时机。
  2. 让席位出那份 docs-only PR,写好 GA 文案。发布索引里的日期要写成最终被合并的那次版本 PR 刷新的 UTC 日期,这个日期没有门禁校验。
  3. 先合并本 PR,再合并 docs PR,然后触发版本 PR 刷新。确认它的 head 里 @objectstack/spec 是 18.0.0 之后,再合并版本 PR。

Generated by Claude Code

…ass cuts the 18.0.0 GA

Written by `pnpm changeset pre exit` (@changesets/cli 3.0.3), committed as the
tool wrote it: `.changeset/pre.json` mode "pre" -> "exit", tag unchanged.

Claude-Session: https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp
Co-authored-by: Claude <noreply@anthropic.com>
…ease in the past tense once pre mode exits

`changeset pre exit` makes eleven present-tense sentences on this page false:
Changesets is no longer in pre mode, check-changeset-no-major.mjs refuses a
`major` again (measured: exit 1 on the exited tree, exit 0 with mode "pre"),
and the next version pass computes 18.0.0. Each sentence now scopes the
`18.0.0-next.N` behaviour to the prerelease, and the page's existing rule
("the window closes at GA, and strict SemVer resumes") is left as written.

Claude-Session: https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读

改了什么:.changeset/pre.json 的 mode 从 pre 改为 exit,用 pnpm changeset pre exit 写出,没有手改。另外 content/docs/protocol/backward-compatibility.mdx 里描述「v18 正处于预发布」的 11 句改成过去时,只写「已退出预发布模式」,不声称 GA 已经发布。共 2 个文件,+12/−12。

为什么改:你说的「发 GA 要先退出预发布模式」。合入之后,下一个 Version Packages PR 会把 fixed group 的 73 个包从 18.0.0-next.5 升到 18.0.0,并一次消耗掉 1029 个 changeset(.changeset/pre/ 里的 1011 个加上待发的 18 个)。这是开发在临时副本里逐字重放 release.yml 的版本步骤预演出来的,pre.json 也会在那一步被删除。

风险与代价(含回滚):

席位意见:建议在你选定的 GA 日按这个顺序合:本 PR → #24569 → 刷新 Version Packages PR。合并的时机你来定。

你要做的:在你选定 GA 的那天合入本 PR,紧接着合 #24569;然后让 Version Packages PR 刷新一次,确认渲染出 18.0.0,再按你平常的流程发版。

席位核对

ACCEPT · domain:devx seat 1 · os-warren · session_01FM29ZqLTrnPNXxijioZwMp · 2026-10-19T13:55Z · head abec338235 · card #24528 (claim 6267382000)

  • Form. Draft, base main, first line Fixes #24528, assignee os-warren, skip-changeset (the mode switch is not a package change). The card's ruling keeps it draft for the maintainer's merge whatever its tier; node scripts/pm/check-governed-merges.mjs --pr 24555 → NOT governed, 24 changed lines.

  • The switch, read in the diff. .changeset/pre.json goes from { "mode": "pre", "tag": "next" } to { "mode": "exit", "tag": "next" }, written by pnpm changeset pre exit (@changesets/cli 3.0.3; exitPre spreads the state and sets mode: exit, so the minimal file needs no initialVersions).

  • The docs edit. 11 sentences in content/docs/protocol/backward-compatibility.mdx move from "is in pre mode" to past tense: "has exited prerelease mode", "during the v18 prerelease". None claims GA is published. The edit is outside the claim's surface, accepted under the role file's rule for a published statement this change falsifies.

  • Dry-run (release.yml version-pr Render, Validate and Restore replayed verbatim in a scratch clone at this head):

    reading result
    fixed group (73) 18.0.0-next.5 → 18.0.0 (73/73 read)
    consumed changesets 1029 (1011 from .changeset/pre/, 18 from .changeset/)
    .changeset/pre.json deleted by the version pass
    Render with this PR alone exit 1 at sync-release-index-currency (the v18 index status is prose)
    Validate with this PR alone red, check:release-page-status (the v18 page says no GA yet)
    Render / Validate / Restore with stand-in GA prose 0 / green (15/15) / 0
  • Mode-keyed gates on the exited tree.

    • check-changeset-no-major green on this PR. It re-arms: a new major is exit 1 after the exit and exit 0 in pre mode, measured in the scratch clone.
    • release.yml's pending count reads 1029 in exit mode, and Restore re-reads the mode from the restored tree.
    • Major-boundary classes do not move: PROTOCOL_VERSION is already 18.0.0, and the templates are already at ^18.
    • cut-rc.yml is unchanged (it requires mode: pre with tag rc). The hotcrm block is no longer keyed on pre.json, so the card's premise there was stale.
    • release-pending-publish, check-docs-image-tag, sync-docs-image-tags (9 pins 17.7.0 → 18.0.0 in the dry-run), check-published-files and check-adr-0087-registration are all green.
  • Gates, pinned to head abec338235. 44 check-runs, all completed: 33 success, 11 skipped. node scripts/pm/check-expected-skips.mjs --pr 24555 → VERDICT check-expected-skips: OK — 11 skipped check-run(s), every one in the roster (exit 0). The dev's local union: 54 derived families, 54 run, 0 NOT-MEASURED.

  • The GA blocker the dry-run found, carded and dispatched. docs(releases): the v18 GA prose — the release index and the v18 page state 18.0.0 released, so the GA Version Packages PR renders #24569 is the docs-only PR for the two release-owned passages (option A of the dev's open question, taken by this seat). It also stays draft, and merges right after this one.

  • Noted for the maintainer, not measured: Version Packages PR chore: version packages #24524 stands at 18.0.0-next.6.

⛔ Not readied or queued by this seat: the merge is the maintainer's GA timing call.


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review October 19, 2026 14:27
@os-zhuang
os-zhuang enabled auto-merge October 19, 2026 14:27
@os-zhuang
os-zhuang added this pull request to the merge queue Oct 19, 2026
Merged via the queue into main with commit c46b5f1 Oct 19, 2026
52 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-24528-changesets-pre-exit branch October 19, 2026 14:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore(release): exit Changesets pre mode (next), so the next Version Packages PR versions 18.0.0 GA

3 participants