Skip to content

Commit 014b968

Browse files
ci(release): retire the rc channel — delete cut-rc.yml, remove the retired rc dist-tag (#26394)
<!-- claude-projects-attribution: {"github_login":"os-steve"} --> _Requested by **Steve** · [project thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi9WrbfurU16VFMgPKTWCi1GD)_ Fixes #26196 Clause-②: no Metadata-protocol: none ## 维护者速读 - **改了什么**:删掉 Cut RC 工作流(`cut-rc.yml`),发布通道只剩 `latest`(正式版)和 `next`(预发布);发布后整理 npm 标签的脚本对 `rc` 改为删除而不是挪动;文档和脚本注释里指向 cut-rc 的说法全部改掉。 - **为什么改**:维护者裁定「现在只有 latest 和 next」。cut-rc 只能发 `rc`,留着会让人和 AI 以为 `rc` 通道还活着;npm 上 68 个包至今还挂着 `rc: 17.0.0-rc.6`。 - **风险与回滚**:正式发布流程(`release.yml`)只改了注释,行为不变;变化在下一次正式版发布后的标签整理步骤:它会删掉 `rc` 标签(该步骤本来就是非阻塞的)。回滚 = revert 本 PR。 - **先看**:① `scripts/release-dist-tags.mjs` 的 `RETIRED_TAGS` 与 `planTags` 删除分支;② `docs/releases-maintenance.md` 新的「Release channels」节;③ `scripts/check-changeset-no-major.mjs` 改写的 wiring 断言(原来要求 cut-rc.yml 存在)。 - **两分钟验证**:`node scripts/release-dist-tags.mjs`(只读计划,不写 npm),预期末行 `73 tag(s) would move, 68 retired tag(s) would be removed … 0 failure(s)`。 - **需要你做的**:这是受管面(`.github/workflows/**`)改动,需要 GOVERNED_APPROVERS(os-zhuang、hotlong)批准后落地;PR 保持 draft,席位不转 ready、不开自动合并。 ## What changes 1. **`.github/workflows/cut-rc.yml` is deleted.** It could only cut `X.Y.Z-rc.N` and refused to run unless `.changeset/pre.json` said `tag: rc`. 2. **Channel rule, stated once** in `docs/releases-maintenance.md` under a new `### Release channels`: `latest` = stable, `next` = pre-release (`release.yml` in pre mode, `.changeset/pre.json` tag `next`, published by `changeset publish` under that tag), no `rc`. The two-route table and the whole "Cutting an rc — the snapshot flow" section (bypass-list prerequisite, stale #6208 note, runtime-image note) are removed; the ADR-0125 environment-gate paragraph is kept and reworded for the one lane. Two other lines that named `cut-rc.yml` (Console build, `fetch-depth: 0`) and one "snapshot flow above" back-reference are reworded. 3. **`scripts/release-dist-tags.mjs`** gains `RETIRED_TAGS = ['rc']` (exported, frozen). `planTags` now returns `{ moves, removes, kept }`: a retired tag is removed whatever it points at (a stale, an ahead, a stable or a non-semver value), checked before any version test; every other tag keeps today's rule, so `next` still moves. `sweep`/`main` take a `remove` writer (`npmDistTagRm` = `npm dist-tag rm NAME TAG`); plan mode prints `plan NAME rc: VERSION → removed (retired channel)` and the summary counts removals separately. A pre-release `RELEASE_VERSION` still does nothing (the header says a retired tag goes at the next stable publish). The `release.yml` step name and wiring are unchanged. 4. **`scripts/check-changeset-no-major.mjs`**: the wiring battery asserted that `cut-rc.yml` exists and calls this script twice with no `--event`. Those 4 assertions are replaced by 4 over **every** workflow: the walk lists workflow files; control: it finds the real scan in `pr-automation.yml`; every call site is in `pr-automation.yml`; every workflow that runs the script has a `pull_request` trigger. So a new non-PR caller cannot silently land in the `no-pull-request` stand-down lane. The `no-pull-request` comment, verdict-table line and its runtime message now name a local run as the only caller (the message is not pinned by any test: `git grep` of the old and new text). 5. **Comment-only rewording** (no behaviour), so `git grep cut-rc` stops pointing at a deleted lane: `lint.yml` (the `jq` note), `release.yml` (3 comments: the allowlist twin sentence, the one-list principle, the five-filter note), and in `scripts/`: `check-docs-image-tag.mjs`, `check-self-test-wired.mjs` (header + one control comment), `check-skill-compatibility-version.mjs`, `docs-audit/README.md`, `pm/clause2-line.mjs`, `pm/dispatch-gates.self-test.mjs`, `pm/release-rehearsal-clone.mjs`, `release-spec-changes.sh`, `sync-release-index-currency.mjs` (4 comments + one self-test case title), `sync-template-versions.mjs`. These 11 files were added to the claim's file surface by the seat before editing. 6. **`scripts/release-verify-npm.mjs`, comment only** (from the seat's check of #26376, which set `release.yml`'s publish job to `timeout-minutes: 120`): the header said the publish job "declares no `timeout-minutes`, so GitHub's 360-minute job default bounds this step"; it now names the 120-minute bound and that raising the 35-minute settle budget means re-checking that sum. Applied after merging `origin/main` (756220c, which carries #26376 as fa5d72e) as the seat's prepared commit; `node scripts/release-verify-npm.mjs --self-test` → exit 0, `131 cases pass across 14 batteries`. ## Why The maintainer, in the triage seat thread: 「现在只有 latest 和 next」, then 「同意」 to retiring the rc lane. A lane that can only cut a channel that no longer exists tells every reader, human or AI, that `rc` is live; the registry still serves `rc: 17.0.0-rc.6`, and the dist-tag sweep after each stable publish would keep moving it forward instead of letting it go. ## Tests - `node scripts/release-dist-tags.mjs --self-test` → exit 0: `OK release-dist-tags self-test: 54 cases pass across 7 batteries`. New battery 7 "Retired channels: removed, never moved, and only those" (9 cases, floor 9; roster floor 6 → 7): the list is exactly `rc`; today's registry shape gives `rc` = remove and `next` = move; a retired tag on a stable, an ahead and a non-semver value is removed; `next` is never retired; control: with an empty list `rc` moves again; the plan line names the removal; `--apply` removes `rc` through the remover and never `add`s it. Batteries 1–5 updated: every `sweep`/`main` call that could reach a removal passes a stub remover, so the self-test never spawns a real `npm`; the end-to-end battery's stub `npm` on PATH records `dist-tag add @objectstack/cli@18.1.0 next`, `dist-tag rm @objectstack/cli rc`, `dist-tag rm create-objectstack rc`. - `node scripts/check-changeset-no-major.mjs --self-test` → exit 0, 456 assertions (wiring battery stays at its 22 floor: 4 removed, 4 added). ## Acceptance notes - **Card acceptance 1:** `git grep -n 'cut-rc' -- .github scripts docs/releases-maintenance.md` on the head returns one line: `scripts/pm/check-half-states.mjs:26115`, the recorded title of card #9828 inside a fixture roster. It is history and stays. `docs/releases-maintenance.md` names "the Cut RC lane" once, in the channel section, as the retired lane. - **Card acceptance 2 (plan, read-only):** `node scripts/release-dist-tags.mjs` against registry.npmjs.org, anonymous GETs only, nothing written: `Planning (read-only; pass --apply to write) the dist-tags of 111 package(s)` → `73 tag(s) would move, 68 retired tag(s) would be removed, 0 kept ahead of latest, 0 package(s) skipped, 0 failure(s).` Every move is `next: 18.0.0-next.5 → 18.1.0`, every removal is `rc: 17.0.0-rc.6 → removed (retired channel)`. The card says 73 packages carry `rc`; measured today it is 68: `@objectstack/connector-imap`, `organizations`, `service-mailbox`, `service-print` and `skills` carry `next` but no `rc`. Not run with `--apply` (AGENTS.md directive 15); #25764's one-time sweep is not touched. - **Card acceptance 3:** every script under `scripts/` that names `release.yml` or `lint.yml` and has a `--self-test` (49 files) was run with `--self-test` on 088f95b: 48 exit 0; `scripts/pm/dispatch-gates.mjs --self-test` hit the loop's 280 s cap (exit 124) and is covered by `pnpm check:pm-dispatch-gates` below, which runs it. On 2b9dd3d the derived set re-ran the ones it names, and `release-verify-npm.mjs --self-test` passed after the merge (131 cases). - Left as written, outside the acceptance grep (comments only): `packages/cli/test/scaffold-exact-objectstack-pin.test.ts:12`, `packages/create-objectstack/src/runtime-image.test.ts:237`, `template-consistency.test.ts:799`, `template-version-stamps.test.ts:11,51,274,338,387`. ADRs and `docs/audits/**` are history and unchanged. - `scripts/release-spec-changes.sh --generate` had one workflow caller, `cut-rc.yml`; after this PR no workflow calls that mode (`release.yml` uses `--prepare`/`--verify`/`--attach`). Observation only. - `check-changeset-no-major.mjs` still says "RC window" / "RC exemption" for Changesets pre mode in general; that is pre mode, not the deleted lane, and is unchanged. ## Verification `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` on the final head 2b9dd3d (merge base 756220c) derived 81 commands (the 79 of the pre-merge head 088f95b plus `check-adr-links.mjs` and its `--self-test`). All ran locally with stdin from `/dev/null`, exit captured before any pipe; `check:dts-closure` and `check:dual-build-cjs-loads` after `turbo run build --concurrency=2 --filter='./packages/**'` under the verify lock (`VERDICT command-exit 0`, 546 s). `--ran` reconciliation on 2b9dd3d: **81 derived, 81 run, 0 NOT-MEASURED, 0 UNRUN** (a derived zero). The same 79 had also all exited 0 on 088f95b. Every other family is CI's, read from this PR's check-runs. <details><summary>Gate readings</summary> - all 81 derived commands on 2b9dd3d: exit 0, including `pnpm check:changeset-gate-self-tests`, `pnpm check:doc-authoring`, `pnpm check:required-contexts`, `pnpm check:template-version-sync`, `pnpm check:release-index-currency-sync`, `node scripts/check-self-test-wired.mjs` (+ `--self-test`), `node scripts/pm/release-rehearsal-clone.mjs --self-test`, `node scripts/release-dist-tags.mjs --self-test`. - `pnpm check:pm-dispatch-gates` on 2b9dd3d: exit 0, captured by its wrapper (fast tier, 501.3 s on this box). - `node scripts/check-comment-mask-corpus.mjs` first hit the loop's 300 s cap (exit 124) while the build held the box; re-run alone: exit 0, `18308 files, 0 disagree, 0 unparseable, 232.7s`. - `check:dts-closure`: 75 built packages swept, 233/233 declaration files present. `check:dual-build-cjs-loads`: provenance line 112/69/2381/1 over floors 90/58/520/1. </details> ## Human Minimum Each item is one rule from [CONTRIBUTING.md › Human Minimum](https://github.com/objectstack-ai/objectstack/blob/main/CONTRIBUTING.md#human-minimum), which links the AGENTS.md text that states it. - [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic. (`packages/spec` is not touched.) - [x] 2. Every name I added uses the casing for its kind. (`RETIRED_TAGS`, `npmDistTagRm`, `plannedRemovals`.) - [ ] 3. No formatter ran; `pnpm lint` passes. (No formatter ran; `pnpm lint` is CI's run, read from `Lint & Repo Gates` on this PR.) - [x] 4. One issue, one branch off an up-to-date `main`, one pull request. (`origin/main` 756220c merged in.) - [x] 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset. (Nothing publishes; both lines are above.) - [x] 6. `packages/spec` touched: its generated artifacts are regenerated and committed. (Not touched.) - [x] 7. No hand edits to `content/docs/references/`, `packages/*/CHANGELOG.md` or `content/docs/releases/`. - [ ] 8. Title, body and comments are in English. (All English except the 维护者速读 block, which the fleet's dev contract asks for in Chinese.) - [ ] The *Before Submitting* list in CONTRIBUTING.md is done. (Not walked item by item; the gate readings above are what was run.) ## Changeset None: the diff touches workflows, a doc and repo-internal scripts under `scripts/`, which no package publishes. `skip-changeset` is set at creation. --- _Generated by [Claude Code](https://claude.ai/code/session_01ADHkmS6EcuQGiiD6cwnYUT)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 8852099 commit 014b968

17 files changed

Lines changed: 235 additions & 1104 deletions

‎.github/workflows/cut-rc.yml‎

Lines changed: 0 additions & 877 deletions
This file was deleted.

‎.github/workflows/lint.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1810,7 +1810,7 @@ jobs:
18101810
# from a primary checkout, a detached HEAD and a non-repo cwd with
18111811
# identical results, so neither this checkout's depth nor its branch is an
18121812
# input. This step itself needs only `jq` and `git` (both in the runner
1813-
# image; `jq` is already relied on bare by cut-rc.yml and release.yml),
1813+
# image; `jq` is already relied on bare by release.yml),
18141814
# plus whatever each discovered matrix declares in its own header —
18151815
# discovery above is by glob, so a matrix needing more (node, pnpm, a
18161816
# built tree, a generator) must say so there; this comment is not the

‎.github/workflows/release.yml‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -387,8 +387,7 @@ jobs:
387387
# surface, 4 major-only — the three blank-template files and
388388
# protocol-version.ts. The template half is now judged by the gates below;
389389
# protocol-version.ts is still refused, for the measured reason given at
390-
# the refusal. cut-rc.yml's allowlist block carries the same surface for
391-
# the RC lane and agrees path-for-path.
390+
# the refusal.
392391
#
393392
# WHAT IS CHECKED, AND WHY EACH ONE (7 s total, measured on the rendered
394393
# tree in this job's own state: install, NO build. With the major-boundary
@@ -401,8 +400,8 @@ jobs:
401400
# the doc and template halves of that surface are RESOLVED AT RUN TIME
402401
# from the very declarations the rewriters read (`SURFACES` in
403402
# check-docs-image-tag.mjs, `stampedPaths()` in
404-
# sync-template-versions.mjs), on the principle cut-rc.yml states for
405-
# the same two lists: one list, N consumers, because "a fourth literal
403+
# sync-template-versions.mjs), on the principle those two declarations
404+
# exist for: one list, N consumers, because "a fourth literal
406405
# is a fourth contract". This is the half that ends the treadmill: a
407406
# fifth rewriter, or an existing one growing an output, reddens this
408407
# lane on the day it lands instead of shipping unvalidated.
@@ -658,7 +657,7 @@ jobs:
658657
exit 1
659658
fi
660659
661-
# Same five filters cut-rc.yml applies to the same surface: the fixed
660+
# Five filters over the reviewed surface: the fixed
662661
# release paths by pattern, then the four declared lists by WHOLE-LINE
663662
# EXACT match, so no derived filter can accept a path its declaration
664663
# does not name and none needs regex-escaping.

‎docs/releases-maintenance.md‎

Lines changed: 28 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ turbo task `@objectstack/console#build:dist`, which depends on
4747
`@object-ui/console#build` and then runs `packages/console/build-dist.sh`: it copies
4848
`ui/apps/console/dist` into `packages/console/dist`, adds the tracked repo-root
4949
`sdui.manifest.json`, and runs the single-zod canary on the bytes it ships.
50-
`pnpm run release` and `cut-rc.yml` both run it after `pnpm run build`, so a release
51-
ships the Console from the same commit as the spec it builds against.
50+
`pnpm run release` runs it after `pnpm run build`, so a release ships the Console
51+
from the same commit as the spec it builds against.
5252

5353
> objectui's pending changesets from before the merge still sit under
5454
> `ui/.changeset/`, where the root config does not read them. Moving them into the
@@ -183,132 +183,45 @@ looks like a real checkout. Its `--self-test` runs in `lint.yml` and pins both
183183
directions, so the refusal cannot rot into a guard that fires on everything or on
184184
nothing.
185185

186-
**This is a local-rehearsal trap only.** `cut-rc.yml` and `release.yml` check out
187-
with `fetch-depth: 0`, so a real cut has full history and never enters that
188-
branch.
186+
**This is a local-rehearsal trap only.** `release.yml` checks out with
187+
`fetch-depth: 0`, so a real cut has full history and never enters that branch.
189188

190189
## Cutting a release
191190

192-
Two routes, and the choice is not a preference — an rc and a GA release need
193-
different things, so they get different lanes.
191+
### Release channels
194192

195-
| | **rc prerelease** | **GA** |
193+
There are two npm channels, and both are published by `release.yml`:
194+
195+
| dist-tag | Carries | Published by |
196196
|---|---|---|
197-
| Workflow | `.github/workflows/cut-rc.yml` | `.github/workflows/release.yml` |
198-
| Trigger | one `workflow_dispatch` | merge the Version Packages PR, then approve the `release` environment |
199-
| Console | built from `ui/apps/console` at the snapshot | built from `ui/apps/console` at the version commit |
200-
| Changelog review | none (the record is generated) | human review on the PR |
201-
| Board-clearing / #7275-A precondition | not required | required |
202-
203-
Both lanes are gated by `environment: release`, and neither publishes anything until
204-
a human approves the deployment there. What differs since ADR-0125 (2026-08-20) is
205-
the *start*: `cut-rc.yml` is still `workflow_dispatch`-only — an event no push, bot,
206-
merge-queue landing or schedule can synthesise — while `release.yml`'s publish lane
207-
**is** started by the push that lands the Version Packages PR (D1), and is then held,
208-
**whole**, at the environment gate until a required reviewer approves it (D2). The
209-
approval is the release act on that lane, so read ADR-0125 D3 before touching it: an
210-
environment with no required reviewers passes automatically and silently, which on a
211-
push-started lane is not a weakened gate but no gate at all. (`release.yml` also
197+
| `latest` | the stable release (GA) | `release.yml`, outside pre mode — the Version Packages PR flow below |
198+
| `next` | the pre-release (`X.Y.Z-next.N`) | `release.yml` in pre mode: `.changeset/pre.json` says `mode: pre`, `tag: next`, and `changeset publish` publishes under that tag |
199+
200+
There is no `rc` channel. The Cut RC lane that cut `X.Y.Z-rc.N` is retired and its
201+
workflow deleted, and a pre-release cycle enters pre mode with tag `next`, never `rc`.
202+
npm may still carry the `rc` dist-tag from that lane (`17.0.0-rc.6` when the lane was
203+
retired). `scripts/release-dist-tags.mjs` lists `rc` as a retired tag: after
204+
every stable publish it removes that tag (`npm dist-tag rm`) and moves a stale `next`
205+
up to `latest`. Run with no flags, it prints that plan and writes nothing.
206+
207+
`release.yml`'s publish lane is gated by `environment: release`, and it publishes
208+
nothing until a human approves the deployment there. Since ADR-0125 (2026-08-20) the
209+
lane **is** started by the push that lands the Version Packages PR (D1), and is then
210+
held, **whole**, at the environment gate until a required reviewer approves it (D2).
211+
The approval is the release act on that lane, so read ADR-0125 D3 before touching it:
212+
an environment with no required reviewers passes automatically and silently, which on
213+
a push-started lane is not a weakened gate but no gate at all. (`release.yml` also
212214
carries a 6-hourly `schedule`, added by #11233; it reaches only the bookkeeping
213215
`version-pr` job, which has no publish capability by construction.) That is the
214216
2026-08-07 ruling (「版本发布必须是人工的」) re-implemented, not relaxed, and it is not
215217
negotiable in either direction — see `release.yml`'s header for the two releases that
216218
were minted without a human and the lane split that ended it.
217219

218-
### Cutting an rc — the snapshot flow (#7447)
219-
220-
**Actions → Cut RC → Run workflow**, branch `main`, `version` = the version you
221-
expect to come out (e.g. `17.0.0-rc.6`). Run it once with **`dry_run` checked**
222-
first; that stops before anything irreversible and uploads the exact commit a real
223-
run would push, so the first real dispatch is never this workflow's first execution.
224-
225-
What it does, in order:
226-
227-
1. Checks out `main` and records that sha — the snapshot. Nothing downstream
228-
re-reads `main`, so **`main` may keep moving for the whole run**.
229-
2. Builds the workspace, both scopes, then the Console (`pnpm run build`, then
230-
`pnpm run console:build`) from that snapshot. The dist carries the tracked
231-
`sdui.manifest.json`, whose freshness the required `Build & E2E UI` job already
232-
held on the PR that changed it and in the merge queue. A Console that cannot build
233-
stops the cut before anything is written.
234-
3. Runs the gates that read `.changeset/*`, before versioning consumes it.
235-
4. Runs `pnpm run version` — the repo script, never a bare `changeset version` —
236-
and **fails unless the computed version equals the one you typed**.
237-
5. Pushes one squashed version commit to `main`, then publishes **from that landed
238-
commit**. Never the other way round: rc.3 and rc.4 tagged commits that lived only
239-
on `changeset-release/main`, which is #6170.
240-
241-
**Why this lane exists.** The standing Version Packages PR *was* force-refreshed on
242-
every main push, so its CI could not converge while main was busy — and cutting
243-
through it used to mean chasing a moving objectui pin as well (rc.6 was chased across
244-
four pin-bump laps, every one overtaken before its CI finished, and finishing would
245-
have needed ~40 minutes of coordinated freezes across two repos). A snapshot removes
246-
the race instead of asking people to hold still.
247-
248-
⚠️ **The first half of that premise no longer holds** (#11233, 2026-08-23):
249-
`release.yml`'s `version-pr` job moved off `push` onto a 6-hourly `schedule` plus an
250-
on-demand `workflow_dispatch` (`refresh_version_pr`), so between refreshes
251-
`changeset-release/main` is static and its CI does converge. This section is left
252-
standing because the *snapshot* lane was never only about that, and `cut-rc` is still
253-
the rc route. What it does mean is that "the
254-
version PR can never converge" is no longer a reason to reach for it.
255-
256-
**One-time admin prerequisite.** The lane pushes the version commit straight to
257-
`main`, so the pushing identity must be on main's ruleset **bypass** list —
258-
`contents: write` alone does not put it there. Either add the GitHub Actions app to
259-
the bypass list (ideally scoped to this workflow), or store a fine-grained PAT for an
260-
account already on that list as the repository secret `RELEASE_PUSH_TOKEN`. The
261-
workflow uses the secret when present and the Actions identity otherwise, so
262-
configuring either one is enough and neither needs a workflow edit. Until one is
263-
configured the push step fails with that message and **nothing is published**.
264-
265-
**What happens to the standing Version Packages PR.** After a cut, the standing
266-
`chore: version packages (rc)` PR ([#6208](https://github.com/objectstack-ai/objectstack/pull/6208),
267-
branch `changeset-release/main`) comes to rest **stale**, showing an
268-
already-consumed version bump, until the next refresh of the version-PR lane. Since
269-
#11233 that refresh is the 6-hourly `schedule` (or one `workflow_dispatch` with
270-
`refresh_version_pr`, if you want it now) — **not** the cut's own push, under either
271-
push credential, because `version-pr` no longer runs on `push` at all.
272-
273-
The push credential still decides whether the cut's push fires `release.yml` at all,
274-
which matters for the `release-integrity` lane below — so it is written down here
275-
rather than rediscovered at 2am:
276-
277-
- **Route (a), the Actions identity.** GitHub does not create workflow runs from
278-
events triggered by the automatic `GITHUB_TOKEN` — the documented recursion guard,
279-
whose only exceptions are `workflow_dispatch` and `repository_dispatch`. So the
280-
version-commit push does **not** fire `release.yml`'s `push` lane at all. This repo
281-
already depends on that guard elsewhere and says so: see `docker-publish.yml`'s
282-
header, which explains that a `push: tags:` trigger "would never fire" because the
283-
release workflow pushes its tags with `GITHUB_TOKEN`.
284-
- **Route (b), a PAT in `RELEASE_PUSH_TOKEN`.** A PAT is not the `GITHUB_TOKEN`, so
285-
the push triggers normally and `release-integrity` audits the pushed commit.
286-
287-
**A stale #6208 after an rc cut is EXPECTED AND HARMLESS — do not "fix" it by hand.**
288-
Its changesets were consumed by the cut and MOVED into `.changeset/pre/` — under
289-
`@changesets/cli` v3 that move is what "consumed" looks like on disk, and
290-
`.changeset/pre.json` carries `{"mode","tag"}` and nothing else, so it is not a
291-
record of what was consumed (it was, under v2). The PR is bookkeeping, it carries no
292-
publish capability by construction (`release.yml` passes the changesets action no
293-
`publish:` script), and it regenerates correctly at the next scheduled refresh (at
294-
most six hours; sooner if you dispatch one) or the next GA cut. Editing or
295-
force-refreshing it manually only risks putting a version commit somewhere the
296-
publish lane can reach.
297-
298-
**The runtime image is not built here.** `release.yml`'s `release-integrity` lane runs
299-
on every push to `main` and requests the image once the version is on npm, so it
300-
follows within the next merge or two. Note that this is a *later* push under **both**
301-
routes, not the cut's own: on route (a) the cut's push triggers nothing at all, and on
302-
route (b) it does trigger, but `release-integrity` runs its audit long before the
303-
publish step further down the cut has put the version on npm — so it reports "release
304-
pending a human" and exits green either way. For an image immediately, dispatch
305-
`docker-publish.yml` with the version.
306-
307220
### Cutting a GA release — the Version Packages PR flow
308221

309222
Unchanged, and everything that makes a GA release a *judgement* stays here: the
310223
board-clearing pass, the #7275-A cut precondition, and the human review of the
311-
generated changelogs on the PR. Like an rc, a GA release ships the Console built from
224+
generated changelogs on the PR. A GA release ships the Console built from
312225
`ui/apps/console` at the commit it releases. Merge the `chore: version
313226
packages` PR (#4935); that push is what starts the publish lane, and the run then
314227
waits at `environment: release` for a required reviewer to approve a job named
@@ -317,7 +230,6 @@ never one you supply (ADR-0125 D1/D2). ⛔ **Actions → Release → Run workflo
317230
**not** the GA publish path: since ADR-0125 D4 that dispatch takes no version input at
318231
all, and left unchecked it starts the repair lane for a publish that died partway —
319232
checked, it starts the bookkeeping lane described next.
320-
`release.yml`'s three lanes are untouched by the rc lane.
321233

322234
**First, check #4935 is current** (#11233). It is regenerated on a 6-hourly schedule
323235
rather than on every push, so at cut time it can be up to one refresh window behind
@@ -370,8 +282,8 @@ later tree and the gate rightly ignores it.
370282

371283
**This is a wait, not a merge freeze.** `main` keeps moving the whole time: the
372284
approval publishes the version commit's tree and nothing that landed after it, so
373-
runs on later commits are not what you are waiting for. The snapshot flow above
374-
chose against coordinated freezes, and a Version Packages PR merged behind `main` is
285+
runs on later commits are not what you are waiting for. The release lanes chose
286+
against coordinated freezes, and a Version Packages PR merged behind `main` is
375287
already handled by `annotate-shipped` (#23818); neither changes.
376288

377289
**After a stable `@objectstack/spec` publish, refresh the retired-after census** (#20390): run `pnpm --filter @objectstack/spec exec tsx scripts/build-retired-after-census.ts` (prefix `NODE_USE_ENV_PROXY=1` behind a proxy) and commit the rewritten `packages/spec/src/conversions/retired-after.census.json` in an ordinary PR — until it lands, the census test holds an unpublished entry's `retiredAfter` only to the range from the last censused release to the label, not to the label exactly.

0 commit comments

Comments
 (0)