Files
PIG-Demo/scripts/check-receipts.mjs
karti-ai eb88138d15 Frontend: site chrome, demo shell, pages, and the contract gates
Five parallel lanes plus an integration pass. The header, gallery, router and
sitemap are all generated from the demo registry, so adding src/demos/<slug>/
puts a demo everywhere with zero edits to shared files — which is the whole
reason demo nine cannot break demo one.

check-demos enforces the twelve contract rules: 142 checks over one live demo.
Two worth naming. The shell may not mention a specific slug, because an
'if (slug === wordle)' in src/components/demo/ is a contract bug wearing a
patch. And a spec-status demo must ship a real specification — task, actions,
grader, counterweight, eval command — since a coming-soon card reads worse than
an honest empty gallery.

Bundle budget holds: entry 108.79 kB gzipped against a 160 kB ceiling, the demo
chunk 21.15 kB against 90 kB. recharts is 108 kB gzipped and lives behind a lazy
import so it never touches the entry.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019mt6sHQHEnEYrJZvoMCJSB
2026-08-28 16:09:59 -07:00

197 lines
7.3 KiB
JavaScript

#!/usr/bin/env node
/**
* The receipt checker.
*
* Every demo quotes the code that computes its reward, verbatim, next to the
* number that code produced. That quote is the page's whole claim to being
* evidence rather than marketing, so the thing it quotes has to exist and the
* region it quotes has to resolve. A receipt panel that silently renders empty,
* or renders the wrong forty lines because someone inserted a function above
* the marker, is worse than no receipt at all.
*
* ── MARKER SYNTAX ──────────────────────────────────────────────────────────
*
* `RewardSpec.source` is `{ path, code, marker? }`. Without a `marker` the
* whole file is the receipt. With one, the receipt is the region delimited by
* a matched pair of comment lines in the source file itself:
*
* # region: pig-demo/<marker>
* ... the quoted lines ...
* # endregion: pig-demo/<marker>
*
* Rules, all enforced here:
*
* · The marker lines are EXCLUSIVE — neither appears in the quoted region.
* · EXACTLY ONE pair per file per marker. Zero is a broken receipt; two or
* more is ambiguous, and an ambiguous receipt silently quotes whichever
* region the reader's implementation happened to find first. Both are
* fatal.
* · `region` must come before `endregion`, and the region must be non-empty
* once the marker lines are removed.
* · The comment token is `#` (Python) or `//` (TypeScript), then optional
* whitespace. Everything else on the line is ignored, so
* `# region: pig-demo/consistency (see PR #41)` is legal.
* · The `pig-demo/` prefix is required. It is what makes these greppable and
* stops an editor's own `#region` folding markers from being read as
* receipts.
*
* Marker names are `[a-z0-9][a-z0-9-]*`: they end up in a grep, a CI message
* and a code comment, and mixed case in all three is a bug factory.
*/
import path from 'node:path';
import {
Report,
abs,
allDemoDirs,
demoFiles,
evalLiteral,
exists,
isUnresolved,
literalsAfter,
read,
rel,
} from './_lib.mjs';
const report = new Report('check-receipts');
const RULE_PATH = 'reward.source.path';
const RULE_MARKER = 'reward.source.marker';
const MARKER_NAME = /^[a-z0-9][a-z0-9-]*$/;
const markerLine = (kind, marker) =>
new RegExp(`^[ \\t]*(?:#|//)[ \\t]*${kind}:[ \\t]*pig-demo/${marker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?![\\w-])`, 'm');
/** Every `source: { ... }` literal in a demo, wherever the demo chose to put it. */
function receipts(slug) {
const out = [];
for (const file of demoFiles(slug)) {
const src = read(file);
for (const literal of literalsAfter(src, /\bsource\s*:\s*/, '{')) {
// `code` is legitimately an identifier here — the Python arrives through
// a Vite `?raw` import, which cannot resolve in plain Node — so the
// literal is read leniently and only `path` and `marker` are trusted.
const evaluated = evalLiteral(literal.text, 'RewardSpec.source', { lenient: true });
if (!evaluated.ok) {
out.push({ file, error: evaluated.error });
continue;
}
const value = evaluated.value;
if (!value || typeof value !== 'object' || !('path' in value)) continue; // some other `source:` key
const line = src.slice(0, literal.start).split('\n').length;
out.push({ file, line, value });
}
}
return out;
}
const slugs = allDemoDirs().filter((s) => !s.startsWith('_'));
if (slugs.length === 0) {
console.log('check-receipts: no demos to check.');
process.exit(0);
}
for (const slug of slugs) {
const found = receipts(slug);
if (found.length === 0) {
report.fail(
rel(path.join(abs('src', 'demos'), slug)),
RULE_PATH,
'no `reward.source` found. Every demo quotes the code that computes its reward; without a source ' +
'the receipt panel has nothing to show and the page is asserting its numbers rather than proving them.',
);
continue;
}
for (const receipt of found) {
const where = `${rel(receipt.file)}${receipt.line ? `:${receipt.line}` : ''}`;
if (receipt.error) {
report.fail(where, RULE_PATH, receipt.error);
continue;
}
const { path: sourcePath, marker } = receipt.value;
if (typeof sourcePath !== 'string' || sourcePath.trim() === '') {
report.fail(
where,
RULE_PATH,
`\`path\` is ${isUnresolved(sourcePath) ? 'an imported constant' : JSON.stringify(sourcePath)}. ` +
'It must be a literal repo-relative path, e.g. "envs/wordle_five/wordle_five/rubric.py", ' +
'so a reader can open the same file this page quotes.',
);
continue;
}
// Repo-relative by contract. A leading slash would mean the site root,
// which is not where the Python lives, and an absolute disk path would
// only work on the machine that wrote it.
const clean = sourcePath.replace(/^\.\//, '');
if (path.isAbsolute(clean)) {
report.fail(where, RULE_PATH, `\`path\` is absolute ("${sourcePath}"). Use a repo-relative path.`);
continue;
}
const onDisk = abs(clean);
if (!report.check(exists(onDisk), where, RULE_PATH, `\`path\` is "${sourcePath}", and ${rel(onDisk)} does not exist.`)) {
continue;
}
if (marker === undefined || marker === null) continue; // whole file is the receipt
if (typeof marker !== 'string' || !MARKER_NAME.test(marker)) {
report.fail(
where,
RULE_MARKER,
`\`marker\` is ${JSON.stringify(marker)}. Markers are lower-kebab-case (${MARKER_NAME}) because they ` +
'appear in a grep, a CI message and a code comment, and mixed case in all three is a bug factory.',
);
continue;
}
const body = read(onDisk);
const lines = body.split('\n');
const opens = [];
const closes = [];
lines.forEach((line, i) => {
if (markerLine('region', marker).test(line)) opens.push(i);
if (markerLine('endregion', marker).test(line)) closes.push(i);
});
const syntax = `Expected exactly one "# region: pig-demo/${marker}" and one "# endregion: pig-demo/${marker}".`;
if (opens.length !== 1 || closes.length !== 1) {
report.fail(
rel(onDisk),
RULE_MARKER,
`marker "${marker}" (referenced from ${where}) resolves to ${opens.length} region marker(s) and ` +
`${closes.length} endregion marker(s). ${syntax} ` +
(opens.length > 1 || closes.length > 1
? 'More than one pair is ambiguous: the receipt would quote whichever region the reader found first.'
: 'A missing marker renders the receipt panel empty, which reads as the code not existing.'),
);
continue;
}
const [open] = opens;
const [close] = closes;
if (close <= open) {
report.fail(
rel(onDisk),
RULE_MARKER,
`marker "${marker}": the endregion is on line ${close + 1}, at or before the region on line ${open + 1}.`,
);
continue;
}
const region = lines.slice(open + 1, close);
report.check(
region.some((line) => line.trim() !== ''),
rel(onDisk),
RULE_MARKER,
`marker "${marker}" delimits an empty region between lines ${open + 1} and ${close + 1}. The markers are ` +
'exclusive, so a pair on adjacent lines quotes nothing.',
);
}
}
report.finish();