Skip to content

fix(devx): route missing GITHUB_REPOSITORY/GITHUB_TOKEN to EXIT_NOT_WIRED - #17362

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-16329-single-claim-paths-not-wired
Sep 10, 2026
Merged

fix(devx): route missing GITHUB_REPOSITORY/GITHUB_TOKEN to EXIT_NOT_WIRED#17362
baozhoutao merged 1 commit into
mainfrom
claude/issue-16329-single-claim-paths-not-wired

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes #16329

What changed

check:single-claim-paths's readPrContext only guarded PR_NUMBER via Object.hasOwn. With PR_NUMBER set and GITHUB_REPOSITORY unset, ctx.repo resolved to '', the request was built anyway (/repos//pulls/…), and the run died on an unhandled rejection — exiting 1, this gate's FINDING code, instead of the exit-2 NOT WIRED path its own header already reserves for exactly this.

readPrContext now returns a three-way result: null (nothing wired at all), { wired: false, missing, number } (PR_NUMBER present but GITHUB_REPOSITORY or GITHUB_TOKEN missing), or the full context. judge() routes both null and wired: false through the same EXIT_NOT_WIRED (2) verdict text — still saying "judged nothing", never printing a .

GITHUB_REPOSITORY and GITHUB_TOKEN are checked by truthiness (trimmed, non-empty), not PR_NUMBER's Object.hasOwn presence check. This is a deliberate, documented departure from the PR_NUMBER convention, not a silent swap — see the docblock above readPrContext: PR_NUMBER's value is never read to build a request, it only witnesses that the workflow ran, so presence is the right test. GITHUB_REPOSITORY/GITHUB_TOKEN are consumed directly to build the request (URL slug, Authorization header), so an empty string is the same failure as an absent variable — the exact /repos//pulls/… bug is reproduced by a GITHUB_REPOSITORY that a presence check would call "wired" the moment it is merely set to ''. The token is guarded unconditionally too: every real invocation reads a PR's file list over the network, and the wiring workflow always supplies one (pinned by the existing self-test: "the wiring passes a token, without which no file list can be read").

Why out of scope stays out of scope

The NODE_USE_ENV_PROXY=1 / 403-vs-404 proxy question named at the end of the issue is not touched here — the issue itself calls it "a separate question," and the claim comment repeats the same boundary. No self-re-exec was added to this gate.

Measurements

Before (original readPrContext, PR_NUMBER set, GITHUB_REPOSITORY unset):

$ PR_NUMBER=16326 node scripts/check-single-claim-paths.mjs
EXIT=1

file:///.../scripts/check-single-claim-paths.mjs:481
  if (!response.ok) throw new Error(`GitHub API ${response.status} for ${path}`);
                          ^

Error: GitHub API 404 for /repos//pulls/16326/files?per_page=100&page=1
    at .../scripts/check-single-claim-paths.mjs:481:27
    at process.processTicksAndRejections (node:internal/process/task_queues:103:5)
    at async listPrPaths (.../scripts/check-single-claim-paths.mjs:431:19)
    at async collect (.../scripts/check-single-claim-paths.mjs:440:16)
    at async .../scripts/check-single-claim-paths.mjs:696:44

Node.js v22.22.2

After (this PR, same command, same env):

$ PR_NUMBER=16326 node scripts/check-single-claim-paths.mjs
EXIT=2

check:single-claim-paths: NOT WIRED — GITHUB_REPOSITORY is not set (or set to an empty string), so this run was handed no usable pull
request context and judged nothing. This is a wiring or usage failure, NOT a verdict: it says
nothing about whether any PR claims a single-claim path, and no author caused it.

Fix:  run it from the workflow that supplies the context (.github/workflows/single-claim-path-guard.yml), or locally with
      PR_NUMBER=123 GITHUB_REPOSITORY=owner/repo GITHUB_TOKEN=... node scripts/check-single-claim-paths.mjs

Reverse control (all three variables supplied — behaviour UNCHANGED, a real verdict is still returned):

$ NODE_USE_ENV_PROXY=1 GITHUB_REPOSITORY=objectstack-ai/objectstack PR_NUMBER=16326 GITHUB_TOKEN=x node scripts/check-single-claim-paths.mjs
EXIT=0
✓ check:single-claim-paths: PR #16326 modifies none of the 1 declared at-most-one-writer path(s), so there is nothing to serialise.

--self-test (raised battery floor, 'Wiring absent: never clean, never an accusation.' 5 → 16, including the new missing-slug and missing-token cases and one assertion pinning the exit code as the literal number 2):

$ node scripts/check-single-claim-paths.mjs --self-test
...
  ✓ no PR context at all exits NOT WIRED
  ✓ NOT WIRED says it judged nothing
  ✓ NOT WIRED does not read as a clean board
  ✓ a fully wired environment (all three variables) is wired
  ✓ an unset environment is not wired
  ✓ PR_NUMBER with no GITHUB_REPOSITORY is not fully wired
  ✓ ...and it is attributed to the right missing variable
  ✓ a missing GITHUB_REPOSITORY exits NOT WIRED, never the FINDING code
  ✓ ...and that exit code really is the number 2 (#16329, not just the named constant)
  ✓ the missing-repo verdict says it judged nothing
  ✓ the missing-repo verdict does not read as a clean board
  ✓ the missing-repo verdict names the missing variable
  ✓ PR_NUMBER + repo but no GITHUB_TOKEN is not fully wired
  ✓ ...and it is attributed to GITHUB_TOKEN
  ✓ a missing GITHUB_TOKEN exits NOT WIRED too
  ✓ all three variables present is fully wired (reverse control)
...
✓ check-single-claim-paths self-test: 65 cases pass.

Gates run

node scripts/pm/dispatch-gates.mjs --commands derived 32 commands for this diff; all 32 ran green, including pnpm check:pm-dispatch-gates (1674 cases pass) and pnpm check:single-claim-paths (the self-test above). node scripts/check-nul-bytes.mjs also passes.

Changeset

skip-changeset — measured, not assumed: every package's package.json files[] publishes only dist, README.md, CHANGELOG.md (checked across all workspace packages with a files field). scripts/check-single-claim-paths.mjs is a repo-root CI gate script under scripts/, not scripts/pm/**, and is not built into any package's dist — nothing published moves.

Clause-②: no


Generated by Claude Code

…IRED

check:single-claim-paths only guarded PR_NUMBER, so a run with PR_NUMBER
set but GITHUB_REPOSITORY unset assembled a request against an empty
repo slug, threw an unhandled rejection, and exited 1 — this gate's
FINDING code — instead of the exit-2 NOT WIRED path it already
documents and reserves for exactly this. GITHUB_TOKEN is guarded the
same way, since every real run needs it to read a PR's file list.

readPrContext now returns a { wired: false, missing, number } shape for
this half-wired case, and judge() routes it through the same NOT WIRED
verdict text as the fully-unset case (still saying "judged nothing",
never printing a clean-board mark). GITHUB_REPOSITORY/GITHUB_TOKEN are
checked by truthiness rather than PR_NUMBER's Object.hasOwn presence
check — the docblock says why the convention differs: both values are
consumed directly to build the request, so an empty string reproduces
the exact defect a presence check would wave through.

Adds a --self-test battery for the missing-slug and missing-token
inputs, including an assertion pinning the exit code as the literal
number 2, and a reverse-control case proving all three variables
present still resolves to a full, usable context.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU
@baozhoutao baozhoutao added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 10, 2026 — with Claude
@baozhoutao
baozhoutao marked this pull request as ready for review September 10, 2026 08:51
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 10, 2026
Merged via the queue into main with commit d29fdcf Sep 10, 2026
35 of 36 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-16329-single-claim-paths-not-wired branch September 10, 2026 09:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

check:single-claim-paths reports a WIRING failure at exit 1 — the code reserved for a finding — when GITHUB_REPOSITORY is unset

2 participants