The chain-guard release (r475): the release chain audited itself. The last never-audited surface produced this round's only P1 — and the guards it shipped are the ones yesterday's v0.10.30 escape proved were missing.
Security / Release integrity
- Version provenance is verified before anything is built or pushed (P1). Tags are created by hand (
git tag vX <commit>), and nothing in the chain compared the tag's VALUE to what the checkout contains — a typo or a stale SHA would pass every check (it IS a green commit), push a mislabeled image whose panel reports a different VERSION, silently roll:latestbackward, and create a GitHub Release the CHANGELOG asset contradicts.release-publish.ymlnow asserts tag == package.json == version.ts immediately after checkout, before install/build/push;release-workflows.test.tspins the step's existence, its content and its position beforepnpm install.
Fixed
The local gate now means what CI's gate means (P2).
pnpm release:checkwas a singleturbo run typecheck lint build test— turbo has no dependency edges between those task names, so the phases could interleave (and a warm tree could satisfyserver#testfrom a staleapps/web/dist). CI's verify job has always run the four invocations sequentially;release:checknow mirrors it:pnpm typecheck && pnpm lint && pnpm build && pnpm turbo run test --concurrency=1. This is exactly the gap the 0.10.30 unparseable-version.tsescape slipped through locally.bump-version.js refuses to lie (P2). A pattern miss on the load-bearing
VERSIONliteral printed a warning and exited 0 — ten package.jsons saying 0.10.31 while the panel kept reporting 0.10.30, every gate green. That miss is now fatal, and the script closes by asserting every package.json and theVERSIONliteral actually landed on the new version before printing its success banner.
Added
pnpm release:tag— the local tag gate. Wrapsgit tagwith the checks the drill previously held as discipline: clean working tree; the tag does not already exist (re-tagging stays an explicit delete-and-recreate); tag == package.json == version.ts == CHANGELOG[0] read from the exact commitish viagit show(not the working tree); no unfilled stub survives; and the server typechecks — the precise step an unescaped-apostrophe changelog fails. Used for this very release.
Verified sound (for the record)
CI's structure held under the audit: build-before-checks, checks-before-push, needs-gated edge publish, least-privilege permissions, no pull_request_target/fork-secret exposure, strict tag-shape validation; bump-version.js is idempotent with no flag bypass of the >=1.0.0 owner policy. The parked dependency majors stay parked with evidence: drizzle-kit 0.31.11 still depends on the deprecated @esbuild-kit/esm-loader (our patch remains required; the bump buys nothing), and js-yaml 4.3.2 carries no advisory while pm2 pins the 4.x line.