@@ -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+ / t h e s w e e p | t h e b o a r d r e a d / . 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+ / t h e s w e e p | t h e b o a r d r e a d / . 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+ / t h e s w e e p | t h e b o a r d r e a d / . 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