@@ -8,9 +8,9 @@ release gate.
88## 1. Pre-release checklist (every release)
99
1010Start from a clean checkout that contains current ` origin/main ` . Do not release
11- from a detached HEAD or from a moving ` main ` branch. The staged pipeline records
12- the candidate branch, commit SHA, and build run, then refuses to approve or tag
13- if any of them changes .
11+ from a detached HEAD or from a moving ` main ` branch. The npm-first pipeline
12+ pins the candidate branch and commit SHA, retains the exact npm tarballs, and
13+ refuses to tag if the branch moves or any public registry shasum differs .
1414
1515``` bash
1616# Confirm the checkout is current and clean.
@@ -61,27 +61,31 @@ gh workflow run test.yml --ref "release/v$VERSION" -f tier=full
6161gh workflow run simctl-tests.yml --ref " release/v$VERSION "
6262```
6363
64- Then use the staged pipeline:
65-
66- The local publish/approve process requires npm 11.17 or newer and a valid
67- ` SOCKET_API_TOKEN ` for the mandatory tarball scan. CI uses OIDC for staging;
68- do not set a long-lived npm publish token. The local npm account must be a
69- maintainer of all nine packages because it lists and approves their staged
70- entries.
71-
72- ``` bash
73- npm --version # must be >= 11.17.0
74- # If needed: npm install -g npm@latest
75- npm whoami # must succeed as an @perryts package maintainer
76- ```
77-
78- One-time GitHub setup: create the environment named in the OIDC identity and
79- store the Socket credential at environment scope (the secret value is entered
80- interactively and must never be committed):
64+ Freeze that candidate SHA. Unrelated merges to ` main ` after the branch is cut do
65+ not invalidate successful gates and do not require another test cycle. Refresh
66+ the candidate only for a release-blocking fix or a required version change.
67+
68+ Then use the npm-first pipeline. npm publication happens only in GitHub Actions
69+ through Trusted Publisher/OIDC. There is no ` npm login ` , npm account session,
70+ 2FA approval, or long-lived npm token in CI or on the maintainer machine. The
71+ local command uses GitHub authentication to dispatch/watch the workflow and
72+ anonymous registry reads to verify the published bytes.
73+
74+ Socket is an optional pre-publish tarball scan, not an npm credential. When
75+ enabled, Actions submits each of the exact nine ` .tgz ` files as a temporary
76+ Socket full scan and evaluates the results against the ` perryts ` Socket
77+ organization's security policy. Policy actions configured as ` error ` block
78+ publication; ` warn ` findings are recorded but do not block. The same tarballs
79+ that pass are then sent to npm. For the current release, omit ` --socket-scan ` ;
80+ the workflow records an explicit skipped receipt and continues.
81+
82+ To enable the gate later, store ` SOCKET_API_TOKEN ` as a repository or
83+ organization Actions secret with permission to create/read full scans, read the
84+ organization policy, and read quota. No GitHub Environment is required. The
85+ value stays only in GitHub and must never be committed or exported locally:
8186
8287``` bash
83- gh api --method PUT repos/PerryTS/perry/environments/npm-publish
84- gh secret set SOCKET_API_TOKEN --repo PerryTS/perry --env npm-publish
88+ gh secret set SOCKET_API_TOKEN --repo PerryTS/perry
8589```
8690
8791Before the first nine-package release, an npm organization owner must confirm
@@ -90,51 +94,51 @@ Trusted Publisher configuration:
9094
9195- provider: GitHub Actions
9296- organization/repository: ` PerryTS/perry `
93- - workflow filename: ` npm-stage-publish .yml`
94- - environment: ` npm-publish `
95- - allowed action: ** ` npm stage publish ` **
97+ - workflow filename: ` release-packages .yml`
98+ - environment: none
99+ - allowed action: ** ` npm publish ` **
96100
97- npm permits only one trusted publisher per package. Configurations created
98- before May 20, 2026 were carried forward with only direct ** ` npm publish ` **
99- allowed, so edit every existing package and explicitly enable
100- ** ` npm stage publish ` ** ; merely seeing a trusted publisher entry is not enough.
101- The old ` release-packages.yml ` direct-publish path cannot occupy a second
102- trusted-publisher slot and is not part of the canonical release.
101+ npm permits only one trusted publisher per package. The canonical identity is
102+ the existing ` release-packages.yml ` workflow above, with direct ** `npm
103+ publish` ** enabled. Do not configure ` npm-stage-publish.yml` as a second
104+ publisher and do not add an environment name that the other packages do not
105+ use.
103106
104107In particular, verify the ARM64 Windows package:
105108
106109``` bash
107110npm view @perryts/perry-win32-arm64 name
108111```
109112
110- If that returns ` E404 ` , an ` @perryts ` npm owner must make the initial public
111- name-reservation publish (the repository permits version ` 0.0.0 ` only for this
112- bootstrap), then configure the same Trusted Publisher fields above. The
113- pipeline intentionally refuses a partial set.
113+ If that returns ` E404 ` , stop: npm does not allow a Trusted Publisher to be
114+ configured for a package that does not exist, so OIDC cannot perform its first
115+ publish. An ` @perryts ` owner must provision that package name once before this
116+ nine-package OIDC-only flow can work, then configure the Trusted Publisher
117+ fields above. The release pipeline intentionally refuses a partial set.
114118
115119``` bash
116- npm run publish:stage # CI builds all platforms, stages 9 npm packages ,
117- # verifies sha1, runs the mandatory Socket scan,
118- # and downloads the exact proof tarballs locally
119- npm run publish:status # inspect the commit/run/package receipt
120- npm run publish:approve # explicit 2FA promote; waits for registry liveness
121- # and only then creates v0.x.y + the GitHub Release
120+ npm run publish:release # one Release Packages run: exact-SHA gates/builds ,
121+ # publish + verify all 9 via OIDC, then create
122+ # v0.x.y + the GitHub Release last
123+ # Later, once the repository/org Socket secret exists:
124+ npm run publish:release -- --socket-scan
125+ npm run publish:status # inspect the commit/run/package/Socket receipt
122126```
123127
124- Approval promotes nine packages sequentially. If 2FA or the network interrupts
125- that loop after some packages are already public, re-run ` publish:approve ` with
126- the same retained CI proof. It resumes only when each already-public package's
127- immutable registry shasum matches the exact CI tarball; otherwise use a new
128- version. Do not discard the proof directory until the tag and release exist .
128+ Publication sends the eight platform packages first and the wrapper last. If a
129+ network or registry error interrupts it before the tag exists, rerun the failed
130+ jobs (or ` publish:release ` ) on the same candidate. Actions skips an
131+ already-public package only when its immutable registry shasum matches the exact
132+ CI tarball; otherwise it stops and requires a new version .
129133
130134If the accumulated changelog fragments exceed the inline release-note budget,
131135the publisher keeps the GitHub Release body concise and uploads the complete
132136notes as the checksummed ` release-notes-full.md ` asset. No fragment is dropped.
133137
134- Do not run ` git tag ` , manually publish a GitHub Release, or use the legacy
135- ` release-packages.yml cut_release=true ` route for a normal release. That older
136- route creates the tag before npm publication; it does not satisfy the stricter
137- registry-first/tag-last contract .
138+ Do not run ` npm login ` , ` git tag ` , or manually publish a GitHub Release. The
139+ local command dispatches ` release-packages.yml ` with ` cut_release=true ` ; that
140+ workflow creates the tag only after all nine npm versions are public and their
141+ registry shasums match the exact CI tarballs .
138142
139143## 2. Additional major-release verification
140144
@@ -212,17 +216,17 @@ Artifacts are published to:
2122164 . ** winget** — manifest auto-update
2132175 . ** hub.perryts.com** — worker notification so cloud build workers refresh
214218
215- In the canonical staged flow, any failing host or cross build prevents all npm
216- staging, so no partial package set is promoted. Once a version has become public,
217- fix-forward with a new patch version rather than amending an existing tag.
219+ In the canonical npm-first flow, any failing host or cross build prevents npm
220+ publication. The wrapper is not published when a platform package fails. Once a
221+ version has become public, fix-forward with a new patch version rather than
222+ amending an existing tag.
218223
219224## 4. Release gates (what blocks a release)
220225
221- ` npm-stage-publish .yml` rejects a real stage unless ` test.yml ` has a successful
226+ ` release-packages .yml` rejects a cut release unless ` test.yml ` has a successful
222227** ` full-suite-gate ` ** and ` simctl-tests.yml ` has a successful run on the exact
223- candidate SHA. A green PR-tier or push-to-main sweep does * not* count. The same
224- two gates are enforced by ` release-packages.yml ` 's legacy release path. See
225- [ CI tiers] ( ../testing/ci-tiers.md ) . The full tier is:
228+ candidate SHA. A green PR-tier or push-to-main sweep does * not* count. See [ CI
229+ tiers] ( ../testing/ci-tiers.md ) . The full tier is:
226230
227231- everything the PR gate and the post-merge sweep run (` lint ` , ` check ` , ` warnings ` ,
228232 ` cargo test --workspace ` , the gap suite, ` gc-stress ` , Windows x64 + ARM64 builds,
@@ -247,11 +251,12 @@ candidate's fault, fix it on `main` first (or open an issue and consciously
247251re-add a job-level ` continue-on-error: true ` with that issue number) — do not
248252publish past it.
249253
250- The staging workflow then requires every host/cross package build, all nine npm
251- stages, sha1 verification, and the Socket scan. ` benchmark.yml ` , docs, container
252- tests, Homebrew, APT, winget, and worker refresh are tag riders or distribution
253- steps: monitor them after the GitHub Release is created, but do not mistake them
254- for pre-tag gates.
254+ The release workflow then requires every host/cross package build, all nine
255+ exact npm publishes, and public-registry sha1 verification. Socket is an
256+ optional pre-publish gate and is currently skipped. ` benchmark.yml ` , docs,
257+ container tests, Homebrew, APT, winget, and worker refresh are tag riders or
258+ distribution steps: monitor them after the GitHub Release is created, but do
259+ not mistake them for pre-tag gates.
255260
256261## 4a. What tells you a release is overdue
257262
@@ -290,9 +295,9 @@ a release has been cut is not something a PR author can fix.
290295
291296- ** Wrong artifact published** : tag a new patch release with the fix; npm
292297 rejects re-publishes of the same version anyway.
293- - ** Broken build before approval ** : fix it and stage the complete nine-package
294- set again ; the canonical flow will not promote a partial set.
295- - ** Broken binary discovered after approval ** : ship a follow-up patch version;
298+ - ** Broken build before npm publication ** : fix it and rebuild the complete
299+ nine-package set ; the canonical flow will not tag a partial set.
300+ - ** Broken binary discovered after publication ** : ship a follow-up patch version;
296301 neither npm versions nor release tags are mutable.
297302- ** A post-tag distribution hook failed** : re-run the failed workflow. To retry
298303 the legacy release-packages distribution legs, dispatch it with
0 commit comments