Skip to content

Commit 220b424

Browse files
objectstack-agentclaude
andcommitted
test(spec): pin the liveness governance denominator against the live registry
Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3 Co-authored-by: Claude <noreply@anthropic.com>
1 parent a48d4fa commit 220b424

1 file changed

Lines changed: 108 additions & 0 deletions

File tree

‎packages/spec/scripts/liveness/check-liveness.test.ts‎

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ import { cpSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:f
2525
import { tmpdir } from 'node:os';
2626
import path from 'node:path';
2727
import { 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

2935
const HERE = path.dirname(fileURLToPath(import.meta.url));
3036
const 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(/^governance denominator: \d+ authorable type\(s\) — \d+ registered kind\(s\) \+ \d+ unregistered-kind stack collection\(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

Comments
 (0)