Skip to content

Commit 2a139d6

Browse files
committed
add the recorded-fact census: inventory, classification, and a reparse-ledger attribution scoreboard
scripts/fact-inventory.ts walks src/ast.ts with the TypeScript compiler and classifies every property signature as either a recorded fact the printer prints bytes from, or exempt (a discriminant, position bookkeeping, structural containment, verbatim content, an unread record, a type-level sentinel, or a container whose own arm is classified separately). The gate in factInventoryFailures fails when ast.ts grows an unclassified field or the classification map goes stale in either direction. Today's checkout: 71 facts. scripts/fact-inventory-ledger.json maps each of the 71 facts to an existing test with round-trip coverage of it (a landedLemma flag marks the two facts that already carry a dedicated lemma test in the #178/ #181 sense) and to the tests/conformance/reparse-ledger.json families it plausibly explains, with a basis (argued vs argued-not-counted) so a stretched connection is recorded rather than silently dropped or silently counted. The scoreboard: of the 276 reparse-ledger rows across 8 families, 127 (46.0%) are attributed to a recorded fact by an argued, non-stretch connection (indent-dropped, plus-respelled, gap-line-lost, fence-style-detached, heading-under-a-term). That is below the pre-registered 50% gate, so the retirement premise this inventory was built to test does not hold on the current fact set; see factlemmas-report.md for the full accounting and the five self-audit answers.
1 parent 2b67b2b commit 2a139d6

4 files changed

Lines changed: 1096 additions & 0 deletions

File tree

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
/**
2+
* The classification data `scripts/fact-inventory.ts` gates against:
3+
* every `src/ast.ts` property, sorted into FACTS (a printer under
4+
* `src/print/` reads it to choose an output SHAPE) or EXEMPT (a
5+
* discriminant, position bookkeeping, structural containment,
6+
* verbatim content, an unread record, a type-level sentinel, or a
7+
* container whose own arm's fields are classified separately). The
8+
* criterion, the verification method, and the surprises it turned up
9+
* are stated in `scripts/fact-inventory.ts`'s own module doc — this
10+
* file is the two maps alone, split out so that file stays under the
11+
* `max-lines` ceiling.
12+
*
13+
* Reasons are kept SHORT here on purpose (most owe their length to a
14+
* long key, not a long reason): the narrative for each recorded fact
15+
* — where the reparse-ledger fits, which test already exercises it —
16+
* lives in `scripts/fact-inventory-ledger.json`, which is what a
17+
* reader chasing "why is this a fact" should open next.
18+
*/
19+
20+
/** Selects which printer function runs; carries no spelling of its own. */
21+
const TYPE_DISCRIMINANT =
22+
"type discriminant, not a print-shape choice of its own";
23+
/** The printer's byte output never reads this. */
24+
const POSITION = "position bookkeeping, not read by the printer";
25+
/** Walked by Prettier's path.map or the printer's own recursive print. */
26+
const STRUCTURAL = "structural containment, not a shape choice of its own";
27+
/** Copied into the output unconditionally; no branch on its own value. */
28+
const CONTENT = "verbatim leaf content, copied unconditionally";
29+
/** Constructed at parse time; no `src/print/*.ts` reference found. */
30+
const UNREAD = "recorded but unread under src/print (verified by grep)";
31+
/** `?: undefined`; never holds a runtime value. */
32+
const SENTINEL = "type-level sentinel, never holds a value";
33+
/** A record/union pointer whose own arms are classified separately. */
34+
const CONTAINER = "container; its arm's fields are classified separately";
35+
36+
/**
37+
* Fields read under `src/print/` to choose between output shapes —
38+
* the recorded facts themselves. See `scripts/fact-inventory.ts` for
39+
* the criterion and `scripts/fact-inventory-ledger.json` for the
40+
* per-fact narrative.
41+
*/
42+
export const FACTS: ReadonlyMap<string, string> = new Map([
43+
["DocumentNode.byteOrderMark", "printer.ts replays it"],
44+
["ParagraphNode.firstWordEndsItsLine", "block-start-hazard.ts's question"],
45+
["BoldNode.constrained", "mark width choice"],
46+
["ItalicNode.constrained", "mark width choice"],
47+
["MonospaceNode.constrained", "mark width choice"],
48+
["HighlightNode.constrained", "mark width choice"],
49+
["CurvedQuoteNode.quote", "entity-pair choice"],
50+
["LinkNode.text", "3-state bracket spelling"],
51+
["InlineAnchorNode.form", "bracket-width choice"],
52+
["InlineAnchorNode.reftext", "verbatim-replay framing"],
53+
["HeadingNode.level", "only record of marker count"],
54+
["DocumentHeaderNode.underline", "compat-mode spelling"],
55+
["DiscreteHeadingNode.level", "marker-count fact"],
56+
["CommentNode.commentType", "// vs //// choice"],
57+
["AttributeEntryNode.unset", "collapses two spellings"],
58+
["ListNode.variant", "callout marker path choice"],
59+
["ListNode.marker", "printedGap's gap-fold key"],
60+
["LeafDelimitedBlockNode.variant", "delimiter char lookup"],
61+
["LeafDelimitedBlockNode.form", "framing-strategy choice"],
62+
["FencedCodeBlockNode.variant", "delimiter char lookup"],
63+
["FencedCodeBlockNode.form", "framing-strategy choice"],
64+
["MasqueradedBlockNode.variant", "style-attribute label"],
65+
["MasqueradedBlockNode.form", "framing-strategy choice"],
66+
["IndentedLiteralBlockNode.variant", "delimiter char lookup"],
67+
["IndentedLiteralBlockNode.form", "framing-strategy choice"],
68+
["ParagraphFormBlockNode.variant", "style-attribute label"],
69+
["ParagraphFormBlockNode.form", "framing-strategy choice"],
70+
["LeafDelimitedBlockNode.annotatedBy", "invariant (xi)"],
71+
["FencedCodeBlockNode.annotatedBy", "invariant (xi)"],
72+
["FencedCodeBlockNode.language", "[source,lang] emission"],
73+
["MasqueradedBlockNode.annotatedBy", "invariant (xi)"],
74+
["MasqueradedBlockNode.sourceDelimiter", "invariant (xiii)"],
75+
["IndentedLiteralBlockNode.annotatedBy", "invariant (xi)"],
76+
["ParagraphFormBlockNode.annotatedBy", "invariant (xi)"],
77+
["OpenParentBlockNode.openDelimiter", "tilde spelling, issue #64"],
78+
["CompoundParentBlockNode.variant", "only record of the delimiter"],
79+
["AdmonitionNode.form", "label vs delimited-wrapper"],
80+
["ItemBody.trailingContinuation", "issue #181's landed lemma"],
81+
["ItemBody.detachedTail", "detachedTail print arm"],
82+
["ItemBody.activeTail", "blank-line count after list"],
83+
["ItemBody.everyTextLineIndented", "reflow-guard input"],
84+
["ListItemNode.markerSpelling", "printed marker bytes"],
85+
["ListItemNode.markerIndent", "structure, not decoration"],
86+
["ListItemNode.checkbox", "checklist marker bytes"],
87+
["ListItemNode.calloutNumber", "callout marker bytes"],
88+
["ItemBlock.gap", "the brief's own gap example"],
89+
["DescriptionTermNode.line", "keeps three spellings apart"],
90+
["DescriptionListItemNode.printing", "own doc: never re-derived"],
91+
["TermEntry.gap", "gap alphabet, for a term"],
92+
["TableNode.open", "psv/csv/dsv family choice"],
93+
["TableNode.columns", "gates ragged-row decline"],
94+
["TableNode.leadingRuns", "gates interior-content check"],
95+
["TableNode.annotatedBy", "not covered by invariant (xi)"],
96+
["TableNode.attrlistUnread", "declines layout normalization"],
97+
["TableClose#0.kind", "gates the closing line"],
98+
["TableClose#1.kind", "same fact, other arm"],
99+
["TableCutting.format", "table-layout.ts's isNotPsv"],
100+
["TableCutting.separator", "table-layout.ts's isNotPsv"],
101+
["TableColumnSpec.style", "suppresses ws normalization"],
102+
["TableCellNode.columnIndex", "own doc: recorded, not derived"],
103+
["TableCellOpening#0.kind", "separator vs lineStart choice"],
104+
["TableCellOpening#1.kind", "same fact, other arms"],
105+
["TableTextRun.kind", "content/comment/blank choice"],
106+
["TableTextRun.image", "table-partition bytes, (xv)"],
107+
["TableCellSpec.style", "opening.parsed.style read"],
108+
["TableCellRepeat#0.kind", "none/span/duplicate choice"],
109+
["TableCellRepeat#1.kind", "same fact, other arms"],
110+
["TableCellRepeat#1.colspan", "span calculation input"],
111+
["TableCellRepeat#1.rowspan", "rowVisits reservation"],
112+
["TableCellRepeat#2.kind", "same fact, other arms"],
113+
["TableCellRepeat#2.count", "span calculation input"],
114+
]);
115+
116+
/**
117+
* Fields that are NOT recorded facts, each with a reason (shared
118+
* constants above for the common buckets, a short bespoke string for
119+
* the rest).
120+
*/
121+
export const EXEMPT: ReadonlyMap<string, string> = new Map([
122+
["Location.offset", POSITION],
123+
["Location.line", POSITION],
124+
["Location.column", POSITION],
125+
["Node.type", TYPE_DISCRIMINANT],
126+
["Node.position", POSITION],
127+
["DocumentNode.type", TYPE_DISCRIMINANT],
128+
["DocumentNode.children", STRUCTURAL],
129+
["ParagraphNode.type", TYPE_DISCRIMINANT],
130+
["ParagraphNode.children", STRUCTURAL],
131+
["TextNode.type", TYPE_DISCRIMINANT],
132+
["TextNode.value", CONTENT],
133+
["BoldNode.type", TYPE_DISCRIMINANT],
134+
["BoldNode.role", CONTENT],
135+
["BoldNode.children", STRUCTURAL],
136+
["ItalicNode.type", TYPE_DISCRIMINANT],
137+
["ItalicNode.role", CONTENT],
138+
["ItalicNode.children", STRUCTURAL],
139+
["MonospaceNode.type", TYPE_DISCRIMINANT],
140+
["MonospaceNode.role", CONTENT],
141+
["MonospaceNode.children", STRUCTURAL],
142+
["HighlightNode.type", TYPE_DISCRIMINANT],
143+
["HighlightNode.role", CONTENT],
144+
["HighlightNode.children", STRUCTURAL],
145+
["CurvedQuoteNode.type", TYPE_DISCRIMINANT],
146+
["CurvedQuoteNode.children", STRUCTURAL],
147+
["SuperscriptNode.type", TYPE_DISCRIMINANT],
148+
["SuperscriptNode.children", STRUCTURAL],
149+
["SubscriptNode.type", TYPE_DISCRIMINANT],
150+
["SubscriptNode.children", STRUCTURAL],
151+
["CharacterReferenceNode.type", TYPE_DISCRIMINANT],
152+
["CharacterReferenceNode.value", CONTENT],
153+
["EscapedMarkNode.type", TYPE_DISCRIMINANT],
154+
["EscapedMarkNode.value", CONTENT],
155+
["AttributeReferenceNode.type", TYPE_DISCRIMINANT],
156+
["AttributeReferenceNode.name", CONTENT],
157+
["LinkNode.type", TYPE_DISCRIMINANT],
158+
["LinkNode.form", "own doc: unread"],
159+
["LinkNode.target", CONTENT],
160+
["XrefNode.type", TYPE_DISCRIMINANT],
161+
["XrefNode.form", 'fixed "shorthand"'],
162+
["XrefNode.target", CONTENT],
163+
["XrefNode.text", "ordinary optional content"],
164+
["InlineAnchorNode.type", TYPE_DISCRIMINANT],
165+
["InlineAnchorNode.id", CONTENT],
166+
["InlineMacroNode.type", TYPE_DISCRIMINANT],
167+
["InlineMacroNode.name", CONTENT],
168+
["InlineMacroNode.target", CONTENT],
169+
["InlineMacroNode.attrlist", CONTENT],
170+
["HardLineBreakNode.type", TYPE_DISCRIMINANT],
171+
["RawLineNode.type", TYPE_DISCRIMINANT],
172+
["RawLineNode.value", CONTENT],
173+
["PassthroughNode.type", TYPE_DISCRIMINANT],
174+
["PassthroughNode.value", CONTENT],
175+
["HeadingNode.type", TYPE_DISCRIMINANT],
176+
["HeadingNode.title", CONTENT],
177+
["AuthorLineNode.type", TYPE_DISCRIMINANT],
178+
["AuthorLineNode.value", CONTENT],
179+
["RevisionLineNode.type", TYPE_DISCRIMINANT],
180+
["RevisionLineNode.value", CONTENT],
181+
["DocumentHeaderNode.type", TYPE_DISCRIMINANT],
182+
["DocumentHeaderNode.title", CONTENT],
183+
["DocumentHeaderNode.lines", STRUCTURAL],
184+
["DiscreteHeadingNode.type", TYPE_DISCRIMINANT],
185+
["DiscreteHeadingNode.title", CONTENT],
186+
["CommentNode.type", TYPE_DISCRIMINANT],
187+
["CommentNode.value", CONTENT],
188+
["AttributeEntryNode.type", TYPE_DISCRIMINANT],
189+
["AttributeEntryNode.name", CONTENT],
190+
["AttributeEntryNode.value", CONTENT],
191+
["ListNode.type", TYPE_DISCRIMINANT],
192+
["ListNode.children", STRUCTURAL],
193+
["LeafDelimitedBlockNode.type", TYPE_DISCRIMINANT],
194+
["LeafDelimitedBlockNode.content", CONTENT],
195+
["FencedCodeBlockNode.type", TYPE_DISCRIMINANT],
196+
["FencedCodeBlockNode.content", CONTENT],
197+
["MasqueradedBlockNode.type", TYPE_DISCRIMINANT],
198+
["MasqueradedBlockNode.content", CONTENT],
199+
["IndentedLiteralBlockNode.type", TYPE_DISCRIMINANT],
200+
["IndentedLiteralBlockNode.content", CONTENT],
201+
["ParagraphFormBlockNode.type", TYPE_DISCRIMINANT],
202+
["ParagraphFormBlockNode.content", CONTENT],
203+
["LeafDelimitedBlockNode.sourceDelimiter", SENTINEL],
204+
["LeafDelimitedBlockNode.fenced", SENTINEL],
205+
["LeafDelimitedBlockNode.language", SENTINEL],
206+
["FencedCodeBlockNode.sourceDelimiter", SENTINEL],
207+
["FencedCodeBlockNode.fenced", "fixed literal true"],
208+
["MasqueradedBlockNode.fenced", SENTINEL],
209+
["MasqueradedBlockNode.language", SENTINEL],
210+
["IndentedLiteralBlockNode.sourceDelimiter", SENTINEL],
211+
["IndentedLiteralBlockNode.fenced", SENTINEL],
212+
["IndentedLiteralBlockNode.language", SENTINEL],
213+
["ParagraphFormBlockNode.sourceDelimiter", SENTINEL],
214+
["ParagraphFormBlockNode.fenced", SENTINEL],
215+
["ParagraphFormBlockNode.language", SENTINEL],
216+
["OpenParentBlockNode.type", TYPE_DISCRIMINANT],
217+
["OpenParentBlockNode.variant", 'fixed "open"'],
218+
["OpenParentBlockNode.children", STRUCTURAL],
219+
["CompoundParentBlockNode.type", TYPE_DISCRIMINANT],
220+
["CompoundParentBlockNode.openDelimiter", SENTINEL],
221+
["CompoundParentBlockNode.children", STRUCTURAL],
222+
["AdmonitionNode.type", TYPE_DISCRIMINANT],
223+
["AdmonitionNode.variant", "label text, case only"],
224+
["AdmonitionNode.text", STRUCTURAL],
225+
["AdmonitionNode.children", STRUCTURAL],
226+
["ThematicBreakNode.type", TYPE_DISCRIMINANT],
227+
["BlockMacroNode.type", TYPE_DISCRIMINANT],
228+
["BlockMacroNode.name", CONTENT],
229+
["BlockMacroNode.target", CONTENT],
230+
["BlockMacroNode.attrlist", CONTENT],
231+
["FrontMatterNode.type", TYPE_DISCRIMINANT],
232+
["FrontMatterNode.content", CONTENT],
233+
["PreprocessorDirectiveNode.type", TYPE_DISCRIMINANT],
234+
["PreprocessorDirectiveNode.value", CONTENT],
235+
["PageBreakNode.type", TYPE_DISCRIMINANT],
236+
["ItemBody.text", STRUCTURAL],
237+
["ItemBody.blocks", STRUCTURAL],
238+
["ListItemNode.type", TYPE_DISCRIMINANT],
239+
["ItemBlock.block", STRUCTURAL],
240+
["DescriptionListNode.type", TYPE_DISCRIMINANT],
241+
["DescriptionListNode.delimiter", "grouping key, not bytes"],
242+
["DescriptionListNode.children", STRUCTURAL],
243+
["DescriptionTermNode.type", TYPE_DISCRIMINANT],
244+
["DescriptionTermNode.children", STRUCTURAL],
245+
["DescriptionListItemNode.type", TYPE_DISCRIMINANT],
246+
["DescriptionListItemNode.terms", STRUCTURAL],
247+
["DescriptionListItemNode.textLines", "replay bytes; printing is the fact"],
248+
["TermEntry.term", STRUCTURAL],
249+
["TermGapComment.comment", CONTENT],
250+
["BlockAttributeListNode.type", TYPE_DISCRIMINANT],
251+
["BlockAttributeListNode.value", CONTENT],
252+
["BlockTitleNode.type", TYPE_DISCRIMINANT],
253+
["BlockTitleNode.title", CONTENT],
254+
["BlockAnchorNode.type", TYPE_DISCRIMINANT],
255+
["BlockAnchorNode.id", CONTENT],
256+
["BlockAnchorNode.reftext", "ordinary optional content"],
257+
["TableNode.type", TYPE_DISCRIMINANT],
258+
["TableNode.children", STRUCTURAL],
259+
["TableNode.close", CONTAINER],
260+
["TableNode.cutting", CONTAINER],
261+
["TableNode.header", "own doc: derived predicate"],
262+
["TableNode.footer", UNREAD],
263+
["TableClose#0.image", UNREAD],
264+
["TableColumnSpec.halign", UNREAD],
265+
["TableColumnSpec.valign", UNREAD],
266+
["TableRowNode.type", TYPE_DISCRIMINANT],
267+
["TableRowNode.children", STRUCTURAL],
268+
["TableCellNode.type", TYPE_DISCRIMINANT],
269+
["TableCellNode.opening", CONTAINER],
270+
["TableCellNode.runs", CONTAINER],
271+
["TableCellNode.repeat", CONTAINER],
272+
["TableCellOpening#0.parsed", CONTAINER],
273+
["TableCellOpening#0.spec", "concatenated unconditionally into cellImage"],
274+
[
275+
"TableCellOpening#0.separator",
276+
"concatenated unconditionally into cellImage",
277+
],
278+
["TableCellOpening#0.offset", POSITION],
279+
["TableCellOpening#1.offset", POSITION],
280+
["TableTextRun.offset", POSITION],
281+
["TableCellSpec.repeat", UNREAD],
282+
["TableCellSpec.halign", UNREAD],
283+
["TableCellSpec.valign", UNREAD],
284+
]);

0 commit comments

Comments
 (0)