Commit 16cb493
fix(scripts): route check:single-claim-paths through the session proxy so a seat can run it (#18935)
Fixes #18314
Clause-②: no
`check:single-claim-paths` could not be run outside CI. It reads a PR's
file list over the network, node's `fetch` does not read `HTTPS_PROXY`,
and in an agent container the request therefore left without its
credential and GitHub answered `401`. A seat could not pre-run the gate
its own PR would be judged by, and correctly recorded NOT MEASURED where
a reading was one flag away.
**Premise re-verified before anything was written.** The card's repro is
from `53dd5aaa` on 2026-09-15 and `main` has moved. On `625db0e`
(2026-09-18, this container) it still reproduces exactly:
```
$ PR_NUMBER=18313 GITHUB_REPOSITORY=objectstack-ai/objectstack node scripts/check-single-claim-paths.mjs
Error: GitHub API 401 for /repos/objectstack-ai/objectstack/pulls/18313/files?per_page=100&page=1
exit 1
```
## What changed
One file, `scripts/check-single-claim-paths.mjs`, +117 / -1 across two
commits:
- imports `PROXY_FLAG`, `PROXY_REARM_GUARD` and `proxyRearmPlan` from
`scripts/pm/check-half-states.mjs`, and re-execs this process with
`--use-env-proxy` when a proxy is configured and this run is not already
routed through it;
- declares its own re-exec guard, `OS_SINGLE_CLAIM_PATHS_PROXY_REARMED`,
and presents it to the imported plan through a small pure
`proxyPlanEnv()`;
- performs the hand-off **only on the judging path** — a NOT WIRED run
reaches no network, so re-execing it would spend a process to reprint
the identical wiring verdict — and forwards the child's exit status
**verbatim**, so the documented `0` / `1` / `2` contract is unchanged;
- adds a nine-case self-test battery and raises the pinned
battery-roster floor from 7 to 8.
Nothing else is touched. The donor files named on the card stayed
read-only.
## The design fork the card leaves open is already settled in the tree
The card offers "lift the re-exec from
`scripts/pm/check-clause2-carriers.mjs` unchanged", and the real fork
behind that is a fourth verbatim copy versus extracting a shared helper.
Measured on `625db0e`, neither is what the tree wants, because the
helper already exists:
- `proxyRearmPlan` is an exported function with **13 importers** in this
repo — the card's "the fix exists three times" table counts performers,
not the decision;
- one importer is already **outside** `scripts/pm/`:
`scripts/check-issue-citations.mjs` takes it as
`./pm/check-half-states.mjs`, which is exactly this file's position in
the tree;
- `scripts/pm/check-expected-skips.mjs` pins the preference as a
self-test case of its own — `structural: the proxy plan is imported, not
restated`.
So this PR imports the decision and lifts only the roughly twenty-line
performer, which has to stay per-script for the reason in the next
section.
The bootstrapping constraint is not in the way: an ESM `import` is
resolved and evaluated before this module's body runs, and the hand-off
is decided at the dispatch, before the first network read. Import cost
measured at 96 ms wall, node startup included.
## Why the guard variable is local, and why that is not cosmetic
`proxyRearmPlan` reads one env name to decide "already re-armed once
this run". Four sibling instruments hand it `env: process.env`
unchanged, so they ask about the name the PLAN owns rather than their
own — and a sibling's guard then suppresses their re-exec. Measured
here, read-only, same box and same minute:
```
$ node scripts/pm/check-clause2-carriers.mjs --pair 18313
ℹ️ re-exec with --use-env-proxy: HTTPS_PROXY is set (...) and node's fetch does not read it.
...a real verdict. exit 2
$ OS_HALF_STATES_PROXY_REARMED=1 node scripts/pm/check-clause2-carriers.mjs --pair 18313
(no re-exec banner at all)
ℹ️ the token in GITHUB_TOKEN/GH_TOKEN was refused (HTTP 401); falling back to the token-less public read...
check-clause2-carriers: PREREQUISITE NOT MET — GET /repos/.../pulls?state=open... -> HTTP 403. exit 3
```
That is the "the credential died" false story of comment 5715705458,
produced mechanically by one inherited variable. This file therefore
maps its OWN guard onto the name the plan reads, and pins both halves:
its own guard stops its own loop, and a sibling's guard does not stop
it.
## Evidence
Every exit code captured before any pipe, into a file. Tree at
`7c3c674`, clean.
| what | result |
|---|---|
| the card's repro, after the fix | `exit 0` + the clean verdict, banner
on stderr |
| `node scripts/check-single-claim-paths.mjs --self-test` | `exit 0`, 74
cases pass (65 before) |
| NOT WIRED legs: no `PR_NUMBER` / no `GITHUB_REPOSITORY` / empty
`GITHUB_TOKEN` | `exit 2` each, byte-identical text, no re-exec
attempted |
| own guard pre-set, otherwise identical | no re-exec, `401`, `exit 1` —
the guard is what fires it |
| a sibling's guard pre-set, otherwise identical | re-execs, `exit 0` —
no cross-suppression |
| a non-zero child status (`PR_NUMBER=99999999`, a 404 in the child) |
`exit 1` reaches the caller through the hand-off |
| `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--commands` | 29 families derived from the real change set |
| all 29, re-run at final head `7c3c674` | 29 of 29 `exit 0` |
| `dispatch-gates --ran` reconciliation | 29 derived, 29 run, 0
NOT-MEASURED (derived from recorded exit codes), 0 UNRUN |
| `pnpm lint` — the repo-wide scan, not a narrowing | `exit 0`, 68 s, at
`7c3c674` |
## Ablation — both legs, on-disk mutation proved, restore proved
Through `scripts/ablation-replace.mjs`, which asserts the anchor count
fell, the blob hash moved, and that the restore left `git diff HEAD`
empty.
1. Delete the hand-off, run the self-test: `anchor 1 -> 0, blob
79d8c82 -> bf456081441a`, then `✗ structural: the hand-off is
CALLED exactly once, and decided BEFORE the first network read (got
[1,true], want [2,true])` — `1 of 74 case(s) failed`, and only that one.
2. Delete the hand-off, run the card's repro: `GitHub API 401 for
/repos/objectstack-ai/objectstack/pulls/18313/files...`, the exact
pre-fix failure.
Both legs restored with `blob == HEAD` and an empty `git diff HEAD`.
⭐ Planning leg 1 is what produced the second commit. The first version
of that pin compared only positions, and under the mutation the last
occurrence of the performer's name became its own **declaration** —
which sits above the collection and satisfied the comparison with no
hand-off left in the file at all. The ablation reported `true` for a
deleted call. Counting the call sites is the repair; the `got [1,true]`
above is the strengthened pin failing as it should.
## Changeset: `skip-changeset`
Nothing published moves. Measured rather than assumed: across the 70
published packages in this workspace, 217 `files[]` entries were scanned
and **zero** name a `scripts` path; `files[]` is package-relative and no
package root lives under `scripts/`, so a repo-root script cannot be
shipped by any of them. The root manifest is `private: true`. Positive
control on the scan: the same pass finds all 70 packages shipping a
`dist` entry, so it can see entries when they are there.
## Acceptance notes
Found while measuring, deliberately **not** fixed here — out of this
card's scope, reported for filing rather than folded in.
1. **A transport failure on this gate exits `1`, which its own header
defines as an accusation.** `PR_NUMBER=99999999
GITHUB_REPOSITORY=objectstack-ai/objectstack node
scripts/check-single-claim-paths.mjs` ends in an unhandled rejection and
`exit 1`, and the header says `1 judged, an earlier open PR already
claims a listed path` and that "a mis-wired gate must not read as an
accusation, because it would be red on every PR at once for something no
author did". Unchanged by this PR in both directions, and a new exit
code is a contract change, not an internal parameter.
2. **The self-test handshake cannot fire.** `selfTestReachedVerdict =
true` executes synchronously, before the async block that prints the
verdict, so an early `return` inside that block leaves the flag set —
the exact `#13798` hole the flag exists to close, and the opposite of
the rule in AGENTS.md ("Set the flag as the self-test's last statement,
after its success line prints").
3. **The four sibling instruments carry the guard hazard demonstrated
above** (`check-clause2-carriers`, `post-stamped`, `board-snapshot`,
`label-write`): each declares a local guard name nothing reads, and asks
the plan about the imported one. Donor files are read-only on this card
and #18376 is already open against one of them, so this is a report, not
an edit.
Noted, not filed: the card's suggested donor,
`scripts/pm/check-clause2-carriers.mjs`, returns 4 for the card's own
grep pattern, not the 3-of-4 shape the card's table implies; the counts
in the dispatch for `check-prerelease-pin-watch`,
`check-required-contexts` and `check-override-consistency` were not
re-derived because none of those files is the donor. Carrier: whoever
re-measures that table next; there may be none.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 46559f6 commit 16cb493
1 file changed
Lines changed: 122 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
145 | 145 | | |
146 | 146 | | |
147 | 147 | | |
| 148 | + | |
148 | 149 | | |
149 | 150 | | |
150 | 151 | | |
| 152 | + | |
151 | 153 | | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
152 | 160 | | |
153 | 161 | | |
154 | 162 | | |
| |||
182 | 190 | | |
183 | 191 | | |
184 | 192 | | |
| 193 | + | |
185 | 194 | | |
186 | 195 | | |
187 | 196 | | |
188 | 197 | | |
189 | | - | |
| 198 | + | |
190 | 199 | | |
191 | 200 | | |
192 | 201 | | |
| |||
262 | 271 | | |
263 | 272 | | |
264 | 273 | | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
265 | 277 | | |
266 | 278 | | |
267 | 279 | | |
| |||
532 | 544 | | |
533 | 545 | | |
534 | 546 | | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
| 611 | + | |
| 612 | + | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
| 624 | + | |
535 | 625 | | |
536 | 626 | | |
537 | 627 | | |
| |||
676 | 766 | | |
677 | 767 | | |
678 | 768 | | |
| 769 | + | |
| 770 | + | |
| 771 | + | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
| 775 | + | |
| 776 | + | |
| 777 | + | |
| 778 | + | |
| 779 | + | |
| 780 | + | |
| 781 | + | |
| 782 | + | |
| 783 | + | |
| 784 | + | |
| 785 | + | |
| 786 | + | |
| 787 | + | |
| 788 | + | |
| 789 | + | |
| 790 | + | |
| 791 | + | |
| 792 | + | |
679 | 793 | | |
680 | 794 | | |
681 | 795 | | |
| |||
777 | 891 | | |
778 | 892 | | |
779 | 893 | | |
| 894 | + | |
| 895 | + | |
| 896 | + | |
| 897 | + | |
| 898 | + | |
| 899 | + | |
| 900 | + | |
780 | 901 | | |
781 | 902 | | |
782 | 903 | | |
| |||
0 commit comments