You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 0f42d36
Browse filesBrowse the repository at this point in the historyBrowse files
fix(scripts): check-issue-citations re-execs through the proxy instead of prescribing it (#19254)
Fixes#18949
## What was wrong
`scripts/check-issue-citations.mjs` imported `proxyRearmPlan` and used
it only to
compose a hint string. In a container whose only route out is
`HTTPS_PROXY` — which
node's `fetch` does not read — every board read answered HTTP 401, so a
live run
refused with `PREREQUISITE NOT MET` (exit 3) while holding, and
printing, the plan
that would have completed the read. Loud and correct, one re-exec short
of an answer.
## What changed (one file: `scripts/check-issue-citations.mjs`)
`rearmThroughProxy()` now EXECUTES that plan, on the shape
`scripts/pm/post-stamped.mjs` and `scripts/pm/board-snapshot.mjs` have
run daily:
re-exec this file under `--use-env-proxy`, adopt the child's exit code,
degrade
loudly when the re-exec cannot happen. All three arms are kept and each
is an answer:
| arm | behaviour |
|---|---|
| `plan.hint` | print why the route cannot be re-armed, carry on, refuse
below if the read fails |
| `plan.rearm` | re-exec with the flag and adopt the child's exit code |
| re-exec failed | continue in-process **and say** every request will
bypass the proxy |
The guard is **this gate's own** variable,
`OS_ISSUE_CITATIONS_PROXY_REARMED` — never
a shared or inherited name. A sibling instrument's guard answering
"already re-armed"
here is silent, and turns a route failure into a false story about the
credential.
The re-exec is taken by the live modes only: `--self-test` is offline
(and pinned
offline) and `--list` makes no request, so neither spawns a child to
prove a route
nothing is about to use.
## Evidence (this container)
**Before** — `node scripts/check-issue-citations.mjs --census`,
`exit=3`:
```text
❌ check-issue-citations: PREREQUISITE NOT MET — the board was not read.
GET https://api.github.com/repos/objectstack-ai/objectstack/issues?state=all&per_page=100&sort=created&direction=asc -> HTTP 401 (HTTPS_PROXY is set (http://127.0.0.1:36723) and node's fetch does not read it — re-run with `node --use-env-proxy …` or NODE_USE_ENV_PROXY=1)
⛔ Nothing below is a reading. …
```
**After** — same command, `exit=0`, the board is actually read:
```text
ℹ️ re-exec with --use-env-proxy: HTTPS_PROXY is set (http://127.0.0.1:36723) and node's fetch does not read it.
board: enumerated (175 pages), frontier #19252, 17411 numbers resolve
citations judged: 34710 across 2405 file(s)
29805 resolves
2167 allocated-but-absent
1460 resolves-as-pull-request
1278 cross-repo-unjudged
…
ℹ️ census: 2167 unresolvable citation site(s). Report-only — this mode never fails.
```
## "Cannot read" was NOT converted into "silently passes"
Three genuinely unreadable runs, each still `exit=3` with `PREREQUISITE
NOT MET`:
```text
A OS_ISSUE_CITATIONS_PROXY_REARMED=1 (the guard already spent) -> exit 3
ℹ️ … the one re-exec this run allows was already spent … unsetting
OS_ISSUE_CITATIONS_PROXY_REARMED is the fix rather than a new token
❌ check-issue-citations: PREREQUISITE NOT MET — the board was not read.
B HTTPS_PROXY=http://127.0.0.1:1 (re-exec happens, the child cannot reach out) -> exit 3
ℹ️ re-exec with --use-env-proxy: …
❌ check-issue-citations: PREREQUISITE NOT MET — the board was not read.
fetch failed
C no proxy, no token at all -> exit 3
❌ check-issue-citations: PREREQUISITE NOT MET — the board was not read.
GET …/issues?state=all… -> HTTP 403
```
⚠️ Note for whoever repeats this: a bare "no token" run is NOT
unreadable in an agent
container — this box's proxy carries the credential, so `--census` with
`GITHUB_TOKEN`/`GH_TOKEN` unset still reads the board and exits 0. The
three cases
above are constructed so the read genuinely cannot happen.
**The third arm, proven by ablation** (mutation confirmed on disk —
anchor gone,
marker present — then restored, `git diff HEAD` empty and the blob hash
back to
`e7a3516c5`): with `process.execPath` replaced by a path that does not
exist,
```text
ℹ️ re-exec with --use-env-proxy: …
⚠️ could not re-exec with --use-env-proxy (spawnSync /nonexistent/node-ABLATION ENOENT); continuing in-process — every request will bypass the proxy.
❌ check-issue-citations: PREREQUISITE NOT MET — the board was not read. exit 3
```
## Tests
A new `proxy-rearm` self-test battery (10 cases, floor pinned) covers
the guard name
against the patrol's, all three arms, the Actions-runner leg (no proxy ⇒
no re-exec,
not one extra line), and structurally: the plan is imported rather than
restated, the
dispatch really hands it THIS file's guard, and the re-exec sits in the
live-mode arm
only. `--self-test` stays offline.
```text
✅ check-issue-citations --self-test: … (66 cases, 7 batteries) # was 56 cases, 6 batteries
```
All 26 commands from `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack`
(derived against this branch's own change set) exit 0, plus `pnpm
check:issue-citations`.
`npx eslint scripts/check-issue-citations.mjs --no-inline-config` exits
0.
## Declared gaps — ⛔ not fixed here, and not claimed to be
- **CI wiring is untouched.** No workflow invokes this gate; that is
#18224's lane and
it is not addressed here. A local re-exec changes nothing in CI, and the
CI wiring
changes nothing locally.
- `scripts/pm/check-half-states.mjs`,
`scripts/pm/check-governed-merges.mjs`
(`proxyRearmPlan` itself) and `scripts/pm/post-stamped.mjs` are
untouched — other surfaces.
- No changeset: this file ships in no package's `files[]` and the root
manifest is
`private: true`, so nothing released moves. The PR carries
`skip-changeset`, which
`.github/workflows/lint.yml` names as the textbook case for a
CI-internal script.
## Acceptance notes
- noted, not filed: the census now reads 2,167 `allocated-but-absent`
sites against a
frontier of #19252 (the card's own 2026-09-14 reading was 2,785 against
#18219) — a
standing, report-only debt this gate declares by design, not a finding.
Owner of a
follow-up: none identified.
- noted, not filed: `OS_<TOOL>_PROXY_REARMED` is now spelled out by hand
in six
instruments (`post-stamped`, `board-snapshot`, `check-expected-skips`,
`check-clause2-carriers`, `check-governed-merges`, and this gate); each
correctly
owns its name, so there is no defect, only a convention nothing
enumerates.
---
_Generated by [Claude
Code](https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk)_
Co-authored-by: Claude <noreply@anthropic.com>
console.error(`⚠️ could not re-exec with ${plan.flag} (${child.error?.message??'no exit status'}); continuing in-process — every request will bypass the proxy.`);
700
+
returnnull;
701
+
}
702
+
623
703
functionprerequisiteRefusal(message){
624
704
console.error('❌ check-issue-citations: PREREQUISITE NOT MET — the board was not read.');
625
705
console.error(` ${message}`);
@@ -646,8 +726,18 @@ export async function run({ root = process.cwd(), scope = 'diff', base = null, j
/** Deleting a roster entry silences its floor, so the roster's size is pinned too. */
@@ -920,6 +1011,35 @@ export async function selfTest() {
920
1011
check(live.rows.some((r)=>r.qualifier),'the live corpus must contain at least one cross-repo citation — the arm that must never be reported as a phantom');
921
1012
}
922
1013
1014
+
/* 7. THE LOCAL ROUTE. Offline, over the imported plan: the arms that decide
1015
+
* whether a live run re-execs, and the guard-name mix-up whose only
1016
+
* symptom is a 401 blamed on the token. ⛔ Nothing here re-execs anything —
1017
+
* a self-test that spawned a child would be a self-test with a network. */
0 commit comments