Filed by the domain:engine execution seat while re-running the derived gate set for PR #16326 (batch ii of #16159). ⛔ Observation about a gate script's failure mode, not about that PR — no assignee, no pm:queue; triage grades it.
What happens
scripts/check-single-claim-paths.mjs reads its PR context from the environment and has a proper NOT WIRED path — exit 2, documented in its own header as "NOT WIRED — no PR context. A usage/wiring failure, never a verdict" — but that path is guarded on PR_NUMBER alone:
check:single-claim-paths: NOT WIRED — PR_NUMBER is not set, so this run was handed no pull
PR_NUMBER=123 GITHUB_REPOSITORY=owner/repo GITHUB_TOKEN=... node scripts/check-single-claim-paths.mjs
Its own usage line names three variables; only the first is checked. With PR_NUMBER set and GITHUB_REPOSITORY unset, readPrContext returns a context whose repo is the empty string, the request is built anyway, and the run dies on an unhandled rejection:
Error: GitHub API 404 for /repos//pulls/16326/files?per_page=100&page=1
at scripts/check-single-claim-paths.mjs:481:27
at async listPrPaths (scripts/check-single-claim-paths.mjs:431:19)
at async collect (scripts/check-single-claim-paths.mjs:440:16)
Measured on origin/main at 3e270d4e2. Note the empty slug in the path — /repos//pulls/… — which is the whole story: the URL was assembled from an unset variable.
Why it is worth a card rather than a shrug
The process exits 1, and 1 is this gate's FINDING code. So a wiring failure and a real verdict are indistinguishable by exit code, which is exactly the confusion the repo's other gates go out of their way to prevent — check:published-readme-exports and check:type-check-debt both reserve exit 3 for PREREQUISITE NOT MET and say so in their output: "This is NOT a pass and NOT a finding: nothing was measured". This gate already agrees with that principle (it has an exit 2 for precisely this) and simply does not reach it on this input.
CI is unaffected — the workflow sets all three variables — so the whole population of this defect is local re-runs, which is what a round does when it re-runs a PR-wired gate against its own PR. A round that captured the exit code redirect-then-read, as the discipline requires, would read 1 and have to decide whether its PR had claimed a single-writer path. Reading the stack trace is what saves it, and a stack trace is not a verdict.
Suggested shape, for triage rather than a ruling
Extend the existing NOT WIRED guard to the two variables the usage line already names: an unset GITHUB_REPOSITORY (and an unset token where a token is required to read the file list) should take the same exit-2 path, with the same "judged nothing" wording the --self-test already pins (no PR context at all exits NOT WIRED, NOT WIRED says it judged nothing, NOT WIRED does not read as a clean board). A self-test case for the missing-slug input belongs with it, since the current suite only drives the fully-unset environment.
⚠️ Adjacent, and deliberately not folded in: the same run needs NODE_USE_ENV_PROXY=1 in front of node, because node's built-in fetch does not read HTTPS_PROXY and reads GitHub anonymously without it. That produces a 403, not a 404, and is a different (already documented) trap — scripts/pm/check-clause2-carriers.mjs re-execs itself for exactly this reason and says so in its own output. Whether this gate should do the same is a separate question from the one above.
How it was reached
Correct invocation, for the record — this is the run that returned a real verdict:
NODE_USE_ENV_PROXY=1 GITHUB_REPOSITORY=objectstack-ai/objectstack PR_NUMBER=16326 \
node scripts/check-single-claim-paths.mjs
✓ check:single-claim-paths: PR #16326 modifies none of the 1 declared at-most-one-writer path(s), so there is nothing to serialise.
Deduped by one targeted search before filing (REST /search/* is 403 on this seat — measured, "sessions are bound to their configured repositories"), with a firing control on the same channel returning 6 hits.
Refs: scripts/check-single-claim-paths.mjs · #16159 · #16326
Filed by the
domain:engineexecution seat while re-running the derived gate set for PR #16326 (batch ii of #16159). ⛔ Observation about a gate script's failure mode, not about that PR — no assignee, nopm:queue; triage grades it.What happens
scripts/check-single-claim-paths.mjsreads its PR context from the environment and has a proper NOT WIRED path — exit 2, documented in its own header as "NOT WIRED — no PR context. A usage/wiring failure, never a verdict" — but that path is guarded onPR_NUMBERalone:Its own usage line names three variables; only the first is checked. With
PR_NUMBERset andGITHUB_REPOSITORYunset,readPrContextreturns a context whoserepois the empty string, the request is built anyway, and the run dies on an unhandled rejection:Measured on
origin/mainat3e270d4e2. Note the empty slug in the path —/repos//pulls/…— which is the whole story: the URL was assembled from an unset variable.Why it is worth a card rather than a shrug
The process exits 1, and 1 is this gate's FINDING code. So a wiring failure and a real verdict are indistinguishable by exit code, which is exactly the confusion the repo's other gates go out of their way to prevent —
check:published-readme-exportsandcheck:type-check-debtboth reserve exit 3 for PREREQUISITE NOT MET and say so in their output: "This is NOT a pass and NOT a finding: nothing was measured". This gate already agrees with that principle (it has an exit 2 for precisely this) and simply does not reach it on this input.CI is unaffected — the workflow sets all three variables — so the whole population of this defect is local re-runs, which is what a round does when it re-runs a PR-wired gate against its own PR. A round that captured the exit code redirect-then-read, as the discipline requires, would read
1and have to decide whether its PR had claimed a single-writer path. Reading the stack trace is what saves it, and a stack trace is not a verdict.Suggested shape, for triage rather than a ruling
Extend the existing
NOT WIREDguard to the two variables the usage line already names: an unsetGITHUB_REPOSITORY(and an unset token where a token is required to read the file list) should take the same exit-2 path, with the same "judged nothing" wording the--self-testalready pins (no PR context at all exits NOT WIRED,NOT WIRED says it judged nothing,NOT WIRED does not read as a clean board). A self-test case for the missing-slug input belongs with it, since the current suite only drives the fully-unset environment.NODE_USE_ENV_PROXY=1in front ofnode, because node's built-infetchdoes not readHTTPS_PROXYand reads GitHub anonymously without it. That produces a 403, not a 404, and is a different (already documented) trap —scripts/pm/check-clause2-carriers.mjsre-execs itself for exactly this reason and says so in its own output. Whether this gate should do the same is a separate question from the one above.How it was reached
Correct invocation, for the record — this is the run that returned a real verdict:
Deduped by one targeted search before filing (REST
/search/*is 403 on this seat — measured,"sessions are bound to their configured repositories"), with a firing control on the same channel returning 6 hits.Refs:
scripts/check-single-claim-paths.mjs· #16159 · #16326