Skip to content

Commit a87d8be

Browse files
chore(release): enter Changesets pre mode (next) with one major marker, so v18 opens at 18.0.0-next.0 (#22084)
Fixes #22080 Clause-②: no Opens the v18 line on `main`, as ruled B on #22050 (record `6037890422`). Changesets enters pre mode with the tag `next`, and one `major` changeset takes the `fixed` group to 18, so the first version cut is `18.0.0-next.0`. File surface: `.changeset/` plus one line in `scripts/check-adr-0087-registration.mjs` (the seat's surface amendment `6040954045` on #22080; see the patch round below). ⛔ No edit to `.changeset/config.json`, to either changeset guard's logic, or to `.github/workflows/release.yml`. ⛔ No `changeset version` output is committed, and nothing was published. ## The change 1. **`.changeset/pre.json`**, written by `pnpm changeset pre enter next` (`@changesets/cli` 3.0.3) and committed as the tool wrote it (commit `d3d35a1029`): ```json { "mode": "pre", "tag": "next" } ``` Under v3 the file holds `mode` and `tag` only. There is no `initialVersions` and no `changesets` list. 2. **`.changeset/22080-v18-line-opens.md`**, front matter `"@objectstack/spec": major` (commit `52f7a509c6`). Its body is the v18 line's opening note (ADR-0131; #22050). It carries `Clause-②: no` and its ADR-0087 disposition (see Gates). - **Step 2 was not skipped.** The card's count command, re-run on the cut `3d9188502e`, prints `17 : minor` and `32 : patch`, and no `major`. No `major` was pending, so without this marker pre mode would version `17.8.0-next.0`. - **Why `@objectstack/spec`.** Any `fixed`-group member moves all 69 to the same version, so the choice decides only where the note lands and what the major means. `@objectstack/spec` is the protocol package. `scripts/sync-protocol-version.mjs`, which the root `version` script runs, derives `PROTOCOL_VERSION` from this package's major, and ADR-0131 C8 calls the line "protocol 18". So the major sits on the package whose major is the protocol version, and the note lands where an upgrading reader looks first: `packages/spec/CHANGELOG.md`, under `### Major Changes`. ## The no-major guard reads `pre.json` from the checkout it runs in (the head), so steps 1 and 2 ship together - `scripts/check-changeset-no-major.mjs:396` sets `REPO_ROOT` to the checkout the script runs in. `readPre(root)` (`:2018`) reads `join(root, '.changeset', 'pre.json')` from that working tree, `:2098` hands the result to `judge`, and `judge` returns `exempt` when `pre?.mode === 'pre'` (`:662`). `--base` sets only where the diff starts. It is never where `pre.json` is read. - In CI, the `Check Changeset` job in `.github/workflows/pr-automation.yml` checks out the PR merge ref (the default for a `pull_request` event) and runs `node scripts/check-changeset-no-major.mjs --base "$MERGE_BASE"` (`:1130`). The merge ref carries this PR's `pre.json`. - Both legs were measured at `52f7a509c6`: - **As committed:** exit 0, `✓ Changesets is in pre-release mode (tag: next) — ... skipping the no-major guard.`, plus a notice naming `@objectstack/spec`. - **Control:** with `.changeset/pre.json` deleted from the working tree only, exit 1, `⛔ This PR introduces changeset(s) that declare a major bump.` The base `3d9188502e` has no `pre.json` at all, so a guard that read the base could never have answered `exempt`. Restored with `git checkout HEAD -- .changeset/pre.json`: `git diff HEAD` is empty, and the guard exits 0 again. - So no split is needed, and this PR carries steps 1 and 2. ## Pins | Pin | Reading | |---|---| | In a throwaway worktree, never committed, `changeset version` puts the `fixed` group at `18.0.0-next.0` | Detached throwaway worktree at `52f7a509c6`: `pnpm changeset version` exits 0. All 69 `fixed`-group members read `18.0.0-next.0`, and `pre.json` is unchanged. The 37 consumed changesets (36 pending plus this one) moved to `.changeset/pre/`. `packages/spec/CHANGELOG.md` opens `## 18.0.0-next.0` / `### Major Changes` with this note. The worktree was then removed. | | `check-changeset-no-major` and `check-changeset-fixed` pass on the PR | `node scripts/check-changeset-no-major.mjs --base origin/main` exits 0 on the pre-release exemption above. Driven with a `pull_request` payload carrying this body (`--event`), it also exits 0 with the level axis read. `node scripts/check-changeset-fixed.mjs` exits 0: `✓ .changeset/config.json "fixed" group is in sync with 69 public workspace packages.` | | `.changeset/pre.json` is present at the merge, with `"mode": "pre"` and `"tag": "next"` | Added in `d3d35a1029` with the content above. No later commit touches it. It is in the merge ref CI checks out, which is the file the guard's exemption reads. | ## `release.yml`: in pre mode, `changeset publish` publishes under `next`, and `latest` stays 17.7.0 This is a reading of the file. Nothing was edited, and no publish was run. - `.github/workflows/release.yml:1762` is job `publish`, step `Publish to npm + push version tags`. At `:1772` it runs `pnpm run release`. - The root `package.json` `release` script is `pnpm run build && bash scripts/build-console.sh && bash scripts/release-publish.sh`. - `scripts/release-publish.sh:64` runs `changeset publish` with no `--tag`. - In `@changesets/cli` 3.0.3, `dist/getPublishPlan.mjs:599-603` `getReleaseTag` works like this: with no `--tag` and a pre state present, the tag is `preState.tag`, which is `next`. The one exception is a package whose every published version is already a `next` prerelease. Separately, `dist/publish.mjs:61-63` refuses a custom `--tag` in pre mode. - `npm view` read today: all 69 `fixed`-group packages have `latest` = `17.7.0` and no `next` tag. So each publishes to `next`, and `latest` stays `17.7.0`. ## Gates (run on `52f7a509c6`, exit codes captured before any pipe) `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derives 20 commands for this diff. All 20 exit 0, and `--ran` reconciles: `20 derived, 20 run, 0 NOT-MEASURED, 0 UNRUN`. **Patch round, head `a630de657d` (seat-appended from addendum `6041313497`).** At `52f7a509c6` the required `Lint & Repo Gates` was red at `PM dispatch-gates self-test`, case `the extension narrowing costs no lead … (refused: .changeset/pre)`: with `pre.json` tracked, the battery's loose and strict hint rules disagreed on the literal `.changeset/pre/` at `scripts/check-adr-0087-registration.mjs:536`. That line now names the existing exclusion as `CONSUMED_PRERELEASE_EXCLUDED` (the predicate's match is unchanged), which the extractor reads as an excluded surface. At `a630de657d`: 41 derived commands, 41 run, all exit 0; `pnpm check:pm-dispatch-gates` → `1976 cases pass` (965.7 s), the case reads `(refused: none)`; `check-adr-0087-registration --self-test` 441 assertions and `--base origin/main` exit 0. - **Changeset gates:** - `check-adr-0087-registration --base origin/main` exits 0. The gate requires a disposition for a `major`: a probe commit without one was refused, exit 1, with `declares a breaking change (major) but no adr-0087: disposition marker`. The probe was never pushed. The changeset now carries the `not-required (no-migration-prescription)` marker in the ADR-0087 HTML-comment form, because the marker moves no authorable key, export, type or stored shape. - `check-changeset-no-major --base origin/main` and `check-empty-changeset --base origin/main` exit 0. - The self-tests of all three, plus `check:changeset-gate-self-tests`, exit 0. - **Remaining derived commands, all exit 0:** `check-closing-keyword-parity` and its self-test, `check-comment-mask-corpus`, `release-rehearsal-clone --self-test`, `release-pending-publish --self-test`, `check:driver-memory-census`, `check:gitlink-declared`, `check:nul-bytes`, `check:objectui-changeset`, `check:pm-changeset-deadline-census`, `check:published-files`, `check:refd-timer-probe` and `check:watch-hint-literal`. - **Run beyond the derivation, all exit 0:** `check-changeset-fixed` (an artifact roster under `.changeset/`), `check:future-spec-major`, `check:lockstep-package-count` and `check:docs-image-tag`. - **NOT MEASURED:** the four type-check lanes. The diff touches no TypeScript. ## Read before merging: after this lands, the version-PR refresh goes red until its major-boundary gates are wired The card expects changesets/action to refresh #21988 into the `18.0.0-next.0` version PR once this lands. On the files as they stand, it will not. - The `version-pr` job in `release.yml` (cron `0 */6 * * *`) runs `pnpm run version` in the step `Render the post-version tree` (`:450`). The step `Validate the post-version tree` (`:518`) then exits 1 at `:610-613` when the pass wrote any major-boundary path: "this version pass crossed a MAJOR boundary ... Wire them in before letting this refresh through." The step `Create or update the "chore: version packages" PR` has no `if:`, so it does not run after that failure. - I measured this in the same throwaway. After `changeset version`, the other three rewriters of the root `version` script exit 0. Replaying the step's shape assertion gives 0 unexpected paths and 4 major-only paths: `packages/spec/src/kernel/protocol-version.ts` (`PROTOCOL_VERSION 17.0.0` to `18.0.0`) and the blank template's `objectstack.config.ts`, `objectstack.manifest.json` and `package.json`. - The other lane, `cut-rc.yml`, refuses a pre tag other than `rc` (`:232-237`) and a version not shaped `X.Y.Z-rc.N` (`:156-159`). The ruled tag is `next`. - What this means: once this lands, no lane in this repository cuts `18.0.0-next.0` without a workflow change. That change is outside this card, which forbids any `release.yml` edit. The red is the lane's own designed refusal. It publishes nothing and queues nothing. #21988 stays at its current 17.8.0 content and, as the card says, ⛔ is not merged. ## Acceptance notes - The same throwaway `pnpm run version` shows two more things that the lane wiring has to account for. - `scripts/sync-release-index-currency.mjs` re-dates the v17 entry in `content/docs/releases/index.mdx` from `17.7.0, released 2026-10-06` to `released 2026-10-07`. That is the prerelease cut's date. 17.7.0's version commit `4e4e881427` is dated 2026-10-06. Reported to the seat as a finding. - `scripts/sync-docs-image-tags.mjs` moves the 9 pins in `docker/README.md`, `content/docs/deployment/self-hosting.mdx` and `content/docs/upgrading.mdx` from `17.7.0` to `18.0.0-next.0`. The self-hosting docs on `main` would then name a prerelease while `latest` is 17.7.0. Noted only. - The opening note says the dist-tag `latest` stays on 17.x until GA. The pre-mode publish path above is what makes that true. ## 维护者速读(草稿) **改了什么**:在 `main` 上进入 Changesets 预发布模式(标签 `next`),并加一条把 `@objectstack/spec` 标为 `major` 的 changeset。合并后,下一次切版本得到的是 `18.0.0-next.0`,而不是 `17.8.0`。改动是 `.changeset/` 下的两个文件,加上门禁脚本里的一行重命名:`scripts/check-adr-0087-registration.mjs` 把已有的排除路径 `.changeset/pre/` 写成 `_EXCLUDED` 命名常量(匹配逻辑不变),这样 `pre.json` 存在后 PM 门禁自检能正确识别它。没有代码或工作流改动。 **为什么改**:按 #22050 的裁决 B,v18 直接在 `main` 上开发。v18 的破坏性改动要以 `18.0.0-next.N` 预发布版发到 npm 的 `next` 标签上;`latest` 保持 17.7.0,普通安装的用户不受影响,直到 18.0 正式版。 **风险与代价(含回滚)**:合并后,版本 PR 的自动刷新(`release.yml` 的 `version-pr`,每 6 小时一次)会在 "Validate the post-version tree" 一步变红。原因是这一版跨了大版本,会改写 `PROTOCOL_VERSION` 和模板文件,而这条通道对这些文件还没有门禁,按设计会拒绝。它不发布,也不排队任何东西,但在另开卡把这些门禁接上之前,#21988 不会变成 `18.0.0-next.0`。`cut-rc.yml` 只接受 `rc` 标签,也走不通。#21988 无论如何都不要合并。回滚办法:删除 `.changeset/pre.json` 和 `.changeset/22080-v18-line-opens.md` 两个文件(在切出任何预发布版之前,这一步没有副作用)。 **席位意见**: **你要做的**:确认后合并本 PR。合并后需要另开一张卡,让版本通道能切出 `18.0.0-next.0`;这张卡由席位提出。 --- _Generated by [Claude Code](https://claude.ai/code/session_01VF48aw8RPG6wzDnMgp6rtw)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 498ea50 commit a87d8be

3 files changed

Lines changed: 20 additions & 1 deletion

File tree

‎.changeset/22080-v18-line-opens.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
"@objectstack/spec": major
3+
---
4+
5+
The v18 line opens (ADR-0131; #22050). Every release from here is an `18.0.0-next.N` prerelease on the npm dist-tag `next`, until 18.0 GA.
6+
7+
Clause-②: no
8+
9+
- **What this marks.** Changesets is in pre mode with the tag `next`. Every `@objectstack/*` package versions together, so this one `major` takes all of them to 18, and the first version cut is `18.0.0-next.0`. `PROTOCOL_VERSION` follows `@objectstack/spec`'s major at version time, so it reads `18.0.0` from that prerelease on. The marker itself changes no code, key, export or stored shape.
10+
- **What the line carries.** ADR-0131 (organization ownership is total: no NULL `organization_id`) lands on this line in stages. Each stage that breaks something says so in its own changeset, with its own migration.
11+
- **Who receives it.** A plain install still resolves the dist-tag `latest`, which stays on 17.x until 18.0 GA. A prerelease is installed only on request: `@objectstack/spec@next`, or an exact `18.0.0-next.N`.
12+
- **What ends it.** 18.0 GA exits pre mode and publishes to `latest`.
13+
14+
<!-- adr-0087: not-required (no-migration-prescription) the v18 line's opening marker: Changesets pre mode plus one major on the fixed group, so the train versions 18.0.0-next.N. No authorable key, spelling, export, type or stored shape moves in this diff, and no stored row is read, rewritten or converted; each breaking stage of the line states its own disposition in its own changeset. The other categories are closed on facts: the package publishes (not unpublished); no ADR-0087 id is named or touched (not registered or already-registered); and no TypeScript declaration moves (not runtime-interface-only or type-surface-only). -->

‎.changeset/pre.json‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"mode": "pre",
3+
"tag": "next"
4+
}

‎scripts/check-adr-0087-registration.mjs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,8 @@ const isChangesetFile = (p) => p.startsWith('.changeset/') && p.endsWith('.md')
533533
* audit surfaces is a readability fix; filtering the verdict would be a bypass,
534534
* and `PRE2` in the self-test pins the difference.
535535
*/
536-
const isConsumedPrerelease = (p) => p.startsWith('.changeset/pre/');
536+
const CONSUMED_PRERELEASE_EXCLUDED = '.changeset/pre/';
537+
const isConsumedPrerelease = (p) => p.startsWith(CONSUMED_PRERELEASE_EXCLUDED);
537538

538539
/**
539540
* Split a changeset into its frontmatter bump entries and its body.

0 commit comments

Comments
 (0)