@@ -120,6 +120,52 @@ import { join, resolve } from 'node:path';
120120import { maskComments } from './js-comment-mask.mjs' ;
121121import { isEntrypoint } from './invoked-as.mjs' ;
122122
123+ // ── The self-test's own battery roster and floor (#13489) ──────────────────
124+ //
125+ // `failures.length === 0` used to be this self-test's ONLY success condition, so
126+ // "every case held" and "the cases never ran" printed the same line. Closed the
127+ // way PR #13487 validated on check-doc-authoring: what is pinned is the
128+ // registered NAMES, not a number. Every section opens with `battery('<name>')`,
129+ // every assertion is attributed to the battery most recently opened, and the
130+ // floor requires the OPENED set to equal the DECLARED set with each battery at
131+ // or above its own count.
132+ //
133+ // ⛔ A pinned TOTAL is not the repair: a battery dropping from 9 cases to 3
134+ // keeps a total "right" the moment a sibling grows.
135+ //
136+ // The counts are a FLOOR, not an equality — adding cases is ordinary work and
137+ // must not red. A battery BELOW its floor means cases stopped running; the
138+ // remedy is to find what stopped registering.
139+ const SELF_TEST_BATTERIES = Object . freeze ( {
140+ 'The base path is DERIVED, and its absence is not an empty population.' : 2 ,
141+ 'LOAD-BEARING NEGATIVE: a mount with an exact row is clean.' : 1 ,
142+ 'LOAD-BEARING POSITIVE: a mount added with no row REDDENS, naming the route.' : 2 ,
143+ 'THE RIGHT BOUNDARY, both directions. This is the defect class #10534 fell into.' : 4 ,
144+ 'The method is part of the identity: same path, different verb, is a different route.' : 1 ,
145+ 'CONSTRAINT 3: the lanes are excluded, and adding one does not redden.' : 2 ,
146+ 'Mounts that are not under basePath are not this ledger\'s business.' : 2 ,
147+ 'A commented-out mount is not a mount.' : 2 ,
148+ 'A string-literal mount under basePath is still a mount (no `${basePath}` required).' : 1 ,
149+ 'CONSTRAINT 4: what cannot be read is reported, never skipped.' : 3 ,
150+ 'The vendor inventory accounts for a shadowing mount, and says so.' : 2 ,
151+ 'The rationale half: a pasted row does not satisfy this gate.' : 5 ,
152+ 'A row whose mount is gone fails (the direction the hand-written pin already had).' : 1 ,
153+ 'PENDING_DISPOSITION, reconciled in BOTH directions.' : 5 ,
154+ '#8435 remedy authority. PLACEMENT is pinned here, per-gate, because the' : 4 ,
155+ 'Parse anchors: a moved anchor is a REFUSAL input, never an empty population.' : 2 ,
156+ 'The escaped-quote shape the real notes use is measured, not truncated.' : 1 ,
157+ 'And the real inputs on disk are readable, so the anchors have not moved.' : 2 ,
158+ } ) ;
159+
160+ // DELETING an entry silences that battery's floor exactly as effectively as
161+ // zeroing it, so the roster's own size is pinned too.
162+ const SELF_TEST_BATTERY_FLOOR = 18 ;
163+
164+ // The key an assertion is filed under when no battery is open. It is not a
165+ // declared battery, so it reds by the same set difference rather than silently
166+ // inflating whichever battery happened to run last.
167+ const UNATTRIBUTED_BATTERY = '(no battery open)' ;
168+
123169const ROOT = resolve ( new URL ( '..' , import . meta. url ) . pathname ) ;
124170
125171/** The two inputs. Module-scope literals, so `dispatch-gates` derives this
@@ -558,16 +604,32 @@ const REAL_NOTE =
558604let selfTestReachedVerdict = false ;
559605
560606function selfTest ( ) {
607+ // The battery ledger this self-test's floor is evaluated against (#13489).
608+ // `battery()` opens a battery; every assertion below is attributed to the one
609+ // most recently opened, so a section that stops running stops registering and
610+ // names ITSELF at the floor rather than going quiet.
611+ const batterySeen = new Map ( ) ;
612+ let openBattery = null ;
613+ const battery = ( name ) => {
614+ openBattery = name ;
615+ } ;
616+ const registerCase = ( ) => {
617+ const b = openBattery ?? UNATTRIBUTED_BATTERY ;
618+ batterySeen . set ( b , ( batterySeen . get ( b ) ?? 0 ) + 1 ) ;
619+ } ;
620+
561621 const fail = [ ] ;
562622 let cases = 0 ;
563- const ok = ( cond , what ) => { cases += 1 ; if ( ! cond ) fail . push ( what ) ; } ;
623+ const ok = ( cond , what ) => { registerCase ( ) ; cases += 1 ; if ( ! cond ) fail . push ( what ) ; } ;
564624 const kinds = ( r ) => r . findings . map ( ( f ) => f . kind ) . sort ( ) ;
565625
566626 // -- The base path is DERIVED, and its absence is not an empty population.
627+ battery ( 'The base path is DERIVED, and its absence is not an empty population.' ) ;
567628 ok ( deriveBasePath ( FIXTURE_PREAMBLE ) === FIXTURE_BASE , 'basePath was not derived from the plugin' ) ;
568629 ok ( deriveBasePath ( 'const basePath = 42;' ) === null , 'a plugin with no derivable basePath did not refuse' ) ;
569630
570631 // -- LOAD-BEARING NEGATIVE: a mount with an exact row is clean.
632+ battery ( 'LOAD-BEARING NEGATIVE: a mount with an exact row is clean.' ) ;
571633 ok (
572634 runFixture (
573635 'rawApp.post(`${basePath}/admin/unlock-user`, h);' ,
@@ -577,6 +639,7 @@ function selfTest() {
577639 ) ;
578640
579641 // -- LOAD-BEARING POSITIVE: a mount added with no row REDDENS, naming the route.
642+ battery ( 'LOAD-BEARING POSITIVE: a mount added with no row REDDENS, naming the route.' ) ;
580643 {
581644 const r = runFixture ( 'rawApp.post(`${basePath}/admin/zzz-new`, h);' , [ ] ) ;
582645 ok ( kinds ( r ) . includes ( 'unaccounted-mount' ) , 'an unledgered mount did not redden the gate' ) ;
@@ -587,6 +650,7 @@ function selfTest() {
587650 }
588651
589652 // -- THE RIGHT BOUNDARY, both directions. This is the defect class #10534 fell into.
653+ battery ( 'THE RIGHT BOUNDARY, both directions. This is the defect class #10534 fell into.' ) ;
590654 {
591655 // The shorter route is mounted; only the LONGER sibling is ledgered.
592656 const r = runFixture (
@@ -621,6 +685,7 @@ function selfTest() {
621685 }
622686
623687 // -- The method is part of the identity: same path, different verb, is a different route.
688+ battery ( 'The method is part of the identity: same path, different verb, is a different route.' ) ;
624689 ok (
625690 runFixture (
626691 'rawApp.get(`${basePath}/config`, h);' ,
@@ -630,6 +695,7 @@ function selfTest() {
630695 ) ;
631696
632697 // -- CONSTRAINT 3: the lanes are excluded, and adding one does not redden.
698+ battery ( 'CONSTRAINT 3: the lanes are excluded, and adding one does not redden.' ) ;
633699 {
634700 const r = runFixture (
635701 'rawApp.all(`${basePath}/*`, h);\n' +
@@ -642,6 +708,7 @@ function selfTest() {
642708 }
643709
644710 // -- Mounts that are not under basePath are not this ledger's business.
711+ battery ( 'Mounts that are not under basePath are not this ledger\'s business.' ) ;
645712 ok (
646713 runFixture ( "rawApp.get('/.well-known/openid-configuration', h);" , [ ] ) . findings . length === 0 ,
647714 'a .well-known mount outside basePath was treated as an auth-ledger mount' ,
@@ -652,6 +719,7 @@ function selfTest() {
652719 ) ;
653720
654721 // -- A commented-out mount is not a mount.
722+ battery ( 'A commented-out mount is not a mount.' ) ;
655723 ok (
656724 runFixture ( '// rawApp.post(`${basePath}/admin/ghost`, h);' , [ ] ) . findings . length === 0 ,
657725 'a commented-out mount was counted -- comment masking is not reaching the scan' ,
@@ -662,12 +730,14 @@ function selfTest() {
662730 ) ;
663731
664732 // -- A string-literal mount under basePath is still a mount (no `${basePath}` required).
733+ battery ( 'A string-literal mount under basePath is still a mount (no `${basePath}` required).' ) ;
665734 ok (
666735 runFixture ( "rawApp.post('/api/v1/auth/admin/literal', h);" , [ ] ) . findings . some ( ( f ) => f . text . includes ( '/admin/literal' ) ) ,
667736 'a mount written with a literal path instead of the template bypassed the census' ,
668737 ) ;
669738
670739 // -- CONSTRAINT 4: what cannot be read is reported, never skipped.
740+ battery ( 'CONSTRAINT 4: what cannot be read is reported, never skipped.' ) ;
671741 ok (
672742 kinds ( runFixture ( "rawApp.on('POST', `${basePath}/x`, h);" , [ ] ) ) . includes ( 'unreadable-mount' ) ,
673743 'rawApp.on(...) was silently skipped instead of reported' ,
@@ -682,6 +752,7 @@ function selfTest() {
682752 ) ;
683753
684754 // -- The vendor inventory accounts for a shadowing mount, and says so.
755+ battery ( 'The vendor inventory accounts for a shadowing mount, and says so.' ) ;
685756 {
686757 const r = runFixture (
687758 'rawApp.post(`${basePath}/admin/ban-user`, h);' ,
@@ -693,6 +764,7 @@ function selfTest() {
693764 }
694765
695766 // -- The rationale half: a pasted row does not satisfy this gate.
767+ battery ( 'The rationale half: a pasted row does not satisfy this gate.' ) ;
696768 ok (
697769 kinds ( runFixture (
698770 'rawApp.post(`${basePath}/admin/pasted`, h);' ,
@@ -731,6 +803,7 @@ function selfTest() {
731803 ) ;
732804
733805 // -- A row whose mount is gone fails (the direction the hand-written pin already had).
806+ battery ( 'A row whose mount is gone fails (the direction the hand-written pin already had).' ) ;
734807 ok (
735808 kinds ( runFixture (
736809 '' ,
@@ -740,6 +813,7 @@ function selfTest() {
740813 ) ;
741814
742815 // -- PENDING_DISPOSITION, reconciled in BOTH directions.
816+ battery ( 'PENDING_DISPOSITION, reconciled in BOTH directions.' ) ;
743817 {
744818 const mount = 'rawApp.post(`${basePath}/set-initial-password`, h);' ;
745819 const p = [ { route : 'POST /api/v1/auth/set-initial-password' , issue : '#10975' , why : 'x' } ] ;
@@ -776,6 +850,7 @@ function selfTest() {
776850 // farm-wide sweep deliberately checks only PRESENCE (its header states the
777851 // split: "Presence here, placement there"). Both paths that expand
778852 // PENDING_DISPOSITION must name their owner IN THE MESSAGE THE AUTHOR READS.
853+ battery ( '#8435 remedy authority. PLACEMENT is pinned here, per-gate, because the' ) ;
779854 ok (
780855 RATCHET_AUTHORITY === '⛔ MAINTAINER-ONLY' ,
781856 'the authority token is not the spelling scripts/check-ratchet-remedy-authority.mjs sweeps for' ,
@@ -807,20 +882,69 @@ function selfTest() {
807882 ) ;
808883
809884 // -- Parse anchors: a moved anchor is a REFUSAL input, never an empty population.
885+ battery ( 'Parse anchors: a moved anchor is a REFUSAL input, never an empty population.' ) ;
810886 ok ( parseLedgerRows ( 'export const SOMETHING_ELSE = [];' ) === null , 'a missing AUTH_ROUTE_LEDGER anchor parsed as zero rows' ) ;
811887 ok ( parseVendorSurface ( 'export const SOMETHING_ELSE = [];' ) === null , 'a missing surface anchor parsed as zero rows' ) ;
812888
813889 // -- The escaped-quote shape the real notes use is measured, not truncated.
890+ battery ( 'The escaped-quote shape the real notes use is measured, not truncated.' ) ;
814891 ok (
815892 unescape ( "objectui app-shell\\'s wizard" ) . length === 'objectui app-shell\'s wizard' . length ,
816893 'an escaped quote in a note was mis-measured' ,
817894 ) ;
818895
819896 // -- And the real inputs on disk are readable, so the anchors have not moved.
897+ battery ( 'And the real inputs on disk are readable, so the anchors have not moved.' ) ;
820898 for ( const rel of [ MOUNT_SOURCE , LEDGER_SOURCE ] ) {
821899 ok ( existsSync ( join ( ROOT , rel ) ) , `${ rel } does not exist -- this gate's anchor moved` ) ;
822900 }
823901
902+ // ── The floor: every declared battery RAN, and ran its cases (#13489) ───
903+ //
904+ // Evaluated after every battery has had its chance and BEFORE the verdict, so
905+ // the success line below can only be printed by a run in which the set of
906+ // batteries that registered assertions EQUALS the set declared. A set
907+ // difference names WHICH battery stopped; a count says only that something did.
908+ const floorFailure = ( message ) => {
909+ fail . push ( message ) ;
910+ } ;
911+ const declaredBatteries = Object . keys ( SELF_TEST_BATTERIES ) ;
912+ let floorBreached = false ;
913+ if ( declaredBatteries . length < SELF_TEST_BATTERY_FLOOR ) {
914+ floorBreached = true ;
915+ floorFailure (
916+ `SELF_TEST_BATTERIES declares ${ declaredBatteries . length } batteries, below the pinned ` +
917+ `${ SELF_TEST_BATTERY_FLOOR } — a battery deleted from the roster takes its own floor with it.` ,
918+ ) ;
919+ }
920+ for ( const [ name , count ] of batterySeen ) {
921+ if ( declaredBatteries . includes ( name ) ) continue ;
922+ floorBreached = true ;
923+ floorFailure (
924+ `self-test battery "${ name } " registered ${ count } case(s) but is not declared in ` +
925+ 'SELF_TEST_BATTERIES — an assertion attributed to no declared battery is one nothing floors.' ,
926+ ) ;
927+ }
928+ for ( const name of declaredBatteries ) {
929+ const count = batterySeen . get ( name ) ?? 0 ;
930+ if ( count >= SELF_TEST_BATTERIES [ name ] ) continue ;
931+ floorBreached = true ;
932+ floorFailure (
933+ count === 0
934+ ? `self-test battery "${ name } " DID NOT RUN — 0 cases registered, ${ SELF_TEST_BATTERIES [ name ] } pinned. ` +
935+ 'The verdict below would have claimed those cases hold.'
936+ : `self-test battery "${ name } " registered ${ count } case(s), below its pinned floor of ` +
937+ `${ SELF_TEST_BATTERIES [ name ] } — cases that used to run no longer do.` ,
938+ ) ;
939+ }
940+ if ( floorBreached ) {
941+ floorFailure (
942+ 'A battery at or below its floor means cases STOPPED RUNNING — the battery is the bug, not the ' +
943+ 'number. Find what stopped registering (an early return, a deleted block, a guard that now ' +
944+ 'skips) and restore it.' ,
945+ ) ;
946+ }
947+
824948 if ( fail . length ) {
825949 console . error ( 'check-auth-mount-ledger --self-test FAILED:' ) ;
826950 for ( const f of fail ) console . error ( ` - ${ f } ` ) ;
0 commit comments