Skip to content

Commit b09c8f5

Browse files
committed
test(tooling): pin the fourth door — the card's two acceptance inputs, both lit controls, and the floor
Five batteries, 18 cases, on the fixtures the census itself produces: a receiver type authored across lines, put through `.replace(/\s+/g, ' ')` -- the collapse the census performs -- and wrapped in the synthetic alias it builds. • the collapsed receiver STILL exits through `parseSourceFile`, and through `parseDerivedText` returns a located verdict with the run surviving. Two lit controls hold the fixture family capable of SUCCEEDING: the semicolon spelling (the card's own control) and the authored spelling both parse, so the defect is the collapse and not the shape. • a genuinely unparseable input is still refused at every door: an exit at `parseSourceFile`, and NO TREE back from `parseDerivedText`, so there is nothing to walk and no result that reads as "nothing to report". Without this leg a repair and a switched-off check are the same green. • the fourth door is unreachable from an uncertified origin -- a raw tree, null, undefined, a string -- and that refusal is not swallowable either; a source file a checked Program was built over IS certified. • the knobs are compared across the two doors rather than asserted in prose, and the census counts a rejection as a run that continued. Claude-Session: https://claude.ai/code/session_019srGWGCBBCBHqcDoRZpQRh Co-authored-by: Claude <noreply@anthropic.com>
1 parent 37183a2 commit b09c8f5

1 file changed

Lines changed: 185 additions & 4 deletions

File tree

scripts/ts-parse.mjs

Lines changed: 185 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -248,12 +248,17 @@ const SELF_TEST_BATTERIES = Object.freeze({
248248
'ts.transpileModule: the quietest of the three': 4,
249249
'the refusal is not swallowable on the new doors either': 1,
250250
'the census names which door each source came through': 1,
251+
'the fourth door: a synthesis that does not parse is a VERDICT, not a takedown': 5,
252+
'acceptance: a genuinely unparseable input is still REFUSED at every door': 4,
253+
'the fourth door is UNREACHABLE for a source the gate could not read': 7,
254+
'the fourth door parses under the SAME fixed knobs as the three above': 1,
255+
'the census counts a derived parse as its own door, and a rejection as NOT a refusal': 1,
251256
'the diagnostics reader itself, in-process': 4,
252257
});
253258

254259
// DELETING an entry silences that battery's floor exactly as effectively as
255260
// zeroing it, so the roster's own size is pinned too.
256-
const SELF_TEST_BATTERY_FLOOR = 12;
261+
const SELF_TEST_BATTERY_FLOOR = 17;
257262

258263
// The key an assertion is filed under when no battery is open. It is not a
259264
// declared battery, so it reds by the same set difference rather than silently
@@ -1002,7 +1007,8 @@ export function selfTest() {
10021007
);
10031008
t('the census counts parses and distinct file names',
10041009
counted.status === 0
1005-
&& counted.out === '{"parses":3,"programs":0,"transpiles":0,"files":2,"refusals":0}',
1010+
&& counted.out === '{"parses":3,"programs":0,"transpiles":0,"derived":0,"files":2,'
1011+
+ '"refusals":0,"rejections":0}',
10061012
JSON.stringify(counted));
10071013

10081014
// -- and the report is armed by the first PARSE, not by the IMPORT. Both
@@ -1125,9 +1131,181 @@ export function selfTest() {
11251131
);
11261132
t('the census counts programs and transpiles as their own doors',
11271133
countedAll.status === 0
1128-
&& countedAll.out === '{"parses":3,"programs":1,"transpiles":1,"files":3,"refusals":0}',
1134+
&& countedAll.out === '{"parses":3,"programs":1,"transpiles":1,"derived":0,"files":3,'
1135+
+ '"refusals":0,"rejections":0}',
11291136
JSON.stringify(countedAll));
11301137

1138+
// ⭐⭐ THE FOURTH DOOR. The fixtures are the census's own round trip, spelled
1139+
// the way the census spells it: a receiver type authored across lines, put
1140+
// through `.replace(/\s+/g, ' ')` -- the collapse at
1141+
// `tenant-audit-census.mjs:459` -- and wrapped in the synthetic alias its
1142+
// `:723` builds. Measured 2026-09-18 on TypeScript 6.0.3: the AUTHORED
1143+
// spelling is legal TypeScript (0 diagnostics), the COLLAPSED one is not
1144+
// (1: "';' expected"), and the SEMICOLON spelling is the card's lit control.
1145+
// All three are held here, because only the set of them says the defect is
1146+
// the collapse and not the shape.
1147+
const AUTHORED_RECEIVER = '{\n'
1148+
+ ' insert(object: string, data: unknown): Promise<void>\n'
1149+
+ ' find(object: string, query: unknown): Promise<void>\n'
1150+
+ '}';
1151+
const COLLAPSED_RECEIVER = AUTHORED_RECEIVER.replace(/\s+/g, ' ');
1152+
const SEMICOLON_RECEIVER = '{ insert(object: string, data: unknown): Promise<void>;'
1153+
+ ' find(object: string, query: unknown): Promise<void>; }';
1154+
const alias = (typeText) => `type CensusReceiver = ${typeText};\n`;
1155+
1156+
const IMPORT_DERIVED =
1157+
`import { parseDerivedText } from ${JSON.stringify(pathToFileURL(SELF).href)};\n`;
1158+
1159+
// One certified origin, then the derived parse. The origin is a real parse
1160+
// through the real door, so the case exercises the vouch rather than a
1161+
// model of it.
1162+
const derived = (text, fileName = 'census-receiver-type.ts') =>
1163+
run(
1164+
IMPORT_DERIVED
1165+
+ `const origin = parseSourceFile('corpus/receiver.ts', 'export const x = 1;\\n');\n`
1166+
+ `const r = parseDerivedText(origin, ${JSON.stringify(fileName)}, ${JSON.stringify(text)});\n`
1167+
+ `console.log(JSON.stringify({ tree: r.sourceFile === null ? null : r.sourceFile.statements.length,`
1168+
+ ` failure: r.failure === null ? null : { line: r.failure.line, column: r.failure.column,`
1169+
+ ` count: r.failure.count, names: r.failure.report.includes('corpus/receiver.ts') } }));\n`,
1170+
);
1171+
1172+
battery('the fourth door: a synthesis that does not parse is a VERDICT, not a takedown');
1173+
// The defect, at the door it came in by: the collapsed text still ends the
1174+
// process when it arrives as a TREE to be read. ⛔ This must not change.
1175+
const collapsedThroughRefusal = parse(alias(COLLAPSED_RECEIVER), 'census-receiver-type.ts');
1176+
t('⛔ the collapsed receiver STILL exits through parseSourceFile — the refusal is untouched',
1177+
collapsedThroughRefusal.status === EXIT_UNPARSEABLE && collapsedThroughRefusal.out === '',
1178+
JSON.stringify({ status: collapsedThroughRefusal.status, out: collapsedThroughRefusal.out }));
1179+
// …and the same text through the fourth door: the run SURVIVES and the
1180+
// verdict is data the caller can attribute to one site.
1181+
const collapsedDerived = derived(alias(COLLAPSED_RECEIVER));
1182+
t('⭐ the same collapsed receiver through parseDerivedText returns a verdict and the run continues',
1183+
collapsedDerived.status === 0
1184+
&& JSON.parse(collapsedDerived.out).tree === null
1185+
&& JSON.parse(collapsedDerived.out).failure.count === 1
1186+
&& JSON.parse(collapsedDerived.out).failure.line === 1,
1187+
JSON.stringify(collapsedDerived));
1188+
t('…and that verdict names the SOURCE the text was derived from, not only the synthetic name',
1189+
collapsedDerived.status === 0 && JSON.parse(collapsedDerived.out).failure.names === true,
1190+
JSON.stringify(collapsedDerived));
1191+
// LIT CONTROL, twice. The door must be capable of SUCCEEDING on this
1192+
// fixture family, or the case above is indistinguishable from a door that
1193+
// rejects everything.
1194+
const semicolonDerived = derived(alias(SEMICOLON_RECEIVER));
1195+
t('⭐ LIT CONTROL: the semicolon spelling of the SAME literal parses through the same door',
1196+
semicolonDerived.status === 0 && JSON.parse(semicolonDerived.out).tree === 1
1197+
&& JSON.parse(semicolonDerived.out).failure === null,
1198+
JSON.stringify(semicolonDerived));
1199+
const authoredDerived = derived(alias(AUTHORED_RECEIVER));
1200+
t('⭐ LIT CONTROL: so does the AUTHORED spelling — the collapse is the defect, not the shape',
1201+
authoredDerived.status === 0 && JSON.parse(authoredDerived.out).tree === 1
1202+
&& JSON.parse(authoredDerived.out).failure === null,
1203+
JSON.stringify(authoredDerived));
1204+
1205+
battery('acceptance: a genuinely unparseable input is still REFUSED at every door');
1206+
// Without this leg, "the round trip is fixed" and "the parse check was
1207+
// switched off" are the same green.
1208+
for (const [name, text] of [
1209+
['merge-conflict markers', CONFLICTED],
1210+
['a truncated body', TRUNCATED],
1211+
]) {
1212+
const viaRefusal = parse(text, 'packages/foo/src/bar.ts');
1213+
t(`⛔ ${name}: parseSourceFile still ends the run`,
1214+
viaRefusal.status === EXIT_UNPARSEABLE && viaRefusal.out === '',
1215+
JSON.stringify({ status: viaRefusal.status, out: viaRefusal.out }));
1216+
// At the fourth door the refusal is not an exit -- so what has to hold is
1217+
// that NO TREE comes back. A caller cannot walk recovered wreckage and
1218+
// cannot read the result as "nothing to report".
1219+
const viaDerived = derived(text, 'synthesised.ts');
1220+
t(`⛔ ${name}: parseDerivedText hands back NO TREE, so there is nothing to score clean`,
1221+
viaDerived.status === 0 && JSON.parse(viaDerived.out).tree === null
1222+
&& JSON.parse(viaDerived.out).failure.count >= 1,
1223+
JSON.stringify(viaDerived));
1224+
}
1225+
1226+
battery('the fourth door is UNREACHABLE for a source the gate could not read');
1227+
// The floor, by construction: the only way to hold a certified origin is a
1228+
// door that exits on an unparseable source. An uncertified one is refused.
1229+
const misuse = (originExpr) =>
1230+
run(
1231+
IMPORT_DERIVED
1232+
+ `const r = parseDerivedText(${originExpr}, 'derived.ts', 'const a = 1;\\n');\n`
1233+
+ `console.log('RETURNED ' + JSON.stringify(r.failure));\n`,
1234+
);
1235+
const rawOrigin = misuse(
1236+
`ts.createSourceFile('raw.ts', 'export const a = 1;\\n', ts.ScriptTarget.Latest, true)`,
1237+
);
1238+
t('⛔ a tree this module never certified is REFUSED, however well it parses',
1239+
rawOrigin.status === EXIT_DERIVED_MISUSE && rawOrigin.out === '',
1240+
JSON.stringify({ status: rawOrigin.status, out: rawOrigin.out }));
1241+
t('…and that refusal says which door to use instead',
1242+
rawOrigin.err.includes('parseSourceFile'), rawOrigin.err.slice(0, 400));
1243+
for (const [name, expr] of [['null', 'null'], ['undefined', 'undefined'], ['a string', `'not a tree'`]]) {
1244+
const bad = misuse(expr);
1245+
t(`⛔ an origin that is ${name} is refused rather than throwing a TypeError`,
1246+
bad.status === EXIT_DERIVED_MISUSE, JSON.stringify({ status: bad.status, err: bad.err.slice(0, 120) }));
1247+
}
1248+
const swallowedMisuse = run(
1249+
IMPORT_DERIVED
1250+
+ `let caught = false;\n`
1251+
+ `try { parseDerivedText(null, 'derived.ts', 'const a = 1;\\n'); } catch { caught = true; }\n`
1252+
+ `console.log(caught ? 'SWALLOWED' : 'NOT REACHED');\n`,
1253+
);
1254+
t('⛔ and a caller’s try/catch cannot downgrade the misuse refusal either',
1255+
swallowedMisuse.status === EXIT_DERIVED_MISUSE && !swallowedMisuse.out.includes('SWALLOWED'),
1256+
JSON.stringify(swallowedMisuse));
1257+
// The other certifying door. A gate that reads its corpus through a Program
1258+
// can synthesise from it on the same terms.
1259+
const programOrigin = run(
1260+
IMPORT_DERIVED
1261+
+ `import { createProgramChecked } from ${JSON.stringify(pathToFileURL(SELF).href)};\n`
1262+
+ `const p = createProgramChecked(${JSON.stringify([okEntry])}, ${PROGRAM_OPTIONS});\n`
1263+
+ `const origin = p.getSourceFiles().find((f) => f.fileName === ${JSON.stringify(okEntry)});\n`
1264+
+ `const r = parseDerivedText(origin, 'from-program.ts', 'const a = 1;\\n');\n`
1265+
+ `console.log('DERIVED ' + (r.failure === null));\n`,
1266+
);
1267+
t('⭐ a source file a checked Program was built over IS certified',
1268+
programOrigin.status === 0 && programOrigin.out === 'DERIVED true',
1269+
JSON.stringify(programOrigin));
1270+
1271+
battery('the fourth door parses under the SAME fixed knobs as the three above');
1272+
// Two doors, one source, the knobs compared rather than asserted in prose:
1273+
// a drift in either would leave the header's "the knobs that are NOT knobs"
1274+
// section describing one door only.
1275+
const knobs = run(
1276+
IMPORT_DERIVED
1277+
+ `const origin = parseSourceFile('corpus/a.ts', 'export const x = 1;\\n');\n`
1278+
+ `const direct = parseSourceFile('b.ts', 'export const y = 2;\\n');\n`
1279+
+ `const { sourceFile: fromDerived } = parseDerivedText(origin, 'c.ts', 'export const z = 3;\\n');\n`
1280+
+ `console.log(JSON.stringify({\n`
1281+
+ ` sameTarget: direct.languageVersion === fromDerived.languageVersion,\n`
1282+
+ ` latest: fromDerived.languageVersion === ts.ScriptTarget.Latest,\n`
1283+
+ ` parentsDirect: direct.statements[0].parent !== undefined,\n`
1284+
+ ` parentsDerived: fromDerived.statements[0].parent !== undefined,\n`
1285+
+ `}));\n`,
1286+
);
1287+
t('the derived tree carries the same ScriptTarget and has its parents set, exactly as a direct parse',
1288+
knobs.status === 0
1289+
&& knobs.out === '{"sameTarget":true,"latest":true,"parentsDirect":true,"parentsDerived":true}',
1290+
JSON.stringify(knobs));
1291+
1292+
battery('the census counts a derived parse as its own door, and a rejection as NOT a refusal');
1293+
// The numerator claim: a rejection left the run going, so conflating it
1294+
// with a refusal would make the one report that says how much was measured
1295+
// say it wrong.
1296+
const countedDerived = run(
1297+
IMPORT_DERIVED
1298+
+ `const origin = parseSourceFile('corpus/a.ts', 'export const x = 1;\\n');\n`
1299+
+ `parseDerivedText(origin, 'ok.ts', 'const a = 1;\\n');\n`
1300+
+ `parseDerivedText(origin, 'bad.ts', ${JSON.stringify(alias(COLLAPSED_RECEIVER))});\n`
1301+
+ `console.log(JSON.stringify(parseCensus()));\n`,
1302+
);
1303+
t('two derived parses, one rejected: counted as derived, and the run’s refusals stay 0',
1304+
countedDerived.status === 0
1305+
&& countedDerived.out === '{"parses":3,"programs":0,"transpiles":0,"derived":2,"files":3,'
1306+
+ '"refusals":0,"rejections":1}',
1307+
JSON.stringify(countedDerived));
1308+
11311309
// -- the diagnostics reader itself, in-process ---------------------------
11321310
battery('the diagnostics reader itself, in-process');
11331311
t('describeDiagnostics answers [] for a tree that parsed',
@@ -1155,7 +1333,10 @@ export function selfTest() {
11551333
console.log(
11561334
`✓ ts-parse self-test: ${cases.length} cases pass (every measured wreck refuses and names its file, `
11571335
+ `across all three parser entry points, both ScriptKind directions, a Program's transitive import `
1158-
+ `included, and a caller’s try/catch cannot swallow any of it).`,
1336+
+ `included, and a caller’s try/catch cannot swallow any of it -- plus the fourth door, where text `
1337+
+ `THIS process synthesised returns its verdict instead of ending the run, reachable only from an `
1338+
+ `origin one of those three certified, handing back no tree when it fails, and with the census `
1339+
+ `counting a rejection as a run that continued rather than as a refusal).`,
11591340
);
11601341
selfTestReachedVerdict = true;
11611342
return 0;

0 commit comments

Comments
 (0)