Status: partly live, partly target.
@changesets/cli is configured (.changeset/config.json) and
pnpm changeset, pnpm changeset:status and pnpm changeset:version exist.
.github/workflows/release.yml opens the version pull request and, on its merge, runs changeset publish to npmjs with provenance (pnpm trusted publishing, no token). Not built: a
packed-artifact smoke test and signed artifacts. Today every package is still "private": true,
so nothing publishes yet. Policy: 0025
(supersedes 0013 decision 3), 0177 (npmjs,
provenance), 0024 (MIT),
0119 (the owner merges the version pull request).The rules
- Every change to a member ships with a changeset.
pnpm changesetrecords it;pnpm changeset:statusfails when a changed member has none (target: chained intopnpm verify). CHANGELOG.mdfiles are generated by changesets and never hand-edited.- Versions are bumped by the release job on
main, not on a feature branch.guard-bashdenieschangeset versionlocally for that reason. - Merging the version pull request is the release decision. Claude prepares it; the owner merges it (0119 D7).
- Published or tagged versions are permanent. A version is never republished or silently replaced; roll forward with a new patch.
- Only packages are versioned. Apps and services deploy by commit SHA and get no version or tag (
privatePackages: { version: false, tag: false }, 0025 decision 4). - A package publishes when it flips to
"private": false. It then builds todist/*.mjs+.d.mtsand publishes to public npmjs (@stackrascope) with provenance.
A normal release
1
Land changesets with the work
In each pull request that changes a member:
pnpm changeset, pick the members, pick the bump
(patch, minor, major) and write one user-facing line. Commit the generated file under
.changeset/.2
Let CI open the version pull request
After a push to
main the release job runs pnpm changeset:version (which also refreshes the
lockfile) and opens or updates one pull request titled chore(release): version packages.3
Review it
Read the
CHANGELOG.md diffs and the version bumps. Check the bump matches the change. The
release job pushes and opens it as stackra-bot (a GitHub App token from
actions/create-github-app-token, repo secrets STACKRA_BOT_APP_ID and
STACKRA_BOT_PRIVATE_KEY), so the Quality Gate runs on it without a close and reopen.4
Merge it
The merge to
main is the release. The job builds, publishes every non-private package to
npmjs, tags it and creates the release notes.5
Verify
git ls-remote --tags origin shows the new tags on the merge commit, and each member’s
CHANGELOG.md contains the entry.Before the first release (once, by a human)
- Branch protection on
mainrequires theQuality Gatecheck (CI). - A tag ruleset on the version tag pattern blocks deletion and non-fast-forward updates for everyone. Version tags are immutable.
- The host setting that lets the workflow token create pull requests is on.
- The release job runs in a protected environment restricted to
main, so a branch that edits the workflow cannot obtain a credential.
Publishing to npmjs
0177 (amendment 2026-10-21) records the decision. The release job holds no npm token: changesets runs pnpm 12’s native publish, which exchanges the job’s GitHub OIDC token for a short-lived publish credential and signs a provenance statement. Each package needs arepository field matching figentra/stackra (the validator checks it).
One-time owner steps, per package (the @stackra org is reserved, STA-154):
1
Flip the package
In the package, set
"private": false (its publishConfig is already there; pnpm validate requires it).2
Register the trusted publisher
Needs 2FA enabled on the owner’s npm account for
npm trust (and the web page). npmjs.com → the package → Settings →
Trusted publishing → GitHub Actions: organization or user figentra, repository stackra, workflow filename
release.yml, environment release. Or with the CLI (npm 11.5 or later; one configuration per package):
npm trust github @stackra/<name> --repo figentra/stackra --file release.yml --env release --allow-publish.3
Optionally close the token path
Same page, Publishing access → require two-factor authentication and disallow tokens.
npm trust says so too), so the job cannot create one. Publish the first version once by hand as an owner,
from the package directory after pnpm build:
pnpm publish --no-git-checks --provenance=false --access public. Use pnpm, not plain npm publish: npm would publish literal
workspace: and catalog: versions, and it fails on provenance outside CI. Then register the trusted publisher above. The
first version is not provenance-attested; ship a 0.0.x placeholder.
Rehearse before any real release. After registering @stackra/brand: run the Release workflow with dry_run ticked
(pnpm -r publish --dry-run, same job and environment). Then publish a throwaway 0.0.x of @stackra/brand from CI through a
merged version pull request, and check npm view @stackra/brand dist.attestations shows a provenance entry. Only then release
anything real.
The procedure the job follows:
- No token anywhere. No
.npmrcauth line, repository secret or Infisical entry for npm. - Build and publish with
id-token: writeonly on the release job, in the protectedreleaseenvironment restricted tomain. - Publish is idempotent: a version already on the registry is skipped, so re-running failed jobs is safe.
- Consumers need no account or token to read
@stackra/*. - A prerelease publishes under a
nextdist-tag and moves no floating tag. npm.figentra.comstill serves@figentra/cliand vellum; it is not the@stackraregistry any more.
When something fails
Re-run only the failed jobs of the same run unless a row says otherwise.Revert
- Before merging: close the version pull request. The next push recreates it from the remaining changesets.
- After merging: roll forward. For a bad release that was published, deprecate it (
npm deprecate <pkg>@<bad> "<reason; use <good>>"), move thelatestdist-tag back to the last good version withnpm dist-tag addif needed, and ship a fixed patch. Prefer deprecate over unpublish. - A bad release record on the host: mark it a prerelease or delete the release page; leave the tag.
- The workflow itself: revert the workflow file on
main; nothing else depends on it.
Checks that back this page
See also Mobile release, Deploys and Commits and releases.