Repository navigation
v2.6.0
Added
-
The release-path workflow guards now run in CI.
release.ymltriggers
only on tag push — the tag push is the publish — so it never appeared in a
PR check, and every guard that protected the last change to it was run by
hand, once. Two newtest.ymljobs close that:workflowsrunsscripts/check_workflows.py, which asserts by invariant
thatrelease.yml's trigger set is exactlypush.tags: ["v*"]— no other
event and no branch or path filter alongside it, sincepublishholds
id-token: writeagainst an environment with no protection rules, so any
extra trigger makes trusted publishing reachable from it — and that no
workflow file other thanrelease.yml/docker.ymlis tag-triggered at all;
thebuild → publish → github-releaseordering;environment: releaseby
name; noif:orcontinue-on-errorat job or step level on any of the
three jobs (skippingbuildskipspublishthroughneeds, and the tag push
still concludes green); the
exact committedpermissions:maps at both workflow and job level, the exact
committeduses:references, and exactly the three expected jobs; that every
job in every workflow carries atimeout-minutesof 10–30 (a bare
timeout-minutes: 360re-creates the six-hour default); and by drift
that no job present in the PR base has disappeared from any changed workflow,
including one deleted outright. Drift fails closed: a base ref that does
not resolve, and agit diffthat fails for any other reason (a base sharing
no history with HEAD exits 128), are failures, never "nothing changed". It
also runs a digest-pinned actionlint.release-diff-ackcovers by acknowledgement what an allowlist cannot
cover by enumeration: any PR touchingrelease.ymlfails unless it carries a
release-change-approvedlabel, read live from the API rather than from the
event payload frozen at trigger time.Not covered, deliberately: a timeout above the 10-minute floor but below a
job's real p100; environment protection rules, which live in GitHub settings
and are invisible to any file check; andif:-skipping the guard job itself,
which GitHub counts as satisfying a required check. Per-mutant exit codes,
including the ones that stay green, are in
.consiliency/evidence/mutation-189.md.Because the
uses:andpermissions:allowlists are exact, a legitimate
edit torelease.yml— bumping an action, granting a scope — must update the
constants inscripts/check_workflows.pyin the same PR. That is intended.
Fixed
-
npm package identity now comes from npm's own parser where it is certain, and
is refused otherwise. The hand-written npm flag tables have been repaired five
times (#180 → #192 → #194 → #195 → the 2.5.2 nullable-boolean
spelling), and every defect was in the rules around the tables rather than a
missing entry — so every repair produced a confident wrong answer, which the
freshness gate reads as positive confirmation that a cached tool description
still describes the configured package.For an
npx/npmserver the gateway now asks the host npm's ownnopt, its
own@npmcli/configdefinitions and its ownnpm-package-arg, through a
faithful port of npm'snpx-cli.jspre-scan, and accepts the answer only when
nothing in the invocation could redirect resolution. It refuses when:- the parsed configuration contains any key beyond
--yesand--package
(--registry,--userconfig,--prefix,--cache,--call,--workspace,
a shorthand such as--silentthat expands to--loglevel, or an unknown
flag) — this is an allowlist of plain shapes, not a denylist of dangerous
ones; - the server's environment overlay, or the gateway's own process
environment, setsnpm_config_*(case-insensitive),PATH,HOME,
NODE_PATH,NODE_OPTIONS,PREFIXorNVM_*; - walking up from the effective working directory, npm would set a local
prefix (apackage.jsonornode_modulesin any ancestor), because a
project.npmrccan rename the package and a localnode_modules/.binentry
means npm never reaches the registry at all; npm-package-argreports anything but a registry spec — notably an alias
(npx -y myalias@npm:left-padreally runsleft-pad, and the alias name is a
squattable different package);- the npm subcommand has no package operand (
npm run,npm start,npm test,
npm create, a typo, barenpm -y pkg, and nownpm dlx, which is
pnpm/yarn spelling and is not an npm command at all); - the spawn-time self-test against the host's own parser fails,
bin/npx-cli.js
is not one this port was verified against, or npm's parser cannot be loaded.
A failed self-test refuses — it does not fall back to the tables, because
a failed self-test is precisely the evidence that the tables' model of npm is
wrong. One WARNING is logged.
Refusing costs auto-update coverage for an unusual configuration: the server
keeps running, but its package is reported asunknown, so its descriptions
refresh every cycle andgateway.update_servercannot name a package for it.
Measured cost on the shipped manifest: zero — all 79 npm-family servers use
the plainnpx -y <pkg>shape and all 79 resolve to the same package the real
npxbinary fetches.Where node is not installed the flag tables remain in use unchanged, which is
the behaviour every release through 2.5.1 shipped.Known residual: a
package=orregistry=line in a user or global
.npmrcchanges what npm resolves and the gateway cannot see it. Project-level
.npmrcis covered by the local-prefix refusal, andnpm_config_*in the
gateway's own environment is covered by the process-environment check; the
user/global rc file is the one input that remains unguarded.detect_package_type,_npm_package_arg,get_package_versionand
gateway.update_server's pin detection all take the server's environment
overlay and working directory as required parameters now, since both are
identity inputs. - the parsed configuration contains any key beyond