Skip to content

Commit 1f4a93e

Browse files
Jack Qclaude
andauthored
fix(pm): make repo-not-visible/bad-credential verdicts caller-neutral (#10567)
check-half-states.mjs's classifyTransportProbe/classifyRepoRead is shared with ci-failure.mjs (#9966), a caller that runs no "sweep" and reads no "board". #10156 neutralized repo-scope-refused and host-unreachable; this applies the same fix family to the remaining three verdicts: repo-not-visible, bad-credential, and bad-credential-anon-reachable. Rewrites their headline/fix prose to describe what the CONTAINER/ credential cannot do, and pins the new wording with self-test cases following #10156/PR #10445's pattern. Fixes #10443 Claude-Session: https://claude.ai/code/session_019T1sSZbQTnLhrK9HhNdNiB Co-authored-by: Claude <noreply@anthropic.com>
1 parent b34ef8d commit 1f4a93e

1 file changed

Lines changed: 48 additions & 5 deletions

File tree

scripts/pm/check-half-states.mjs

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3408,7 +3408,7 @@ function classifyRepoRead(tok, primary, repo) {
34083408
if (repo.status === 404) {
34093409
return {
34103410
kind: 'repo-not-visible',
3411-
headline: `\`${OWNER_REPO}\` is not visible to this identity — the sweep would list nothing`,
3411+
headline: `\`${OWNER_REPO}\` is not visible to this identity — this container cannot read it`,
34123412
detail: [
34133413
`\`GET /rate_limit\` -> ${describeProbe(primary)}, but \`GET /repos/${OWNER_REPO}\` -> HTTP 404.`,
34143414
``,
@@ -3559,7 +3559,7 @@ export function classifyTransportProbe(obs) {
35593559
return {
35603560
kind: anonWorks ? 'bad-credential-anon-reachable' : 'bad-credential',
35613561
headline: anonWorks
3562-
? 'the token in the environment is not a valid GitHub credential — and it is the ONLY thing stopping the sweep'
3562+
? 'the token in the environment is not a valid GitHub credential — and it is the ONLY thing stopping this container from reading'
35633563
: 'the token in the environment is not a valid GitHub credential',
35643564
detail: [
35653565
`\`GET /rate_limit\` with GITHUB_TOKEN/GH_TOKEN = ${tok.redacted} -> ${describeProbe(primary)}.`,
@@ -3586,9 +3586,9 @@ export function classifyTransportProbe(obs) {
35863586
? [
35873587
'GITHUB_TOKEN= GH_TOKEN= node scripts/pm/check-half-states.mjs',
35883588
' ↑ anonymous is 60 req/h and that quota is per EGRESS IP, shared with every',
3589-
' other container behind it. This sweep spends one request per label page plus',
3590-
' one per assigned pm-tracked card, so it can exhaust mid-run — which surfaces',
3591-
' as another PREREQUISITE NOT MET, never as a short finding list.',
3589+
' other container behind it. A request-heavy run can exhaust it mid-run,',
3590+
' which then surfaces as another PREREQUISITE NOT MET, never as a short',
3591+
' finding list.',
35923592
]
35933593
: ['export GITHUB_TOKEN=<a real GitHub token> and re-run (see the anonymous reading above).'],
35943594
};
@@ -6191,6 +6191,32 @@ function selfTest() {
61916191
t('#7412 class 3 (cloud dev, measured): 401 + exhausted anon -> bad-credential', class3?.kind, 'bad-credential');
61926192
t('…and it does NOT prescribe the token-less re-run', class3.fix.join(' ').includes('GITHUB_TOKEN= GH_TOKEN='), false);
61936193
t('…and it names a real credential as the remedy', class3.fix[0].includes('a real GitHub token'), true);
6194+
// #10443: bad-credential and bad-credential-anon-reachable are shared with
6195+
// ci-failure.mjs (#9966), same as repo-scope-refused/host-unreachable/
6196+
// repo-not-visible above — a caller that runs no "sweep" and reads no
6197+
// "board". Cover both branches (anon-reachable and not).
6198+
{
6199+
const anonReachable = classifyTransportProbe({
6200+
token: 'prox_abcdefghi',
6201+
authed: { status: 401, rateLimitRemaining: null },
6202+
anon: { status: 200, rateLimitRemaining: 59 },
6203+
});
6204+
t(
6205+
"…and none of bad-credential-anon-reachable's prose names the sweep or the board read",
6206+
/the sweep|the board read/.test([anonReachable.headline, ...anonReachable.detail, ...anonReachable.fix].join(' ')),
6207+
false,
6208+
);
6209+
t(
6210+
'…and its headline instead speaks caller-neutrally about the container',
6211+
anonReachable.headline.includes('stopping this container from reading'),
6212+
true,
6213+
);
6214+
}
6215+
t(
6216+
"…and none of bad-credential's (anon-unusable) prose names the sweep or the board read",
6217+
/the sweep|the board read/.test([class3.headline, ...class3.detail, ...class3.fix].join(' ')),
6218+
false,
6219+
);
61946220

61956221
// Class 4 — proxy-mediated cloud session, measured 2026-08-19 (#9946). The
61966222
// account-scoped reading is not merely 200: it is GENUINELY GitHub's, with a
@@ -6277,6 +6303,23 @@ function selfTest() {
62776303
classifyTransportProbe({ ...class4, repo: { status: 404, rateLimitRemaining: 4999 } }).fix[0].includes('PM_SWEEP_REPO'),
62786304
true,
62796305
);
6306+
// #10443: repo-not-visible is shared with ci-failure.mjs (#9966) exactly like
6307+
// repo-scope-refused above — a caller that runs no "sweep" and reads no
6308+
// "board", so its headline/detail/fix must describe what the CONTAINER
6309+
// cannot do, never what a caller-specific "sweep" would have found.
6310+
{
6311+
const notVisible404 = classifyTransportProbe({ ...class4, repo: { status: 404, rateLimitRemaining: 4999 } });
6312+
t(
6313+
"…and none of repo-not-visible's prose (headline, detail, fix) names the sweep or the board read",
6314+
/the sweep|the board read/.test([notVisible404.headline, ...notVisible404.detail, ...notVisible404.fix].join(' ')),
6315+
false,
6316+
);
6317+
t(
6318+
'…and its headline instead speaks caller-neutrally about the container',
6319+
notVisible404.headline.includes('this container cannot read it'),
6320+
true,
6321+
);
6322+
}
62806323
// A quota genuinely spent between the two stages wears the same 403 and has a
62816324
// completely different remedy — it must not be reported as a scope refusal.
62826325
t('repo-scoped 403 with remaining 0 is the quota, not a scope refusal', kind({ ...class4, repo: { status: 403, rateLimitRemaining: 0 } }), 'rate-limited');

0 commit comments

Comments
 (0)