Skip to content

Commit ee17684

Browse files
committed
merge origin/main into claude/issue-16045-declaration-text-snapshots
Claude-Session: https://claude.ai/code/session_019srGWGCBBCBHqcDoRZpQRh Co-authored-by: Claude <noreply@anthropic.com>
2 parents c16b04b + 16cb493 commit ee17684

1 file changed

Lines changed: 122 additions & 1 deletion

File tree

‎scripts/check-single-claim-paths.mjs‎

Lines changed: 122 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,18 @@
145145
* names a tree that does not exist, so a hint on one can never match anything.
146146
*/
147147

148+
import { spawnSync } from 'node:child_process';
148149
import { existsSync, readFileSync } from 'node:fs';
149150
import { join } from 'node:path';
150151
import process from 'node:process';
152+
import { fileURLToPath } from 'node:url';
151153
import { isEntrypoint } from './invoked-as.mjs';
154+
import { maskCommentsAndLiterals } from './js-comment-mask.mjs';
155+
// ⛔ Not copied. The proxy-rearm PLAN is ONE source for every instrument in this
156+
// tree, so this gate and the sweeps can never disagree about whether this
157+
// container's fetch reaches GitHub at all. Only the guard variable below is
158+
// this file's own, and the block above `rearmThroughProxy` says why.
159+
import { PROXY_FLAG, PROXY_REARM_GUARD, proxyRearmPlan } from './pm/check-half-states.mjs';
152160

153161
// ── The self-test's own battery roster and floor (#13489) ──────────────────
154162
//
@@ -182,11 +190,12 @@ const SELF_TEST_BATTERIES = Object.freeze({
182190
'UNDETERMINED is its own answer. It must never read as clean, and it': 5,
183191
'Wiring absent: never clean, never an accusation.': 16,
184192
'The short-circuit. This is the property that makes the gate affordable,': 22,
193+
'The route to GitHub. A 401 from a bypassed proxy reads as a dead': 9,
185194
});
186195

187196
// DELETING an entry silences that battery's floor exactly as effectively as
188197
// zeroing it, so the roster's own size is pinned too.
189-
const SELF_TEST_BATTERY_FLOOR = 7;
198+
const SELF_TEST_BATTERY_FLOOR = 8;
190199

191200
// The key an assertion is filed under when no battery is open. It is not a
192201
// declared battery, so it reds by the same set difference rather than silently
@@ -262,6 +271,9 @@ function batteryFloorFailures() {
262271

263272
const ROOT = new URL('..', import.meta.url).pathname;
264273

274+
/** This file, resolved for the proxy re-exec and for the self-test's own structural reads. */
275+
const SELF_PATH = fileURLToPath(import.meta.url);
276+
265277
/** The wiring that gives this gate a PR to judge. */
266278
const WIRING_WORKFLOW = '.github/workflows/single-claim-path-guard.yml';
267279

@@ -532,6 +544,84 @@ const githubApi = (token) => async (path) => {
532544
return response.json();
533545
};
534546

547+
// ---------------------------------------------------------------------------
548+
// The route to GitHub (#18314) — why this gate re-execs itself.
549+
//
550+
// Every real run reads a PR's file list over the network, and in an agent
551+
// container that network is reachable only through the session proxy. Node's
552+
// `fetch` does not read the proxy variables, so an unrouted run left WITHOUT
553+
// its credential and GitHub answered 401. That 401 has three readings available
554+
// to whoever sees it — the token is wrong, the PR does not exist, the repo is
555+
// wrong — and none of them is the true one, so a seat that tries to pre-run the
556+
// gate its own PR will be judged by records NOT MEASURED where a real reading
557+
// was one flag away.
558+
//
559+
// The switch is read at process START, which is the whole reason this is a
560+
// re-exec rather than an assignment.
561+
//
562+
// The DECISION is imported, not restated: one spelling of "go through the
563+
// proxy" for the whole tree. What is local is the guard VARIABLE, deliberately.
564+
// Sharing a sibling's guard would let that sibling's re-exec suppress this
565+
// one's — a grandchild is spawned without the flag in its argv, so it needs the
566+
// re-exec even though the guard the parent set says one already happened.
567+
//
568+
// ⭐ On a GitHub Actions runner there is no proxy in the environment, so the
569+
// plan never re-arms and this gate behaves exactly as it did before. The fix is
570+
// inert in CI, which is all this card ever claimed about CI.
571+
// ---------------------------------------------------------------------------
572+
573+
/** This file's OWN re-exec guard — deliberately not any sibling's. */
574+
export const OWN_PROXY_REARM_GUARD = 'OS_SINGLE_CLAIM_PATHS_PROXY_REARMED';
575+
576+
/**
577+
* The environment the imported plan is asked about: this file's own guard,
578+
* presented under the name the plan reads. Pure, so the self-test drives every
579+
* branch offline — and the branch worth pinning is a SIBLING's guard being set,
580+
* which must NOT stop this run from re-arming.
581+
*/
582+
export function proxyPlanEnv(env) {
583+
return { ...env, [PROXY_REARM_GUARD]: env[OWN_PROXY_REARM_GUARD] };
584+
}
585+
586+
/**
587+
* Hand this run off through the proxy, or `null` to carry on in-process.
588+
*
589+
* A returned number is the child's exit status, forwarded VERBATIM: the three
590+
* exit codes above are this gate's contract with CI, so the hand-off has to be
591+
* invisible in them.
592+
*
593+
* Everything printed here goes to STDERR. The clean verdict is this script's
594+
* only stdout, and a status line there would land inside whatever reads it.
595+
*
596+
* @param {string[]} args this run's own argv tail, forwarded unchanged
597+
*/
598+
function rearmThroughProxy(args) {
599+
const plan = proxyRearmPlan({
600+
env: proxyPlanEnv(process.env),
601+
execArgv: process.execArgv,
602+
flagSupported: process.allowedNodeEnvironmentFlags.has(PROXY_FLAG),
603+
});
604+
if (plan.hint) {
605+
console.error(`ℹ️ ${plan.reason}. A refusal below may be about the route, not this container.`);
606+
return null;
607+
}
608+
if (!plan.rearm) return null;
609+
console.error(`ℹ️ re-exec with ${plan.flag}: ${plan.reason}.`);
610+
// The env proxy agent is experimental and says so once per run. Nobody can
611+
// act on that notice, so silence it where the node in use can.
612+
const quiet = process.allowedNodeEnvironmentFlags.has('--disable-warning') ? ['--disable-warning=UNDICI-EHPA'] : [];
613+
const child = spawnSync(process.execPath, [plan.flag, ...quiet, SELF_PATH, ...args], {
614+
stdio: 'inherit',
615+
env: { ...process.env, [OWN_PROXY_REARM_GUARD]: '1' },
616+
});
617+
if (typeof child.status === 'number') return child.status;
618+
console.error(
619+
`⚠️ could not re-exec with ${plan.flag} (${child.error?.message ?? 'no exit status'}); ` +
620+
'continuing in-process — every request will bypass the proxy.',
621+
);
622+
return null;
623+
}
624+
535625
// ---------------------------------------------------------------------------
536626
// Self-test — the verdict layer, the exit-code contract, the declared list's
537627
// own invariants, the short-circuit that makes this affordable, and the wiring.
@@ -676,6 +766,30 @@ function selfTest() {
676766
{ number: '16326', repo: 'o/r', token: 't' },
677767
);
678768

769+
// --- The route to GitHub. A 401 from a bypassed proxy reads as a dead
770+
// credential, so both legs are pinned rather than one: the CI leg must stay
771+
// untouched (no proxy in the environment, no re-exec, no behaviour change at
772+
// all), and a container must re-arm EXACTLY once. The fixture proxy names no
773+
// tree in any repo, per this file's header rule on quoted literals.
774+
battery('The route to GitHub. A 401 from a bypassed proxy reads as a dead');
775+
const proxied = { HTTPS_PROXY: 'http://127.0.0.1:41733' };
776+
t('the Actions-runner leg: no proxy in the env, so no re-exec and nothing changes in CI', proxyRearmPlan({ env: proxyPlanEnv({}) }).rearm, false);
777+
t('an agent container re-arms, which is the whole of the fix', proxyRearmPlan({ env: proxyPlanEnv(proxied), flagSupported: true }).rearm, true);
778+
t("...exactly once — this run's OWN guard is what stops the loop", proxyRearmPlan({ env: proxyPlanEnv({ ...proxied, [OWN_PROXY_REARM_GUARD]: '1' }), flagSupported: true }).rearm, false);
779+
t("...and a SIBLING instrument's guard does NOT suppress it", proxyRearmPlan({ env: proxyPlanEnv({ ...proxied, [PROXY_REARM_GUARD]: '1' }), flagSupported: true }).rearm, true);
780+
t("this file's guard is not the imported one, which is what makes that hold", OWN_PROXY_REARM_GUARD === PROXY_REARM_GUARD, false);
781+
t('a run already routed through the proxy does not re-arm again', proxyRearmPlan({ env: proxyPlanEnv(proxied), execArgv: [PROXY_FLAG], flagSupported: true }).rearm, false);
782+
const unsupported = proxyRearmPlan({ env: proxyPlanEnv(proxied), flagSupported: false });
783+
t('a node that cannot take the flag SAYS so rather than bypassing silently', [unsupported.rearm, unsupported.hint], [false, true]);
784+
const ownSource = maskCommentsAndLiterals(readFileSync(SELF_PATH, 'utf8'));
785+
t('structural: the plan is imported, not restated here', /\bproxyRearmPlan\b/.test(ownSource) && !/function\s+proxyRearmPlan\b/.test(ownSource), true);
786+
// Both halves, because the ablation that planned this case found the
787+
// ordering alone vacuous: with the CALL deleted, the last occurrence is the
788+
// DECLARATION, which sits above the collection and satisfied the comparison
789+
// with no hand-off left in the file at all.
790+
const rearmSites = ownSource.split('rearmThroughProxy(').length - 1;
791+
t('structural: the hand-off is CALLED exactly once, and decided BEFORE the first network read', [rearmSites, ownSource.lastIndexOf('rearmThroughProxy(') < ownSource.lastIndexOf('await collect(')], [2, true]);
792+
679793
// --- The short-circuit. This is the property that makes the gate affordable,
680794
// and it is invisible in the verdict layer, so it is pinned here against a
681795
// recording fake API. Fixture paths name a tree that exists in no repo.
@@ -777,6 +891,13 @@ if (isMain) {
777891
}
778892
} else {
779893
const ctx = readPrContext(process.env);
894+
// Only a run with a usable context reaches the network, so only that run
895+
// needs the route. Re-execing a NOT WIRED run would spend a process to
896+
// reprint the identical wiring verdict.
897+
if (ctx !== null && ctx.wired !== false) {
898+
const handed = rearmThroughProxy(process.argv.slice(2));
899+
if (handed !== null) process.exit(handed);
900+
}
780901
const resolved = ctx === null || ctx.wired === false ? ctx : await collect(ctx, githubApi(ctx.token));
781902
const result = judge(resolved);
782903
const emit = result.exit === EXIT_CLEAN ? console.log : console.error;

0 commit comments

Comments
 (0)