From 333355919039d095f09f3b7e520366d21eebe59c Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 9 Aug 2026 09:11:53 -0400 Subject: [PATCH] ci: require reviewer approval before npm publish (GIT-94) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hold on releasing was an instruction. An instruction-only hold on an irreversible action has no failure signal: nothing physically prevents the action, so nothing reports when it happens without the approval. That is scar b6fa8f3c — a gate triggered only by human intent has no failure signal between uses — applied to release authority rather than to a test harness. v1.8.0 is the demonstration. The content was ruled and the gate was green, but the mechanical tier everyone assumed existed did not: `git tag` and `git push` had been allowlisted months earlier, so the tag reached the registry with no confirmation step anywhere in the path. Whether or not any individual release was authorised, a path where authorisation cannot be independently verified is the defect. Binding publish to a protected GitHub Environment makes a tag build but wait. npm publish then requires a named reviewer to approve the deployment in the GitHub UI — outside the CLI, outside any client's allowlist, and unaffected by what an agent is permitted to run locally. SETUP REQUIRED — this is inert until the environment exists: Settings -> Environments -> New environment -> "npm-production" -> Deployment protection rules -> Required reviewers -> add Chris Until then the job names an unprotected environment and publish proceeds as before, so merging this alone does not close the hole. The GitHub-side step is the fix; this commit is the wiring. Co-Authored-By: Claude Opus 5 --- .github/workflows/ci.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f672779..93b8ddc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,6 +55,24 @@ jobs: needs: build runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/v') + # A tag now BUILDS but does not publish. This environment requires a named + # reviewer to approve the deployment in the GitHub UI before npm publish + # runs, so the release hold exists mechanically rather than by convention. + # + # Why: the hold on releasing was an instruction, and an instruction-only + # hold on an irreversible action has no failure signal — nothing physically + # prevents the action, so nothing reports when it happens without the + # approval. That is the same shape as scar b6fa8f3c (a gate triggered only + # by human intent has no failure signal between uses), applied to release + # authority instead of to a test harness. + # + # SETUP REQUIRED before this has any effect — create the environment in + # GitHub and add the required reviewer: + # Settings -> Environments -> New environment -> "npm-production" + # -> Deployment protection rules -> Required reviewers -> add Chris + # Until that environment exists with a reviewer, this line names an + # unprotected environment and publish proceeds as before. + environment: npm-production permissions: contents: read packages: write