ci(vale): publish the platform packages from npm-autopublish - #136
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the Vale platform-package release pipeline to publish via the reviewer-free npm-autopublish environment (rather than npm-production), and records the rationale/identity constraints in both the workflow header documentation and the OpenSpec requirement text.
Changes:
- Switch
.github/workflows/release-vale.yml’spublishjob toenvironment: npm-autopublishand expand the header documentation to record the approval policy and trusted-publisher identity coupling. - Restore the previously withheld OpenSpec text asserting that Vale platform package publishing proceeds without an environment-approval step.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| openspec/specs/vale-binary-packages/spec.md | Restores requirements/scenario text stating publishing proceeds without an approval click in a reviewer-free environment. |
| .github/workflows/release-vale.yml | Moves the publish job onto npm-autopublish and documents the approval policy + trusted publishing identity constraints. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
thecodedrift
added a commit
that referenced
this pull request
Aug 21, 2026
Review finding on #136: with the required reviewer gone, the environment's deployment branch policy is the ONLY thing keeping an off-`main` `workflow_dispatch` from publishing. Under `npm-production` there were two independent obstacles; this PR removed one of them, so the remaining one should not also be the only one. The guard duplicates the branch policy on purpose. One copy lives in a settings page that can be edited without review and whose relaxation is an absence — nothing turns red when it happens. The other lives here, next to the reasoning, and cannot change without a diff. `github.ref == 'refs/heads/main'` covers both paths into this job. The `push` path is already filtered to `main`, so the guard is a no-op there; the `workflow_dispatch` path is the real case, since a dispatch runs from whatever ref it was launched on. Only `publish` is guarded. `gate` and `prepare` hold no environment, no id-token, and only `contents: read`, so off-`main` they are a harmless dry run of the digest verification and the pack. Guarding them instead would protect `publish` by inference through `needs:` — one control with a spare, which is the shape being corrected. The spec requirement gains the same constraint, so deleting either copy is a spec violation rather than a tidy-up. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jwc9FFroR3mTZ4hLiSkkX3
Closed
13 tasks
The Vale publish job moves off `npm-production` and onto the reviewer-free `npm-autopublish` environment. This was deliberately sequenced after the nightly, so the environment and the OIDC handshake would be proven on a package where a mistake is cheap before a working release path moved onto it. Two nightlies have now published automatically through it (0.11.0-20260821002453x3f114d6 and 0.11.0-20260821025240x423363a). The review gate does not move — it was never the approval click. A human reviews the upstream Vale version and all six SHA256 digests on the manifest-update pull request, and only merging that can start a publish. An environment approval is a second copy of that gate asked where nothing is left to decide. The publish stays bounded by the branch policy on `npm-autopublish` (`main` only) and by the standing guarantee that a published platform package changes no consumer until someone bumps the CLI's exact pin. The header also now records that the npm trusted-publisher binding names this workflow's filename and environment, since both are things a future edit could change without noticing what breaks. Restores the `vale-binary-packages` requirement paragraph and the "Publishing needs no approval click" scenario that were withheld when `nightly-cli-builds` was archived, because they asserted behavior that had not shipped yet. It has now shipped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jwc9FFroR3mTZ4hLiSkkX3
Review finding on #136: with the required reviewer gone, the environment's deployment branch policy is the ONLY thing keeping an off-`main` `workflow_dispatch` from publishing. Under `npm-production` there were two independent obstacles; this PR removed one of them, so the remaining one should not also be the only one. The guard duplicates the branch policy on purpose. One copy lives in a settings page that can be edited without review and whose relaxation is an absence — nothing turns red when it happens. The other lives here, next to the reasoning, and cannot change without a diff. `github.ref == 'refs/heads/main'` covers both paths into this job. The `push` path is already filtered to `main`, so the guard is a no-op there; the `workflow_dispatch` path is the real case, since a dispatch runs from whatever ref it was launched on. Only `publish` is guarded. `gate` and `prepare` hold no environment, no id-token, and only `contents: read`, so off-`main` they are a harmless dry run of the digest verification and the pack. Guarding them instead would protect `publish` by inference through `needs:` — one control with a spare, which is the shape being corrected. The spec requirement gains the same constraint, so deleting either copy is a spec violation rather than a tidy-up. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jwc9FFroR3mTZ4hLiSkkX3
thecodedrift
force-pushed
the
ci/vale-npm-autopublish
branch
from
August 21, 2026 17:36
077b362 to
afa1d94
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
release-vale.yml'spublishjob moves fromenvironment: npm-productiontoenvironment: npm-autopublish— the reviewer-free environment. The file's header gains an APPROVAL POLICY paragraph recording why, and thePUBLISHING IDENTITYparagraph now states that the npm trusted-publisher binding names this workflow's filename and its environment, since both are things a later edit can change without noticing what breaks.No other workflow is touched.
@taskless/clikeeps its required reviewer onnpm-production.Why the approval click was the wrong gate
The review already happens somewhere better. A Vale publish can only start when a manifest-update pull request merges, and on that PR a human reads the upstream Vale version and all six SHA256 digests. By the time the publish job runs, the bytes were fixed when those digests were reviewed — an environment approval is a second copy of the same gate, asked where nothing is left to decide. Approvals that decide nothing get clicked without being read.
What still bounds an unattended publish:
npm-autopublishis exactlymain, in the explicit custom-policy form, so no branch can reach the credential by adding a job that names the environment;Why the sequencing prerequisite is satisfied
This move was deliberately sequenced after the nightly, so the environment and the OIDC handshake would be proven on a package where a mistake is cheap before a working release path moved onto it. Two nightlies have now published automatically through
npm-autopublish:Merge promptly — the binding is already ahead of the code
The maintainer has already updated the npm trusted-publisher configuration: it now names
release-vale.ymlandnpm-autopublish.mainstill saysenvironment: npm-production, so npm and the workflow currently disagree, and a Vale publish attempted in that window would fail the OIDC handshake. Merging this PR is what closes the gap.The exposure is bounded, and this is not an outage:
schedule: cron "23 7 * * 1"runs detect only — it opens a manifest pull request and publishes nothing.push: branches: [main], paths: [".github/scripts/vale-manifest.json"], which fires only when a reviewed manifest change lands onmain.So nothing publishes by accident. The only way to hit the mismatch is to merge a Vale manifest PR before this one lands. Next scheduled detect is Monday.
Trusted-publisher values, for cross-check
npm's binding is environment-scoped — confirmed empirically, since the environment is editable within the trusted-publisher configuration. It is also filename-scoped, and this workflow was renamed
vale-binaries.yml→release-vale.ymlin0cc713e(the release-workflow split); npm exposes no read API for bindings, so whether they still named the old file before this update is not something this PR can determine.What the repository actually requires, for all six packages:
taskless/clirelease-vale.ymlnpm-autopublishPackage names, read from
packages/vale-*/package.jsonrather than from directory names:Nothing else about the binding changes — same repository, same publisher, same six names.
The branch restriction is now enforced twice (review finding)
Copilot flagged that
workflow_dispatchruns from whatever ref it is launched on, leaving the environment's deployment branch policy as the only thing keeping an off-mainpublish from happening. That is correct, and it is correct because of this PR: undernpm-productionan off-maindispatch had to get past the branch policy and a required reviewer. Removing the reviewer leaves one control, and its relaxation is an absence — a settings edit by someone who does not know it is load-bearing turns nothing red.077b362addsif: github.ref == 'refs/heads/main'to thepublishjob. One expression covers both paths in: thepushtrigger is alreadybranches: [main], so the guard is a no-op there;workflow_dispatchis the case that matters. The comment says the duplication is deliberate and must not be deleted as redundant.Only
publishis guarded.gateandpreparehold no environment, noid-token, and onlycontents: read, so off-mainthey are a harmless dry run of the digest verification and the pack — worth keeping. Guarding them instead would protectpublishby inference throughneeds:, which is one control with a spare rather than two independent ones.The spec requirement now asserts the doubling as well ("that restriction SHALL be enforced twice"), plus a scenario for a publish requested from another branch, so removing either copy is a spec violation rather than a cleanup.
Spec
When
nightly-cli-buildswas archived, itsvale-binary-packagesdelta was withheld because it asserted behavior that had not shipped. This PR is what makes it true, so the withheld text is restored verbatim intoopenspec/specs/vale-binary-packages/spec.md: the requirement paragraph plus thePublishing needs no approval clickscenario, under "Platform packages are released by their own workflow, tracking upstream". Nothing else in that file is restructured.Verification
pnpm lintpnpm typecheckpnpm testnode --test .github/scripts/*.test.cjspnpm openspec validate --all --strictnode .github/scripts/openspec-visibility.cjsdetect,gate,prepare,publish);publishhasenvironment: npm-autopublishandif: github.ref == 'refs/heads/main'No
workflow_dispatchrun was performed — that publishes to npm and is the maintainer's call.Scope
Section 1 of #131 only. Section 2 (the acceptance criteria that need situations to arise on
mainover time) stays open, which is why this references rather than closes the issue.Refs #131