Skip to content

Migrate the release toolchain to @changesets/cli v3 — one atomic PR carrying the bump, the pre-mode restructure, and the gates that model v2's semantics #9465

Description

@os-project-manager

Epic — coordination node. Never dispatched itself; the work lives in its sub-issues.

Dependabot #9217 proposes @changesets/cli 2.31.1 → 3.0.0. It is a devDependency bump on paper and a release-machinery migration in fact. The maintainer decided on 2026-08-17 to do it deliberately rather than take or drop the raw bump, and on 2026-08-18 to run it as an epic.

Delegation

  • Epic PM session: session_01HDA9nN6nXQngoQUAAzRdMb
  • Declared file territory (other lanes: treat these as claimed for the duration; this epic does not claim anything else):
    .changeset/** · scripts/check-changeset-no-major.mjs · scripts/check-empty-changeset.mjs · scripts/check-changeset-fixed.mjs · scripts/objectui-changeset-digest.mjs · .github/workflows/cut-rc.yml · .github/workflows/release.yml · .github/workflows/pr-automation.yml (changeset-related steps only) · root package.json (the @changesets/cli range and the version script)
  • Nothing here touches packages/spec. If a sub-issue turns out to, it goes to the domain:spec seat rather than staying in this subtree.

Why do it — benefits are repo-specific, not generic

  1. v3 removes the root cause of our RC-window pollution. Under v2, pre-mode changeset version does not delete the changesets it consumes; they pile up in .changeset/. Measured on the last window: 1552 changeset files, 1279 consumed-but-undeleted. That pollution is why check-changeset-no-major.mjs carries ~88 lines of pre/RC exemption reasoning, whose own header records the absurd consequence — a typo-sized PR shown 171 major-declaring files it never touched, with allow-major as its only exit, a label whose message then means something false. v3 (changesets#2190) moves consumed prerelease changesets into .changeset/pre/.
  2. v3 makes a no-op changeset version exit 1 (changesets#1860) instead of exiting 0 silently. We have paid for that failure mode: 空 changeset 会静默卡死已 version 的发布:Release run 全绿,但 npm 和 Docker 什么都没发(17.0.0-rc.2 现在就卡着) #4898 stalled 17.0.0-rc.2 while every check stayed green.
  3. A chance to drop ___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH.onlyUpdatePeerDependentsWhenOutOfRange — a key whose own name is upstream reserving the right to change it in a patch release.
  4. Maintenance line: 2.x gets fixes only; our .changeset/config.json$schema is still pinned at @changesets/config@3.1.2.

Explicitly not worth effort: changeset taggit-tag (never invoked here), prettierformat (unset), Yarn Classic / Bolt removal, the @clack/prompts interactive rewrite.

Rulings that bind every sub-issue

  1. The bump and the migration land as ONE PR. Both half-states are broken and each is broken silently until the next release: bump alone leaves v3 installed against v2-shaped gates; migration alone leaves gates expecting .changeset/pre/ that v2 never creates.
  2. Measurement outranks the changelog. No sub-issue may act on "v3 is documented to…" where it could instead run the thing and read the output.
  3. The allowlist follows the measurement, never the reverse. If a v3 changeset version produces a file set cut-rc.yml would reject, the allowlist changes — the tool's output is not massaged to fit a list written under v2.
  4. A self-test still asserting v2 semantics is a failed run, not a passing one.
  5. Perform no release. No changeset publish, no version tags, no release-workflow dispatch, no merging of a Version Packages PR, and no pre-mode entry or exit on origin. Releases are human-only in this repo and this epic does not change that.

Timing

.changeset/pre.json is absent as of 2026-08-17: not in pre mode, 143 changesets in the root rather than 1552. That makes now the cheapest window for the migration itself. Each sub-issue re-verifies this at start; if a cut has begun, the migration reschedules rather than forcing.

Sub-issues

#CardState
1/4#9497 — rehearse v3 in a throwaway clone, answer U1–U6 with measurements, no repo changesqueued, dispatched first
2/4#9498 — land the migration as one atomic PRblocked by #9497
3/4#9499 — decide #9208 (changesets/action v1 → v2) against release.yml's pinned rationale (#6170)blocked by #9497
4/4#9500 — verify the migrated release path inside the next real RC windowblocked by #9498; Restart-when: the next pre-mode entry

The six unknowns (U1–U6) are stated in full on #9497, which is the only card allowed to answer them.

Downstream

#9217 closes itself once main carries ^3.0.0. #9208 is held and resolved by #9499 — two open PRs mutating one release machine is the shape this epic exists to prevent.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions