doxa-maps/internal/check-capability-exemplars.mjs
doxa-maps-88655c70 44ef31b2c6 Tooling reads the tree's name from the build config, in one place
The output tree's name has always been configurable (vite.doxa-maps-build-config.json "name"),
but 61 tooling files ignored it and hardcoded 'doxa-maps' (181 literals), so a rename would have
moved the build while the gates kept reading the old folder.

- internal/_tree-name.mjs reads it once (validated as one URL-safe path segment; falls back to the
  historical name) and exports TREE_NAME / TREE_NAME_RE; `node internal/_tree-name.mjs` prints it
  for shell scripts.
- A codemod rewrote 171 literals in internal/ to TREE_NAME; the regex literals, messages with
  backticks, atomic-public-swap.sh (fails loudly if the name cannot be read), build-lock.sh and
  vite.config.js (its duplicate readMfeName is gone) were done by hand.
- The name is unchanged: old and new tooling produce all 1408 files under public/ byte for byte.
  Code lines still naming the tree in internal/ + vite.config.js: 2, both intended (the helper's
  fallback, the private-repo-name pattern in public-hygiene-check).

This renames nothing visible; the eventual rename becomes a one-line config change.
2026-09-25 16:25:22 +00:00

65 lines
3.2 KiB
JavaScript

#!/usr/bin/env node
/**
* check-capability-exemplars.mjs — the generated exemplar table must match the manifest.
*
* Companion to generate-capability-exemplars.mjs. That file's header explains WHY the table is
* derived; this one makes the "derived, so it cannot drift" claim true by REGENERATING and
* comparing, rather than trusting mtimes.
*
* ★ WHY CONTENT-COMPARE AND NOT MTIME. check:library-index compares mtimes, which is right there
* (its generator scans library/ directly, so a source edit really does invalidate the output).
* Here the input is manifest.json, which a full build REWRITES EVERY TIME even when nothing
* changed — so an mtime rule would fail after every build for no reason, and a gate that cries
* wolf gets switched off. Comparing regenerated CONTENT fails only when the answer actually moved.
*
* The same reasoning is why CI regenerates-and-diffs the library index instead of using mtimes:
* a fresh checkout gives every file the same timestamp.
*
* NEGATIVE-TESTED: deleting a row from the table makes this FAIL showing the diff; regenerating
* returns PASS. Deleting the file entirely also FAILs rather than silently skipping.
*/
import { readFileSync, existsSync } from 'node:fs'
import { execFileSync } from 'node:child_process'
import { resolve, dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { TREE_NAME } from './_tree-name.mjs'
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')
const OUT = join(ROOT, 'documentation', 'reference-capability-exemplars.md')
const GEN = join(ROOT, 'internal', 'generate-capability-exemplars.mjs')
const MANIFEST = join(ROOT, 'public', TREE_NAME, 'manifest.json')
if (!existsSync(MANIFEST)) {
console.log('CAPABILITY-EXEMPLARS: SKIP — no manifest.json (nothing built yet).')
process.exit(0)
}
if (!existsSync(OUT)) {
console.error('CAPABILITY-EXEMPLARS: FAIL — documentation/reference-capability-exemplars.md does not exist.')
console.error(' It is the authoritative answer to "which bundle is the capability-N example?",')
console.error(' a question that has been answered wrongly in three separate artifacts.')
console.error(' Fix: bun run capability-exemplars')
process.exit(1)
}
const before = readFileSync(OUT, 'utf8')
execFileSync(process.execPath, [GEN], { stdio: 'pipe' })
const after = readFileSync(OUT, 'utf8')
if (before !== after) {
// Restore what was committed so the check itself never mutates the tree.
const { writeFileSync } = await import('node:fs')
writeFileSync(OUT, before)
const bl = before.split('\n'), al = after.split('\n')
const diff = []
for (let i = 0; i < Math.max(bl.length, al.length) && diff.length < 10; i++) {
if (bl[i] !== al[i]) diff.push(` line ${i + 1}\n file: ${bl[i] ?? '(missing)'}\n real: ${al[i] ?? '(missing)'}`)
}
console.error('\nCAPABILITY-EXEMPLARS: FAIL — the table disagrees with the manifest:')
console.error(diff.join('\n'))
console.error('\n The manifest is what the BUILD computed from each folder\'s real shape.')
console.error(' Fix: bun run capability-exemplars (then commit the regenerated file).')
process.exit(1)
}
const rows = (after.match(/^\| `/gm) || []).length
console.log(`CAPABILITY-EXEMPLARS: PASS — ${rows} exemplar row(s) match the manifest.`)