diff --git a/apps/website/public/agents.t27 b/apps/website/public/agents.t27 index 17836e5e25..d30f1676d1 100644 --- a/apps/website/public/agents.t27 +++ b/apps/website/public/agents.t27 @@ -1,5 +1,5 @@ ; GENERATED by apps/website/scripts/onboarding-from-spec.mjs (gHashTag/trinity) -; from specs/catalog/onboarding.t27, sha256 b0b993292013f0de28d9111de91d529eeac2101f1510d5a7d46d994c37bb828a +; from specs/catalog/onboarding.t27, sha256 9a45a5d4f2d95131b5d9253315b64dcd19f7712f793a62e19c4dcf2a0af543f4 ; Served as https://t27.ai/agents.t27 and https://t27.ai/llms.txt -- the same bytes at both addresses. ; Do not edit either file: edit the spec and re-run the generator. @@ -104,7 +104,8 @@ pub const REQUIRES_RUNNING_OUR_CODE : bool = false; pub const MEASURED_AT : str = "2026-10-04"; pub const SPEC_COUNT : u16 = 1778; pub const SPEC_LINES : u32 = 490448; -; Health as the manifest reports it. The three add up to SPEC_COUNT, and the test below says so. +; Health as the manifest reports it, over the files that are modules. Those three plus +; HEALTH_NOT_MODULE add up to SPEC_COUNT, and the test below says so. ; These moved a long way on 2026-09-21, and the cause was the instrument, not the corpus: the ; wasm served here until then was built from no committed source, and it accepted anything. ; Fed a Zig file, a Markdown file or a string literal left open, it answered typecheck.ok and @@ -119,6 +120,18 @@ pub const SPEC_LINES : u32 = 490448; pub const HEALTH_OK : u16 = 1370; pub const HEALTH_WARN : u16 = 232; pub const HEALTH_FAIL : u16 = 176; +; A file with the .t27 extension is not always a t27 module, and a document is not a broken +; spec. The compiler's own classifier -- the rule `t27c classify` has always used, shared with +; the wasm bridge since gHashTag/t27#4541 -- answers that question before the parser is asked, +; and sync-t27-specs writes its verdict per file as sourceKind plus a notSource total. The +; manifest served today was written by a wasm without the classifier, so it carries no +; notSource and every file counts as a module: this is 0 until the next resync, and the gate +; compares it against manifest.notSource.total rather than trusting it. When the classifier was +; first run over the corpus on 2026-09-21 it found 121 of 1454 files that were not modules, and +; 31 of those compiled cleanly -- whether a file is a module and whether it compiles are two +; questions, which is why this is a fourth group and not a fourth health state. +pub const HEALTH_NOT_MODULE : u16 = 0; +pub const HEALTH_NOT_MODULE_NOTE : str = "not-a-module is the classifier's verdict, not a health state; the manifest's notSource.byKind splits it and notSource.byHealth shows these files compile or fail on their own"; ; Repositories the manifest was built from, and the worlds the scan found on GitHub. pub const REPO_COUNT : u8 = 14; pub const WORLD_COUNT : u8 = 9; @@ -128,9 +141,10 @@ pub const WORLD_COUNT : u8 = 9; ; Do not report the second count as the first, and do not read the total as that many broken specs. ; A file with the .t27 extension is not always t27: Zig source, Markdown prose, an older ; `spec Name { }` dialect and fixtures this repository damages on purpose all sit in the first -; group. This file no longer prints a count per class. It carried one until 2026-09-21, hand -; typed, adding to 154 beside a sentence that said 145 -- a breakdown nothing checked, which is -; the one kind of number this document is not allowed to hold. +; group until the manifest carries the classifier's verdict, after which they leave for +; HEALTH_NOT_MODULE above. This file no longer prints a count per class. It carried one until +; 2026-09-21, hand typed, adding to 154 beside a sentence that said 145 -- a breakdown nothing +; checked, which is the one kind of number this document is not allowed to hold. pub const HEALTH_FAIL_NOTE : str = "health=fail covers both a spec that did not parse and a spec whose backend failed; the manifest's backendFailures separates them"; pub const HEALTH_FAIL_UNPARSED : u16 = 169; ; What is left once the unparsed are set aside was seven specs on 2026-09-21 that parsed, @@ -214,8 +228,14 @@ pub const ASKS_FOR_CREDENTIALS : bool = false; pub const ASKS_TO_ACT_ALONE : bool = false; test health_accounts_for_every_spec { - assert HEALTH_OK + HEALTH_WARN + HEALTH_FAIL == SPEC_COUNT; + // Four groups, not three. The health states are counted over modules; the rest of the + // corpus is HEALTH_NOT_MODULE. Leaving the fourth term out would let this assert hold + // over 1333 files while SPEC_COUNT says 1454 -- the sum would simply be wrong, quietly. + assert HEALTH_OK + HEALTH_WARN + HEALTH_FAIL + HEALTH_NOT_MODULE == SPEC_COUNT; assert HEALTH_FAIL > 0; + // Not every .t27 is a module. Zero is allowed: a manifest written before the classifier + // crossed the wasm boundary has no notSource, and the gate holds this to that manifest. + assert HEALTH_NOT_MODULE < SPEC_COUNT; // The qualifier is part of the count, not a footnote to it: each of these is a SUBSET of // fail, so a reading of HEALTH_FAIL that ignores them is reading a smaller number wrong. // A subset may be the whole: on 2026-09-24 every fail is unparsed, because the specs that diff --git a/apps/website/public/llms.txt b/apps/website/public/llms.txt index 17836e5e25..d30f1676d1 100644 --- a/apps/website/public/llms.txt +++ b/apps/website/public/llms.txt @@ -1,5 +1,5 @@ ; GENERATED by apps/website/scripts/onboarding-from-spec.mjs (gHashTag/trinity) -; from specs/catalog/onboarding.t27, sha256 b0b993292013f0de28d9111de91d529eeac2101f1510d5a7d46d994c37bb828a +; from specs/catalog/onboarding.t27, sha256 9a45a5d4f2d95131b5d9253315b64dcd19f7712f793a62e19c4dcf2a0af543f4 ; Served as https://t27.ai/agents.t27 and https://t27.ai/llms.txt -- the same bytes at both addresses. ; Do not edit either file: edit the spec and re-run the generator. @@ -104,7 +104,8 @@ pub const REQUIRES_RUNNING_OUR_CODE : bool = false; pub const MEASURED_AT : str = "2026-10-04"; pub const SPEC_COUNT : u16 = 1778; pub const SPEC_LINES : u32 = 490448; -; Health as the manifest reports it. The three add up to SPEC_COUNT, and the test below says so. +; Health as the manifest reports it, over the files that are modules. Those three plus +; HEALTH_NOT_MODULE add up to SPEC_COUNT, and the test below says so. ; These moved a long way on 2026-09-21, and the cause was the instrument, not the corpus: the ; wasm served here until then was built from no committed source, and it accepted anything. ; Fed a Zig file, a Markdown file or a string literal left open, it answered typecheck.ok and @@ -119,6 +120,18 @@ pub const SPEC_LINES : u32 = 490448; pub const HEALTH_OK : u16 = 1370; pub const HEALTH_WARN : u16 = 232; pub const HEALTH_FAIL : u16 = 176; +; A file with the .t27 extension is not always a t27 module, and a document is not a broken +; spec. The compiler's own classifier -- the rule `t27c classify` has always used, shared with +; the wasm bridge since gHashTag/t27#4541 -- answers that question before the parser is asked, +; and sync-t27-specs writes its verdict per file as sourceKind plus a notSource total. The +; manifest served today was written by a wasm without the classifier, so it carries no +; notSource and every file counts as a module: this is 0 until the next resync, and the gate +; compares it against manifest.notSource.total rather than trusting it. When the classifier was +; first run over the corpus on 2026-09-21 it found 121 of 1454 files that were not modules, and +; 31 of those compiled cleanly -- whether a file is a module and whether it compiles are two +; questions, which is why this is a fourth group and not a fourth health state. +pub const HEALTH_NOT_MODULE : u16 = 0; +pub const HEALTH_NOT_MODULE_NOTE : str = "not-a-module is the classifier's verdict, not a health state; the manifest's notSource.byKind splits it and notSource.byHealth shows these files compile or fail on their own"; ; Repositories the manifest was built from, and the worlds the scan found on GitHub. pub const REPO_COUNT : u8 = 14; pub const WORLD_COUNT : u8 = 9; @@ -128,9 +141,10 @@ pub const WORLD_COUNT : u8 = 9; ; Do not report the second count as the first, and do not read the total as that many broken specs. ; A file with the .t27 extension is not always t27: Zig source, Markdown prose, an older ; `spec Name { }` dialect and fixtures this repository damages on purpose all sit in the first -; group. This file no longer prints a count per class. It carried one until 2026-09-21, hand -; typed, adding to 154 beside a sentence that said 145 -- a breakdown nothing checked, which is -; the one kind of number this document is not allowed to hold. +; group until the manifest carries the classifier's verdict, after which they leave for +; HEALTH_NOT_MODULE above. This file no longer prints a count per class. It carried one until +; 2026-09-21, hand typed, adding to 154 beside a sentence that said 145 -- a breakdown nothing +; checked, which is the one kind of number this document is not allowed to hold. pub const HEALTH_FAIL_NOTE : str = "health=fail covers both a spec that did not parse and a spec whose backend failed; the manifest's backendFailures separates them"; pub const HEALTH_FAIL_UNPARSED : u16 = 169; ; What is left once the unparsed are set aside was seven specs on 2026-09-21 that parsed, @@ -214,8 +228,14 @@ pub const ASKS_FOR_CREDENTIALS : bool = false; pub const ASKS_TO_ACT_ALONE : bool = false; test health_accounts_for_every_spec { - assert HEALTH_OK + HEALTH_WARN + HEALTH_FAIL == SPEC_COUNT; + // Four groups, not three. The health states are counted over modules; the rest of the + // corpus is HEALTH_NOT_MODULE. Leaving the fourth term out would let this assert hold + // over 1333 files while SPEC_COUNT says 1454 -- the sum would simply be wrong, quietly. + assert HEALTH_OK + HEALTH_WARN + HEALTH_FAIL + HEALTH_NOT_MODULE == SPEC_COUNT; assert HEALTH_FAIL > 0; + // Not every .t27 is a module. Zero is allowed: a manifest written before the classifier + // crossed the wasm boundary has no notSource, and the gate holds this to that manifest. + assert HEALTH_NOT_MODULE < SPEC_COUNT; // The qualifier is part of the count, not a footnote to it: each of these is a SUBSET of // fail, so a reading of HEALTH_FAIL that ignores them is reading a smaller number wrong. // A subset may be the whole: on 2026-09-24 every fail is unparsed, because the specs that diff --git a/apps/website/qa/language-exceptions.json b/apps/website/qa/language-exceptions.json index acbab461e2..f563143083 100644 --- a/apps/website/qa/language-exceptions.json +++ b/apps/website/qa/language-exceptions.json @@ -100,7 +100,7 @@ "Flow Control - advanced flow control and backpressure Enables end-to-end flow management and congestion prevention", "Formula Discovery v1.0 — ULTRA ENGINE Specification", "Forward Pass Demo - VSA-based Neural Network Implements transformer-style forward pass using Vector Symbolic Architecture with multi-head attention, residual connections, and autoregressive generation Author: Dmitrii Vasilev", - "GENERATED by apps/website/scripts/onboarding-from-spec.mjs (gHashTag/trinity) from specs/catalog/onboarding.t27, sha256 ef7c4c5ad4e37abd2a64300d3409bdf724540905e6daf8f15088dfb712ee3732 Served as https://t27.ai/agents.t27 and https://t27.ai/llms.txt -- the same bytes at both addresses.", + "GENERATED by apps/website/scripts/onboarding-from-spec.mjs (gHashTag/trinity) from specs/catalog/onboarding.t27, sha256 9a45a5d4f2d95131b5d9253315b64dcd19f7712f793a62e19c4dcf2a0af543f4 Served as https://t27.ai/agents.t27 and https://t27.ai/llms.txt -- the same bytes at both addresses.", "GENERATED by tools/generate-runtime-tables.py from specs/runtimes/*.json; do not edit. Runtime tables (issue #51): for each pinned runtime, the ggml type ids it defines (block size in weights, bytes per block, the ggml_nbytes rule), the architecture names its model mapping turns into a model class and those it refuses as a main model. Sources, per runtime: 1 llama.cpp: ggml-org/llama.cpp@4364bf7232e65c34eca8d9500c5464389662de6b", "Generated from FORMULA_TABLE_v06.md and FORMULA_TABLE_v07.md SSOT for Trinity formula discovery", "GoldenFloat Cross-Language Conformance All languages must produce identical bits for identical inputs. Reference constant: GF32(φ) = 0x3FCF1BBD", diff --git a/apps/website/scripts/onboarding-from-spec.mjs b/apps/website/scripts/onboarding-from-spec.mjs index 483abe8db6..ff5bb2851a 100644 --- a/apps/website/scripts/onboarding-from-spec.mjs +++ b/apps/website/scripts/onboarding-from-spec.mjs @@ -26,6 +26,11 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs' import { dirname, join } from 'node:path' import { fileURLToPath } from 'node:url' import { CYRILLIC, SITE, checkSchema, compilerErrors, constsOf, loadCompiler, sha256, verdictOf } from './agents-from-specs.mjs' +// The one rule for "is this entry a compilation unit?", imported rather than +// retyped. Every derived count below is a subset of the health states, and the +// health states are counted over modules only -- a second copy of the test here +// is exactly how the two denominators drift apart again. +import { isSourceEntry } from './t27-corpus.mjs' import { runSpecTests } from './viewport-from-spec.mjs' const WASM = 'public/t27/t27_compiler.wasm' @@ -55,7 +60,8 @@ export const ONBOARDING_REQUIRED = { BACKENDS: 'arr', BACKENDS_NOTE: 'str', TS_SHARES_THE_JS_VALUE_LAYER: 'bool', JS_TS_DIVERGENCES: 'u16', MEASURED_AT: 'str', SPEC_COUNT: 'u16', SPEC_LINES: 'u32', - HEALTH_OK: 'u16', HEALTH_WARN: 'u16', HEALTH_FAIL: 'u16', HEALTH_FAIL_NOTE: 'str', + HEALTH_OK: 'u16', HEALTH_WARN: 'u16', HEALTH_FAIL: 'u16', HEALTH_NOT_MODULE: 'u16', + HEALTH_FAIL_NOTE: 'str', HEALTH_NOT_MODULE_NOTE: 'str', HEALTH_FAIL_UNPARSED: 'u16', HEALTH_FAIL_JS_ONLY: 'u16', HEALTH_PARTIAL: 'u16', REPO_COUNT: 'u8', WORLD_COUNT: 'u8', GAME: 'str', GAME_DOC: 'str', GAME_BOARD: 'str', WIN_CONDITION: 'str', @@ -102,8 +108,22 @@ export function semanticProblems(f, file) { if (!f.COMPILER_EXPORTS.includes(name)) p.push(`${file}: COMPILER_EXPORTS must name ${name}`) } - if (f.HEALTH_OK + f.HEALTH_WARN + f.HEALTH_FAIL !== f.SPEC_COUNT) { - p.push(`${file}: health ${f.HEALTH_OK}+${f.HEALTH_WARN}+${f.HEALTH_FAIL} does not add up to SPEC_COUNT ${f.SPEC_COUNT}`) + // Four groups now, not three. The health states are counted over modules, and + // HEALTH_NOT_MODULE is the rest of the `.t27` files -- Markdown documents, + // TRI-27 assembly, fixtures damaged on purpose. Dropping the fourth term + // rather than adding it would have let this invariant pass while the document + // claimed 1454 specs and accounted for 1333. + const counted = f.HEALTH_OK + f.HEALTH_WARN + f.HEALTH_FAIL + f.HEALTH_NOT_MODULE + if (counted !== f.SPEC_COUNT) { + p.push(`${file}: health ${f.HEALTH_OK}+${f.HEALTH_WARN}+${f.HEALTH_FAIL}+${f.HEALTH_NOT_MODULE} = ${counted} does not add up to SPEC_COUNT ${f.SPEC_COUNT}`) + } + // Both are subsets of the failures, and a subset larger than its set is the + // shape of a denominator mismatch rather than a new fact. + if (f.HEALTH_FAIL_UNPARSED > f.HEALTH_FAIL) { + p.push(`${file}: HEALTH_FAIL_UNPARSED ${f.HEALTH_FAIL_UNPARSED} exceeds HEALTH_FAIL ${f.HEALTH_FAIL}; one of them is counting non-modules`) + } + if (f.HEALTH_FAIL_JS_ONLY > f.HEALTH_FAIL) { + p.push(`${file}: HEALTH_FAIL_JS_ONLY ${f.HEALTH_FAIL_JS_ONLY} exceeds HEALTH_FAIL ${f.HEALTH_FAIL}`) } if (!/^\d{4}-\d{2}-\d{2}$/.test(f.MEASURED_AT)) p.push(`${file}: MEASURED_AT must be an ISO date; a snapshot without its day is not a snapshot`) @@ -165,6 +185,12 @@ export function measuredCounts(manifest) { HEALTH_OK: manifest.health.ok, HEALTH_WARN: manifest.health.warn, HEALTH_FAIL: manifest.health.fail, + // The `.t27` files that are not modules: Markdown documents that happen to + // carry the extension, TRI-27 assembly, fixtures damaged on purpose. They + // used to be counted as broken specs -- 81 of the 218 reds were files the + // parser was never meant to accept -- so they now leave the health states + // and are reported as their own group instead of vanishing. + HEALTH_NOT_MODULE: manifest.notSource?.total ?? 0, REPO_COUNT: manifest.repos.length, WORLD_COUNT: manifest.discovery.worlds.length, // The share of health=fail that is the declarations-only backends declining to emit, on @@ -177,16 +203,20 @@ export function measuredCounts(manifest) { // matches nothing, and the constant would have been "corrected" to zero by a generator // that was measuring the wrong set. Compare against DECLARATIONS_ONLY instead, so the // next backend of this kind is a one-word edit there and not a wrong number here. - HEALTH_FAIL_JS_ONLY: manifest.specs.filter((s) => sameSet(s.failedBackends ?? [], DECLARATIONS_ONLY)).length, + HEALTH_FAIL_JS_ONLY: manifest.specs.filter((s) => isSourceEntry(s) && sameSet(s.failedBackends ?? [], DECLARATIONS_ONLY)).length, // The half of health=fail that HEALTH_FAIL_NOTE promises the manifest separates, actually // separated. Zero AST nodes means the parser produced nothing, so no backend ran: its // clean counters say nothing ran, not that nothing went wrong. The remainder parsed and // then lost a backend, which is a different fault and a different fix. - HEALTH_FAIL_UNPARSED: manifest.specs.filter((s) => s.health === 'fail' && !s.nodes).length, + // + // Module-only, like HEALTH_FAIL itself. Over every entry this counts 228 + // against a HEALTH_FAIL of 156 -- a subset larger than its set, which is + // the arithmetic of a Markdown document being called an unparsed spec. + HEALTH_FAIL_UNPARSED: manifest.specs.filter((s) => isSourceEntry(s) && s.health === 'fail' && !s.nodes).length, // A spec whose artifact exists and says, in itself, what it could not print. Counted here // for the same reason as the rest: a number this document states about the corpus is // measured from the corpus or it is not published. - HEALTH_PARTIAL: manifest.specs.filter((s) => (s.partialBackends ?? []).length > 0).length, + HEALTH_PARTIAL: manifest.specs.filter((s) => isSourceEntry(s) && (s.partialBackends ?? []).length > 0).length, } } diff --git a/apps/website/scripts/sync-t27-specs.mjs b/apps/website/scripts/sync-t27-specs.mjs index 8ebd88c635..1f9c95486c 100644 --- a/apps/website/scripts/sync-t27-specs.mjs +++ b/apps/website/scripts/sync-t27-specs.mjs @@ -421,7 +421,7 @@ else console.log(` warning: no tutorial specs found -- page will open on the fi entries.length = 0 entries.push(...tutorial, ...rest) -const { totalLines, categories, tags, health, backendFailures, totals } = corpusAggregates(entries) +const { totalLines, categories, tags, health, notSource, backendFailures, totals } = corpusAggregates(entries) // --------------------------------------------------------------------------- // Which vendored specs could someone else actually reproduce? @@ -509,6 +509,7 @@ writeFileSync(join(OUT_DIR, 'manifest.json'), JSON.stringify({ duplicates: duplicatePairs.sort((x, y) => x.path.localeCompare(y.path)), tags, health, + notSource, backendFailures, featured: FEATURED, totals, @@ -530,7 +531,11 @@ writeFileSync(join(OUT_DIR, 'manifest.json'), JSON.stringify({ console.log(`sync-t27-specs: ${entries.length} specs, ${Object.keys(categories).length} categories`) console.log(` sources: ${sources.map((s) => s.repo).join(', ')} (${duplicates} duplicate files skipped by content hash)`) -console.log(` health: ${health.ok} ok · ${health.warn} warn · ${health.fail} fail`) +// Over modules, and the line says so. These three used to cover every vendored +// file, which meant 81 of the failures were Markdown documents and damaged +// fixtures being counted as broken specs -- a number that could not go down. +console.log(` health: ${health.ok} ok · ${health.warn} warn · ${health.fail} fail (of ${health.ok + health.warn + health.fail} modules)`) +console.log(` not a module: ${notSource.total} ${Object.entries(notSource.byKind).sort((a, b) => b[1] - a[1]).map(([k, n]) => `${k} ${n}`).join(' · ')}`) if (Object.keys(backendFailures).length) console.log(` backend failures: ${JSON.stringify(backendFailures)}`) console.log(` t27 @ ${shortSha}${dirty ? ' (DIRTY -- snapshot includes uncommitted spec/compiler changes)' : ''}`) console.log(` wasm ${(wasmBytes / 1024).toFixed(0)} KB -> public/t27/t27_compiler.wasm (${wasmFrom})`) diff --git a/apps/website/scripts/t27-corpus.mjs b/apps/website/scripts/t27-corpus.mjs index 06878846e9..96ed257978 100644 --- a/apps/website/scripts/t27-corpus.mjs +++ b/apps/website/scripts/t27-corpus.mjs @@ -262,6 +262,16 @@ export function corpusEntry(rel, repo, text, analyze) { // Output size per backend, so the library can show what a spec actually // produces without re-running the compiler. outBytes: a ? Object.fromEntries(Object.entries(a.targets).map(([k, v]) => [k, v.ok ? v.bytes : null])) : {}, + // What this file IS, straight from the compiler's own classifier -- the + // same function `t27c classify` runs, reached through the wasm bridge + // rather than re-derived here, because three earlier reimplementations of + // it each produced a different number. + // + // Defaults to `source` on purpose. A compiler too old to answer, or one + // that threw before it could, must not move a file out of the spec count: + // a file leaves only when the classifier positively says it is not a + // module, never on missing information. + sourceKind: a?.sourceKind ?? 'source', repo, kinds, } @@ -270,12 +280,40 @@ export function corpusEntry(rel, repo, text, analyze) { return entry } +/** Whether an entry is an ordinary compilation unit -- see `sourceKind` above. */ +export const isSourceEntry = (e) => (e.sourceKind ?? 'source') === 'source' + /** The corpus-wide counts the manifest carries, from the entries in their manifest order. */ export function corpusAggregates(entries) { const byCategory = {} for (const e of entries) byCategory[e.category] = (byCategory[e.category] || 0) + 1 + // Health is counted over SOURCE FILES ONLY, and this is the whole point of + // asking what a file is. + // + // A `.t27` extension is a filename, not a type declaration. The corpus holds + // 46 Markdown documents, 66 files that are neither module nor spec nor + // Markdown -- TRI-27 assembly listings whose every line opens with `;`, + // fixtures under `bootstrap/tests/fixtures/damage/` that exist to be damaged + // -- and 8 in the older `spec X { }` surface. The parser answers about all of + // them the only way it can, and counting that answer as a broken spec was + // 81 of the 218 red chips on 2026-09-21: a number that could never go down, + // because nothing was wrong. + // + // The per-entry `health` is NOT rewritten. It stays exactly what the compiler + // measured, so a reader who opens one of these files still sees what happened + // to it. Only the denominator changes, and the files that leave are counted + // in `notSource` rather than dropped -- 31 of them compile cleanly, which is + // a fact about them worth keeping and not a reason to call them specs. const health = { ok: 0, warn: 0, fail: 0 } - for (const e of entries) health[e.health]++ + for (const e of entries) if (isSourceEntry(e)) health[e.health]++ + const notSource = { total: 0, byKind: {}, byHealth: { ok: 0, warn: 0, fail: 0 } } + for (const e of entries) { + if (isSourceEntry(e)) continue + notSource.total++ + const k = e.sourceKind ?? 'unclassified' + notSource.byKind[k] = (notSource.byKind[k] || 0) + 1 + notSource.byHealth[e.health]++ + } const backendFailures = {} for (const e of entries) for (const b of e.failedBackends) backendFailures[b] = (backendFailures[b] || 0) + 1 return { @@ -292,6 +330,7 @@ export function corpusAggregates(entries) { ).sort((a, b) => (a[0] === b[0] ? 0 : b[1] - a[1] || a[0].localeCompare(b[0]))), ), health, + notSource, backendFailures, totals: { tokens: entries.reduce((a, e) => a + e.tokens, 0), diff --git a/apps/website/specs/catalog/onboarding.t27 b/apps/website/specs/catalog/onboarding.t27 index f3c99a15e0..1fd84371fa 100644 --- a/apps/website/specs/catalog/onboarding.t27 +++ b/apps/website/specs/catalog/onboarding.t27 @@ -92,7 +92,8 @@ pub const REQUIRES_RUNNING_OUR_CODE : bool = false; pub const MEASURED_AT : str = "2026-10-04"; pub const SPEC_COUNT : u16 = 1778; pub const SPEC_LINES : u32 = 490448; -; Health as the manifest reports it. The three add up to SPEC_COUNT, and the test below says so. +; Health as the manifest reports it, over the files that are modules. Those three plus +; HEALTH_NOT_MODULE add up to SPEC_COUNT, and the test below says so. ; These moved a long way on 2026-09-21, and the cause was the instrument, not the corpus: the ; wasm served here until then was built from no committed source, and it accepted anything. ; Fed a Zig file, a Markdown file or a string literal left open, it answered typecheck.ok and @@ -107,6 +108,18 @@ pub const SPEC_LINES : u32 = 490448; pub const HEALTH_OK : u16 = 1370; pub const HEALTH_WARN : u16 = 232; pub const HEALTH_FAIL : u16 = 176; +; A file with the .t27 extension is not always a t27 module, and a document is not a broken +; spec. The compiler's own classifier -- the rule `t27c classify` has always used, shared with +; the wasm bridge since gHashTag/t27#4541 -- answers that question before the parser is asked, +; and sync-t27-specs writes its verdict per file as sourceKind plus a notSource total. The +; manifest served today was written by a wasm without the classifier, so it carries no +; notSource and every file counts as a module: this is 0 until the next resync, and the gate +; compares it against manifest.notSource.total rather than trusting it. When the classifier was +; first run over the corpus on 2026-09-21 it found 121 of 1454 files that were not modules, and +; 31 of those compiled cleanly -- whether a file is a module and whether it compiles are two +; questions, which is why this is a fourth group and not a fourth health state. +pub const HEALTH_NOT_MODULE : u16 = 0; +pub const HEALTH_NOT_MODULE_NOTE : str = "not-a-module is the classifier's verdict, not a health state; the manifest's notSource.byKind splits it and notSource.byHealth shows these files compile or fail on their own"; ; Repositories the manifest was built from, and the worlds the scan found on GitHub. pub const REPO_COUNT : u8 = 14; pub const WORLD_COUNT : u8 = 9; @@ -116,9 +129,10 @@ pub const WORLD_COUNT : u8 = 9; ; Do not report the second count as the first, and do not read the total as that many broken specs. ; A file with the .t27 extension is not always t27: Zig source, Markdown prose, an older ; `spec Name { }` dialect and fixtures this repository damages on purpose all sit in the first -; group. This file no longer prints a count per class. It carried one until 2026-09-21, hand -; typed, adding to 154 beside a sentence that said 145 -- a breakdown nothing checked, which is -; the one kind of number this document is not allowed to hold. +; group until the manifest carries the classifier's verdict, after which they leave for +; HEALTH_NOT_MODULE above. This file no longer prints a count per class. It carried one until +; 2026-09-21, hand typed, adding to 154 beside a sentence that said 145 -- a breakdown nothing +; checked, which is the one kind of number this document is not allowed to hold. pub const HEALTH_FAIL_NOTE : str = "health=fail covers both a spec that did not parse and a spec whose backend failed; the manifest's backendFailures separates them"; pub const HEALTH_FAIL_UNPARSED : u16 = 169; ; What is left once the unparsed are set aside was seven specs on 2026-09-21 that parsed, @@ -202,8 +216,14 @@ pub const ASKS_FOR_CREDENTIALS : bool = false; pub const ASKS_TO_ACT_ALONE : bool = false; test health_accounts_for_every_spec { - assert HEALTH_OK + HEALTH_WARN + HEALTH_FAIL == SPEC_COUNT; + // Four groups, not three. The health states are counted over modules; the rest of the + // corpus is HEALTH_NOT_MODULE. Leaving the fourth term out would let this assert hold + // over 1333 files while SPEC_COUNT says 1454 -- the sum would simply be wrong, quietly. + assert HEALTH_OK + HEALTH_WARN + HEALTH_FAIL + HEALTH_NOT_MODULE == SPEC_COUNT; assert HEALTH_FAIL > 0; + // Not every .t27 is a module. Zero is allowed: a manifest written before the classifier + // crossed the wasm boundary has no notSource, and the gate holds this to that manifest. + assert HEALTH_NOT_MODULE < SPEC_COUNT; // The qualifier is part of the count, not a footnote to it: each of these is a SUBSET of // fail, so a reading of HEALTH_FAIL that ignores them is reading a smaller number wrong. // A subset may be the whole: on 2026-09-24 every fail is unparsed, because the specs that diff --git a/apps/website/src/lib/t27Compiler.ts b/apps/website/src/lib/t27Compiler.ts index 1caf999775..4718d33ecd 100644 --- a/apps/website/src/lib/t27Compiler.ts +++ b/apps/website/src/lib/t27Compiler.ts @@ -111,6 +111,19 @@ export const TARGET_LABEL: Record = { export type Health = 'ok' | 'warn' | 'fail' +/** + * What a `.t27` file is, from the compiler's own classifier + * (`bootstrap/src/source_kind.rs`, reached through the wasm bridge). + * + * `unclassified` is a grab-bag and not a synonym for prose: it holds TRI-27 + * assembly, fixtures whose `module` line was damaged on purpose, and files that + * open at a `const`. Do not render it as "not code". + * + * This is not a prediction about compiling. 31 non-`source` files in the corpus + * compile cleanly; plenty of `source` files do not. + */ +export type SourceKind = 'source' | 'alt-syntax' | 'not-code' | 'mixed' | 'unclassified' + export interface SpecEntry { path: string category: string @@ -120,8 +133,16 @@ export interface SpecEntry { bytes: number /** Leading comment block of the spec, boilerplate stripped. */ description: string | null - /** Precomputed at sync time by running this same compiler over the corpus. */ + /** + * Precomputed at sync time by running this same compiler over the corpus. + * + * Measured for every entry including non-`source` ones, so opening a Markdown + * document still shows what the compiler did with it. The corpus-wide counts + * in `SpecManifest.health` are over `source` entries only. + */ health: Health + /** What this file is, before anything asks whether it compiles. */ + sourceKind: SourceKind tokens: number nodes: number depth: number @@ -151,7 +172,17 @@ export interface SpecManifest { specCount: number totalLines: number categories: Record + /** + * Health over the `source` entries only -- these three sum to + * `specCount - notSource.total`, not to `specCount`. + */ health: Record + /** + * The files under a `.t27` extension that are not compilation units. Absent + * on a catalog written before the classifier crossed the wasm boundary, in + * which case `health` is over everything and sums to `specCount`. + */ + notSource?: { total: number; byKind: Record; byHealth: Record } backendFailures: Record featured: string totals: { tokens: number; nodes: number; lossAffected: number; tcAffected: number } diff --git a/apps/website/src/pages/SpecExplorer.tsx b/apps/website/src/pages/SpecExplorer.tsx index 61d13d888b..9ca48f5700 100644 --- a/apps/website/src/pages/SpecExplorer.tsx +++ b/apps/website/src/pages/SpecExplorer.tsx @@ -133,6 +133,9 @@ const UI = { healthOk: 'Clean through every layer', healthWarn: 'Compiles, but the compiler flagged or dropped something', healthFail: 'A backend refused this spec outright', + notSource: 'Not a module', + notSourceHint: + 'Files under a .t27 extension that are not compilation units: Markdown documents, TRI-27 assembly, fixtures damaged on purpose, and the older spec X { } surface. The three health counts are over modules only.', corpusHealth: 'corpus health', startHere: 'START HERE', noneInGroup: 'Nothing in this group.', @@ -236,6 +239,9 @@ const UI = { healthOk: 'Чисто на всех слоях', healthWarn: 'Компилируется, но компилятор что-то отбросил или пометил', healthFail: 'Бэкенд отказался обрабатывать эту спеку', + notSource: 'Не модуль', + notSourceHint: + 'Файлы с расширением .t27, которые не являются единицами компиляции: документы Markdown, ассемблер TRI-27, намеренно повреждённые фикстуры и старая форма spec X { }. Три счётчика здоровья считаются только по модулям.', corpusHealth: 'здоровье корпуса', startHere: 'НАЧНИТЕ ЗДЕСЬ', noneInGroup: 'В этой группе пусто.', @@ -286,6 +292,30 @@ function fmtBytes(n: number): string { return `${(n / 1024).toFixed(1)}K` } +/** The group a chip selects. `not-source` crosses the health axis rather than extending it. */ +type SpecGroup = Health | 'all' | 'course' | 'not-source' + +/** + * Whether a spec belongs in the selected group. + * + * One function, two callers: the list and the tag-facet counts. Those were the + * same three lines written out twice, a few lines apart -- which is exactly how + * a facet ends up promising results the list will not show. + * + * The three health chips count MODULES only, matching the manifest's `health` + * block, so the chip and the list it opens can never disagree. A file that is + * not a compilation unit is in `not-source` whatever the compiler did with it; + * `sourceKind` defaults to `source` so a catalog written before the classifier + * existed behaves exactly as it did. + */ +function inGroup(s: SpecEntry, group: SpecGroup): boolean { + if (group === 'course') return !!s.tutorial + const isSource = (s.sourceKind ?? 'source') === 'source' + if (group === 'not-source') return !isSource + if (group === 'all') return true + return isSource && s.health === group +} + /** Colour a node by its family so structure reads at a glance. */ function kindColor(kind: string): string { if (kind.startsWith('Expr')) return C.accent @@ -443,7 +473,10 @@ export default function SpecExplorer() { // which of 760 specs to open, and "Working 522" is still 522 unordered // files; the eight lessons are the one path through this that starts // somewhere. Every other group is one click away and carries its count. - const [healthFilter, setHealthFilter] = useState('course') + // `not-source` is a fourth group beside the three health states rather than a + // fourth health state: it answers a different question (what IS this file) + // and the two axes cross. 31 files here compile cleanly. + const [healthFilter, setHealthFilter] = useState('course') // Multi-select, AND across selections: picking domain/fpga + has/tests means // "FPGA specs that have tests", which is the question people actually ask. const [tagSel, setTagSel] = useState([]) @@ -630,9 +663,7 @@ export default function SpecExplorer() { const q = query.trim().toLowerCase() return manifest.specs.filter((s) => { if (!inWorld(s)) return false - if (healthFilter === 'course') { - if (!s.tutorial) return false - } else if (healthFilter !== 'all' && s.health !== healthFilter) return false + if (!inGroup(s, healthFilter)) return false if (tagSel.length && !tagSel.every((t) => s.tags.includes(t))) return false if (category && s.category !== category) return false if (!q) return true @@ -657,8 +688,7 @@ export default function SpecExplorer() { const q = query.trim().toLowerCase() const base = manifest.specs.filter((s) => { if (!inWorld(s)) return false - if (healthFilter === 'course') { if (!s.tutorial) return false } - else if (healthFilter !== 'all' && s.health !== healthFilter) return false + if (!inGroup(s, healthFilter)) return false if (category && s.category !== category) return false if (q && !(s.path.toLowerCase().includes(q) || s.name.toLowerCase().includes(q) || (s.module ? s.module.toLowerCase().includes(q) : false) || @@ -1090,15 +1120,21 @@ export default function SpecExplorer() { ['ok', ui.working, manifest.health.ok], ['warn', ui.warnings, manifest.health.warn], ['fail', ui.broken, manifest.health.fail], + // Counted from the entries, not read from `manifest.notSource`, + // so this chip is right on a catalog generated before that + // block existed instead of reading 0 over a full list. + ['not-source', ui.notSource, manifest.specs.filter((s) => inGroup(s, 'not-source')).length], ['all', ui.all, manifest.specCount], - ] as [Health | 'all' | 'course', string, number][]).map(([k, label, n]) => { + ] as [SpecGroup, string, number][]).map(([k, label, n]) => { const on = healthFilter === k - const col = k === 'all' ? C.muted : k === 'course' ? C.golden : HEALTH_COLOR[k] + const col = + k === 'all' || k === 'not-source' ? C.muted : k === 'course' ? C.golden : HEALTH_COLOR[k] return (