Skip to content

ci(vale): publish the platform packages from npm-autopublish - #136

Merged
thecodedrift merged 2 commits into
mainfrom
ci/vale-npm-autopublish
Aug 21, 2026
Merged

ci(vale): publish the platform packages from npm-autopublish#136
thecodedrift merged 2 commits into
mainfrom
ci/vale-npm-autopublish

Conversation

@thecodedrift

@thecodedrift thecodedrift commented Aug 21, 2026

Copy link
Copy Markdown
Member

What changed

release-vale.yml's publish job moves from environment: npm-production to environment: npm-autopublish — the reviewer-free environment. The file's header gains an APPROVAL POLICY paragraph recording why, and the PUBLISHING IDENTITY paragraph 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/cli keeps its required reviewer on npm-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:

  • the deployment branch policy on npm-autopublish is exactly main, in the explicit custom-policy form, so no branch can reach the credential by adding a job that names the environment;
  • publishing a platform package changes no consumer — the CLI pins each one exactly, so a new version reaches a user only when someone reviews a bump to that pin.

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:

0.11.0-20260821002453x3f114d6
0.11.0-20260821025240x423363a

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.yml and npm-autopublish. main still says environment: 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.
  • The publish path is push: branches: [main], paths: [".github/scripts/vale-manifest.json"], which fires only when a reviewed manifest change lands on main.

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.ymlrelease-vale.yml in 0cc713e (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:

Field Value
Organization / repository taskless/cli
Workflow filename release-vale.yml
Environment npm-autopublish

Package names, read from packages/vale-*/package.json rather than from directory names:

@taskless/vale-darwin-arm64
@taskless/vale-darwin-x64
@taskless/vale-linux-arm64
@taskless/vale-linux-x64
@taskless/vale-win32-arm64
@taskless/vale-win32-x64

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_dispatch runs from whatever ref it is launched on, leaving the environment's deployment branch policy as the only thing keeping an off-main publish from happening. That is correct, and it is correct because of this PR: under npm-production an off-main dispatch 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.

077b362 adds if: github.ref == 'refs/heads/main' to the publish job. One expression covers both paths in: the push trigger is already branches: [main], so the guard is a no-op there; workflow_dispatch is the case that matters. The comment says the duplication is deliberate and must not be deleted as redundant.

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 — worth keeping. Guarding them instead would protect publish by inference through needs:, 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-builds was archived, its vale-binary-packages delta was withheld because it asserted behavior that had not shipped. This PR is what makes it true, so the withheld text is restored verbatim into openspec/specs/vale-binary-packages/spec.md: the requirement paragraph plus the Publishing needs no approval click scenario, under "Platform packages are released by their own workflow, tracking upstream". Nothing else in that file is restructured.

Verification

Check Result
pnpm lint clean
pnpm typecheck 1 successful, 1 total
pnpm test 51 files, 630 tests passed
node --test .github/scripts/*.test.cjs 156 pass, 0 fail
pnpm openspec validate --all --strict 24 passed, 0 failed
node .github/scripts/openspec-visibility.cjs every requirement in 24 specs visible
YAML parse of the edited workflow resolves; job graph unchanged (detect, gate, prepare, publish); publish has environment: npm-autopublish and if: github.ref == 'refs/heads/main'

No workflow_dispatch run 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 main over time) stays open, which is why this references rather than closes the issue.

Refs #131

Copilot AI lite review requested due to automatic review settings August 21, 2026 15:57
@thecodedrift thecodedrift added the skip-changeset PR intentionally ships no release note (bypasses the changeset requirement) label Aug 21, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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’s publish job to environment: npm-autopublish and 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.

Comment thread .github/workflows/release-vale.yml
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
thecodedrift and others added 2 commits August 21, 2026 10:36
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
thecodedrift force-pushed the ci/vale-npm-autopublish branch from 077b362 to afa1d94 Compare August 21, 2026 17:36
@thecodedrift
thecodedrift merged commit 35038c2 into main Aug 21, 2026
2 checks passed
@thecodedrift
thecodedrift deleted the ci/vale-npm-autopublish branch August 21, 2026 17:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-changeset PR intentionally ships no release note (bypasses the changeset requirement)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants