@@ -25,6 +25,12 @@ import { cpSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:f
2525import { tmpdir } from 'node:os' ;
2626import path from 'node:path' ;
2727import { fileURLToPath } from 'node:url' ;
28+ // The registry itself, so the denominator block at the bottom of this file can
29+ // hold the gate's output answerable to it rather than to a copied list (#18133).
30+ import {
31+ listMetadataTypeSchemaTypes ,
32+ listUnregisteredKindSchemaTypes ,
33+ } from '../../src/kernel/metadata-type-schemas' ;
2834
2935const HERE = path . dirname ( fileURLToPath ( import . meta. url ) ) ;
3036const SPEC = path . resolve ( HERE , '../..' ) ;
@@ -1120,3 +1126,105 @@ describe('check:liveness — the drill recurses past one level (#17424)', () =>
11201126 expect ( status , output ) . toBe ( 1 ) ;
11211127 } ) ;
11221128} ) ;
1129+
1130+ // The DENOMINATOR the coverage ratchet divides by (#18133).
1131+ //
1132+ // Everything in the block above asks whether the gate judges what it walks
1133+ // correctly. This asks the prior question — WHOM does it walk for? — and it is
1134+ // the one question a gate cannot ask about itself, because the failure mode has
1135+ // no output: a type in neither `GOVERNED` nor `PENDING_GOVERNANCE` produces no
1136+ // row in any bucket, so `ungoverned: []` reads identically whether the gate
1137+ // looked and found nothing or never looked at all.
1138+ //
1139+ // WHAT WAS WRONG. The denominator was `listMetadataTypeSchemaTypes()` under a
1140+ // comment claiming it was "exactly the set of authorable metadata types". That
1141+ // function deliberately does NOT enumerate `UNREGISTERED_KIND_SCHEMAS` (#6245 —
1142+ // enrolling those entries there "would claim a status this change is careful
1143+ // not to grant"), while the four kinds bound in that map are authored on every
1144+ // boot through their stack collections and on every write through
1145+ // `PUT /api/v1/meta/:type/:name`. So `connector`, `sharing_rule` and
1146+ // `analytics_cube` were structurally unnameable by `report.ungoverned` — the
1147+ // same sentence #17356 measured false for the reachability gate, one gate over.
1148+ //
1149+ // WHY THESE ASSERT AGAINST THE LIVE REGISTRY rather than against a literal list:
1150+ // a hard-coded expectation would pass unchanged if the gate stopped reading the
1151+ // registry at all, which is the regression class this whole block exists for.
1152+ // The registry is imported here and the gate is spawned; the two have to agree.
1153+ describe ( 'check:liveness — the governance denominator is the AUTHORABLE set (#18133)' , ( ) => {
1154+ function jsonReport ( extraArgs : readonly string [ ] = [ ] ) : any {
1155+ const { output } = runGate ( undefined , [ '--json' , ...extraArgs ] ) ;
1156+ const start = output . indexOf ( '{' ) ;
1157+ expect ( start , output ) . toBeGreaterThanOrEqual ( 0 ) ;
1158+ return JSON . parse ( output . slice ( start ) ) ;
1159+ }
1160+
1161+ // The control for every assertion below. Without it, "the denominator omits
1162+ // nothing" is also satisfied by a registry that enumerates nothing.
1163+ it ( 'has a non-empty registry on BOTH sides of the union' , ( ) => {
1164+ expect ( listMetadataTypeSchemaTypes ( ) . length ) . toBeGreaterThan ( 20 ) ;
1165+ expect ( listUnregisteredKindSchemaTypes ( ) . length ) . toBeGreaterThan ( 0 ) ;
1166+ // The two sets are disjoint — that disjointness IS #6245, and it is why the
1167+ // union is not a no-op. If this ever fails, the fix below has become moot
1168+ // and this whole block needs re-reading, not re-pinning.
1169+ const registered = new Set ( listMetadataTypeSchemaTypes ( ) ) ;
1170+ expect ( listUnregisteredKindSchemaTypes ( ) . filter ( ( t ) => registered . has ( t ) ) ) . toEqual ( [ ] ) ;
1171+ } ) ;
1172+
1173+ it ( 'counts every unregistered kind, which the registered set alone cannot' , ( ) => {
1174+ const report = jsonReport ( ) ;
1175+ for ( const kind of listUnregisteredKindSchemaTypes ( ) ) {
1176+ expect ( report . authorable , `'${ kind } ' is authored through its stack collection and through `
1177+ + 'PUT /api/v1/meta/:type/:name, so a governance denominator that omits it cannot report '
1178+ + 'on it — which is exactly the state #18133 found' ) . toContain ( kind ) ;
1179+ }
1180+ // …and the denominator is STRICTLY larger than the registered set, which is
1181+ // the assertion that goes red the moment somebody "simplifies" the union
1182+ // back into `listMetadataTypeSchemaTypes()`.
1183+ expect ( report . authorable . length ) . toBeGreaterThan ( listMetadataTypeSchemaTypes ( ) . length ) ;
1184+ expect ( report . authorable ) . toEqual (
1185+ [ ...new Set ( [ ...listMetadataTypeSchemaTypes ( ) , ...listUnregisteredKindSchemaTypes ( ) ] ) ] . sort ( ) ,
1186+ ) ;
1187+ } ) ;
1188+
1189+ it ( 'accounts for every member of it — governed or explicitly pending, never silent' , ( ) => {
1190+ const report = jsonReport ( ) ;
1191+ expect ( report . ungoverned ) . toEqual ( [ ] ) ;
1192+ // An empty `ungoverned` is only meaningful next to a denominator that could
1193+ // have populated it, so assert the population too — this is the pair the
1194+ // old output could not print.
1195+ expect ( report . authorable . length ) . toBeGreaterThan ( 0 ) ;
1196+ // And no pending row claims a debt for a type the denominator does not hold:
1197+ // before the union landed, recording one of the unregistered kinds here would
1198+ // have been reported STALE rather than pending.
1199+ expect ( report . stalePending ) . toEqual ( [ ] ) ;
1200+ } ) ;
1201+
1202+ it ( 'prints the denominator and its composition on EVERY run, green included' , ( ) => {
1203+ const { status, output } = runGate ( ) ;
1204+ expect ( status , output ) . toBe ( 0 ) ;
1205+ const line = output . split ( '\n' ) . find ( ( l ) => l . startsWith ( 'governance denominator:' ) ) ?? '' ;
1206+ // The line used to print only when `PENDING_GOVERNANCE` was non-empty, so the
1207+ // one state worth reporting — "N types looked at, none unaccounted for" —
1208+ // rendered as nothing at all: the same silence an unseen type produces.
1209+ expect ( line , output ) . not . toBe ( '' ) ;
1210+ expect ( line ) . toMatch ( / ^ g o v e r n a n c e d e n o m i n a t o r : \d + a u t h o r a b l e t y p e \( s \) — \d + r e g i s t e r e d k i n d \( s \) \+ \d + u n r e g i s t e r e d - k i n d s t a c k c o l l e c t i o n \( s \) / ) ;
1211+ for ( const kind of listUnregisteredKindSchemaTypes ( ) ) expect ( line ) . toContain ( kind ) ;
1212+ } ) ;
1213+
1214+ // #6245's guarantee, asserted from the gate that had the motive to break it.
1215+ // The repair for #18133 belongs in this gate's own denominator; enrolling the
1216+ // unregistered kinds in the registry instead would have granted them a KIND
1217+ // status (`MetadataTypeSchema` enum membership, a `DEFAULT_METADATA_TYPE_REGISTRY`
1218+ // entry, a create seed, a place in the #4001 campaign count) that #6245 and
1219+ // #2657's still-open B/C decision deliberately withhold.
1220+ it ( 'reads the unregistered kinds WITHOUT registering them' , ( ) => {
1221+ const registered = listMetadataTypeSchemaTypes ( ) ;
1222+ for ( const kind of listUnregisteredKindSchemaTypes ( ) ) {
1223+ expect ( registered , `#6245: '${ kind } ' must not become a registered KIND just because a `
1224+ + 'check needs to enumerate it — listUnregisteredKindSchemaTypes() (#6931) exists so '
1225+ + 'that enumeration costs nothing' ) . not . toContain ( kind ) ;
1226+ }
1227+ const src = readFileSync ( GATE , 'utf8' ) ;
1228+ expect ( src ) . toContain ( 'listUnregisteredKindSchemaTypes' ) ;
1229+ } ) ;
1230+ } ) ;
0 commit comments