Capture harness, fixture verification, CI, and the public README

The site does no live inference. Rollouts are captured once against spark-1 and
replayed at their recorded wall-clock — a public demo with no auth cannot hold
an API key, and a recorded run can be scrubbed, permalinked, blind-compared and
verified in ways a live one cannot. What stops it being a video is that the
browser re-derives every number from the recorded moves.

verify_fixtures.py is the Python half of that: it replays every committed
fixture through the engine and reproduces its own rewards. All 16 land at
delta 0.0. A fixture that cannot be regenerated is a claim with no receipt.

First real measurement, thinking off, 8 seeds: solved 0/8. The model repeats
guesses it has already played, invents words (trape, slith, postt, boomy),
and contradicts its own feedback — consistency 0.09 to 0.17. That is the
published failure taxonomy showing up in our own data on the first run, and it
is why `consistency` is a reward component rather than a footnote.

A capture failure is recorded as a turn with a null reply, never dropped. A
capture that silently discarded failed turns would be reporting a better model
than the one that ran.

CI gates both halves and four things that fail silently in production: the word
lists must rebuild byte-identically, the prerendered routes must carry their own
baked og tags (crawlers do not run JS, so without them every shared link
previews as the homepage), no blob: URL may reach the bundle (the site's CSP has
no worker-src, so it falls back to default-src 'self' and a blob worker is
blocked with no error), and the conformance digest must match across languages.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019mt6sHQHEnEYrJZvoMCJSB
This commit is contained in:
karti-ai
2026-08-28 15:47:31 -07:00
parent 69607fbfe9
commit 408ce4a525
43 changed files with 5279 additions and 136 deletions
+58 -18
View File
@@ -294,7 +294,7 @@ function codeSpans(src) {
* @param {'{' | '['} open
* @returns {string | null}
*/
export function literalAfter(src, anchor, open = '{') {
export function literalsAfter(src, anchor, open = '{') {
const flags = anchor.flags.includes('g') ? anchor.flags : `${anchor.flags}g`;
const re = new RegExp(anchor.source, flags);
const spans = codeSpans(src);
@@ -302,18 +302,29 @@ export function literalAfter(src, anchor, open = '{') {
const hit = spans.find((s) => offset >= s.start && offset < s.end);
return hit ? hit.code : true;
};
const found = [];
let match;
while ((match = re.exec(src)) !== null) {
// The anchor may legally match inside a comment or a string a docblock
// The anchor may legally match inside a comment or a string \u2014 a docblock
// that quotes `const meta = {`. Only a match in real code counts.
if (!isCode(match.index)) continue;
const from = src.indexOf(open, match.index + Math.max(match[0].length - 1, 0));
if (from === -1) continue;
const between = src.slice(match.index + match[0].length, from);
if (/[;=}]/.test(between)) continue;
return src.slice(from, scanBalanced(src, from));
const end = scanBalanced(src, from);
found.push({ text: src.slice(from, end), start: from, end });
// Skip past this literal so a nested anchor of the same name is not
// reported as a second, overlapping hit.
re.lastIndex = end;
}
return null;
return found;
}
/** First literal following `anchor`, or null. See {@link literalsAfter}. */
export function literalAfter(src, anchor, open = '{') {
const [first] = literalsAfter(src, anchor, open);
return first ? first.text : null;
}
/** Marker key on the stand-in an unresolvable identifier evaluates to. */
@@ -501,7 +512,10 @@ const VERTICAL_ANCHORS = [
const asVertical = (id, v) => ({
id,
title: v?.title ?? v?.label ?? v?.name ?? null,
description: v?.description ?? v?.blurb ?? v?.tagline ?? v?.summary ?? null,
// `anxiety` is this repo's field for the one line a vertical page leads
// with, and it is the only prose in a VerticalEntry short enough to be a
// meta description.
description: v?.description ?? v?.blurb ?? v?.tagline ?? v?.summary ?? v?.anxiety ?? null,
});
/**
@@ -559,8 +573,14 @@ export function loadManifest() {
}
/** @type {Map<string, {runs: any[], extras: Record<string, any>}>} */
const byDemo = new Map();
const container =
raw && typeof raw === 'object' && !Array.isArray(raw) && raw.demos && typeof raw.demos === 'object' ? raw.demos : raw;
// Unwrap the two envelopes the shell's own reader accepts. `{runs: [...]}`
// has to be unwrapped BEFORE the record branch below, or "runs" is read as a
// demo slug and every real demo reports zero runs.
let container = raw;
if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
if (raw.demos && typeof raw.demos === 'object') container = raw.demos;
else if (Array.isArray(raw.runs)) container = raw.runs;
}
if (Array.isArray(container)) {
// A flat array of runs, each carrying its own `demo`/`slug`.
@@ -706,12 +726,32 @@ export function discoverRoutePatterns() {
return [...patterns];
}
const DEMO_PARAMS = /^(slug|demo|demoSlug)$/;
const VERTICAL_PARAMS = /^(vertical|verticalId|sector|category)$/;
/**
* Which set fills a pattern's one parameter.
*
* Decided by the STATIC segment in front of it, not by the parameter's name.
* This router calls both of them `:slug` — `demos/:slug` and `verticals/:slug`
* — so a name-based reader confidently prerenders `/verticals/wordle`, twelve
* 404s, and no vertical pages at all.
*/
function fillFor(pattern) {
const head = pattern.replace(/^\/+/, '').split('/')[0]?.toLowerCase() ?? '';
if (/^(demos?|d)$/.test(head)) return 'demo';
if (/^(verticals?|v|sectors?)$/.test(head)) return 'vertical';
const param = pattern.match(/:([A-Za-z0-9_]+)/)?.[1] ?? '';
if (/^(vertical|verticalId|sector|category)$/.test(param)) return 'vertical';
if (/^(slug|demo|demoSlug|id)$/.test(param)) return 'demo';
return null;
}
/**
* Expands the router's patterns against the real data into concrete paths.
*
* Child paths are taken as written. This router nests everything exactly one
* level under `/`, so a child's `path` is already the full path; a second level
* of nesting would need the parent prefix joined on, and this would silently
* emit the wrong routes rather than fail. If you nest deeper, fix this.
*
* @param {{metas: Map<string, any>, verticals: {id: string}[]}} data
* @returns {{routes: {path: string, kind: string, slug?: string, id?: string}[], errors: string[]}}
*/
@@ -749,18 +789,18 @@ export function expandRoutes(data) {
errors.push(`route pattern "${pattern}" has more than one parameter; prerender cannot expand it.`);
continue;
}
const param = params[0];
const fill = (value, record) => add(pattern.replace(/:[A-Za-z0-9_]+\??/, value), record);
if (VERTICAL_PARAMS.test(param)) {
if (!verticalIds.length) errors.push(`route "${pattern}" needs verticals, but none were readable from src/content/verticals.ts.`);
for (const id of verticalIds) fill(id, { kind: 'vertical', id });
} else if (DEMO_PARAMS.test(param) || param === 'id') {
const fill = fillFor(pattern);
const substitute = (value) => pattern.replace(/:[A-Za-z0-9_]+\??/, value);
if (fill === 'vertical') {
if (!verticalIds.length) errors.push(`route "${pattern}" needs verticals, but none were readable from ${rel(VERTICALS_FILE)}.`);
for (const id of verticalIds) add(substitute(id), { kind: 'vertical', id });
} else if (fill === 'demo') {
if (!demoSlugList.length) errors.push(`route "${pattern}" needs demos, but no demo meta was readable under src/demos/.`);
for (const slug of demoSlugList) fill(slug, { kind: 'demo', slug });
for (const slug of demoSlugList) add(substitute(slug), { kind: 'demo', slug });
} else {
errors.push(
`route pattern "${pattern}" uses parameter ":${param}", which prerender cannot fill. ` +
'Name it :slug (a demo) or :vertical (a vertical), or teach scripts/_lib.mjs about it.',
`route pattern "${pattern}" has a parameter prerender cannot fill. Put it under /demos/ or ` +
'/verticals/, or teach fillFor() in scripts/_lib.mjs about it.',
);
}
}
+597
View File
@@ -0,0 +1,597 @@
#!/usr/bin/env node
/**
* The demo contract validator.
*
* `src/lib/demo-kit/types.ts` says what a demo is. TypeScript enforces the
* shapes; this enforces everything a type cannot: that the directory name
* matches the slug, that a `spec` demo is a real published specification rather
* than a coming-soon card, that a `live` demo's traces exist on disk, that the
* shared shell has no idea any particular demo exists, and that the reward has
* something pulling against its objective.
*
* Thirteen numbered rules, each reported with the file to open. Run it with
* `pnpm check`.
*
* Most rules are checked by READING the TypeScript, not by running it — the
* demos import React, `?raw` Python and the Vite `@/` alias, none of which
* survive a bare `node` import. Rule 13 is the exception: it tries the adapter
* for real under tsx first, and says so in the output when it had to settle for
* reading the source.
*/
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { spawnSync } from 'node:child_process';
import {
DEMOS_DIR,
Report,
abs,
allDemoDirs,
demoFiles,
demoSlugs,
die,
exists,
findInDemo,
isUnresolved,
loadManifest,
loadMeta,
parseStringUnion,
read,
rel,
traceFile,
walk,
} from './_lib.mjs';
const report = new Report('check-demos');
/* ------------------------------------------------ the contract, read once */
const TYPES_FILE = abs('src', 'lib', 'demo-kit', 'types.ts');
if (!exists(TYPES_FILE)) die(`${rel(TYPES_FILE)} is missing. It is the contract; there is nothing to check against.`);
const typesSrc = read(TYPES_FILE);
// Parsed out of types.ts rather than restated here. A second copy of this list
// would drift, and it would drift silently in the direction of passing.
const VERTICALS = parseStringUnion(typesSrc, 'Vertical');
const STATUSES = parseStringUnion(typesSrc, 'DemoStatus');
const ROLES = ['objective', 'counterweight', 'gate'];
if (!VERTICALS) die(`could not parse the \`Vertical\` union out of ${rel(TYPES_FILE)}.`);
if (!STATUSES) die(`could not parse the \`DemoStatus\` union out of ${rel(TYPES_FILE)}.`);
/**
* Every icon name lucide-react actually exports, from its own type
* declarations. "Plausible PascalCase" would accept `Grid3X3`, which compiles
* to `undefined` and renders as a hole in the header.
*/
const LUCIDE_ICONS = (() => {
const dts = abs('node_modules', 'lucide-react', 'dist', 'lucide-react.d.ts');
if (!exists(dts)) return null;
const names = new Set();
for (const m of read(dts).matchAll(/declare const ([A-Za-z][A-Za-z0-9_]*)\s*:/g)) names.add(m[1]);
return names.size > 100 ? names : null;
})();
const nonEmptyString = (v) => typeof v === 'string' && v.trim().length > 0;
/** field -> validator returning `true` or the reason it is wrong. */
const META_FIELDS = {
slug: (v) => (/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(String(v)) ? true : 'must be a non-empty kebab-case string'),
title: (v) => (nonEmptyString(v) ? true : 'must be a non-empty string'),
tagline: (v) => (nonEmptyString(v) ? true : 'must be a non-empty string'),
vertical: (v) => (VERTICALS.includes(v) ? true : `must be one of the Vertical union: ${VERTICALS.join(', ')}`),
status: (v) => (STATUSES.includes(v) ? true : `must be one of the DemoStatus union: ${STATUSES.join(', ')}`),
order: (v) => (typeof v === 'number' && Number.isFinite(v) ? true : 'must be a finite number'),
icon: (v) => {
if (!nonEmptyString(v)) return 'must be a non-empty string';
if (!/^[A-Z][A-Za-z0-9]*$/.test(v)) return `"${v}" is not PascalCase, so it is not a lucide export name`;
if (LUCIDE_ICONS && !LUCIDE_ICONS.has(v)) return `lucide-react does not export "${v}" (check the exact casing at lucide.dev)`;
return true;
},
persona: (v) => (nonEmptyString(v) ? true : 'must be a non-empty string'),
rewardLine: (v) => (nonEmptyString(v) ? true : 'must be a non-empty string'),
ogImage: (v) => (nonEmptyString(v) && v.startsWith('/') ? true : "must be a site-absolute path, e.g. '/og/wordle.png'"),
};
/* --------------------------------------------------------- literal anchors */
const COMPONENT_ANCHORS = [/\bcomponents\s*:\s*/];
const ANATOMY_ANCHORS = [/(?:export\s+)?const\s+anatomy\s*(?::\s*[^=]+)?=\s*/, /\banatomy\s*:\s*/];
const PROVENANCE_ANCHORS = [
/(?:export\s+)?const\s+provenance\s*(?::\s*[^=]+)?=\s*/,
/\bprovenance\s*:\s*/,
];
/* ------------------------------------------------------------- the checks */
const slugs = demoSlugs();
if (slugs.length === 0) die(`no demos found under ${rel(DEMOS_DIR)}. A repo with no demos has nothing to validate.`);
const manifest = loadManifest();
for (const slug of slugs) {
const dir = path.join(DEMOS_DIR, slug);
const found = loadMeta(slug);
if ('error' in found) {
report.fail(found.file, 'rule 2 (DemoMeta present)', found.error);
continue;
}
const { meta, file: metaFile } = found;
/* -- 1. directory name is the slug ------------------------------------- */
report.check(
meta.slug === slug,
metaFile,
'rule 1 (slug is the directory name)',
`meta.slug is ${JSON.stringify(meta.slug)} but the directory is ${JSON.stringify(slug)}. ` +
'The registry, the route and the OG card are all keyed on this; rename one of them.',
);
/* -- 2. every required field, correctly typed ---------------------------- */
for (const [field, validate] of Object.entries(META_FIELDS)) {
if (!(field in meta)) {
report.fail(metaFile, 'rule 2 (DemoMeta complete)', `\`${field}\` is missing.`);
continue;
}
const verdict = validate(meta[field]);
report.check(verdict === true, metaFile, 'rule 2 (DemoMeta complete)', `\`${field}\`: ${verdict}`);
}
if (!LUCIDE_ICONS) {
report.staticOnly(`${slug}: lucide-react is not installed, so \`icon\` was only checked for PascalCase shape.`);
}
/* -- 4. the social card exists ------------------------------------------ */
if (nonEmptyString(meta.ogImage)) {
const card = abs('public', String(meta.ogImage).replace(/^\/+/, ''));
report.check(
exists(card),
metaFile,
'rule 4 (ogImage exists)',
`meta.ogImage points at ${meta.ogImage}, which is ${rel(card)} on disk, and that file does not exist. ` +
'Generate it with `node scripts/og.mjs` (x86 only — it needs Playwright chromium).',
);
}
/* -- 5. demo.tsx default-exports via defineDemo ------------------------- */
const demoFile = path.join(dir, 'demo.tsx');
if (!exists(demoFile)) {
report.fail(rel(demoFile), 'rule 5 (defineDemo default export)', 'demo.tsx does not exist.');
} else {
const src = read(demoFile);
report.check(
/export\s+default\s+defineDemo\s*(?:<[^>]*>)?\s*\(/.test(src),
rel(demoFile),
'rule 5 (defineDemo default export)',
'no `export default defineDemo(...)` found. The registry loads demos through defineDemo; ' +
'a bare object default export skips whatever the kit validates.',
);
report.check(
/\bdefineDemo\b[\s\S]*?from\s*'@\/lib\/demo-kit'/.test(src) || /from\s*'@\/lib\/demo-kit'[\s\S]*?\bdefineDemo\b/.test(src),
rel(demoFile),
'rule 5 (defineDemo default export)',
"defineDemo must be imported from '@/lib/demo-kit'.",
);
}
/* -- 6/10. the specification and the reward ----------------------------- */
const anatomy = findInDemo(slug, ANATOMY_ANCHORS, '{', 'anatomy', { lenient: true });
const provenance = findInDemo(slug, PROVENANCE_ANCHORS, '{', 'provenance', { lenient: true });
const components = findInDemo(slug, COMPONENT_ANCHORS, '[', 'reward.components', { lenient: true });
const specRule = 'rule 6 (complete specification)';
if (anatomy.error) {
report.fail(anatomy.file ?? rel(dir), specRule, anatomy.error);
} else {
for (const field of ['task', 'actions', 'grader', 'score']) {
report.check(
nonEmptyString(anatomy.value?.[field]),
anatomy.file,
specRule,
`anatomy.${field} is empty. A ${meta.status} demo publishes the four boxes in full; ` +
'an empty one reads as a placeholder and undoes the page it sits on.',
);
}
}
if (provenance.error) {
report.fail(provenance.file ?? rel(dir), specRule, provenance.error);
} else {
report.check(
nonEmptyString(provenance.value?.command),
provenance.file,
specRule,
'provenance.command is empty. The eval command is the reader\'s way to disprove the page; ' +
'it is the one field that must never be aspirational.',
);
}
const weightRule = 'rule 10 (reward weights)';
if (components.error) {
report.fail(components.file ?? rel(dir), weightRule, components.error);
} else if (!Array.isArray(components.value) || components.value.length === 0) {
report.fail(components.file, weightRule, 'reward.components is not a non-empty array.');
} else {
const list = components.value;
let sum = 0;
let readable = true;
list.forEach((component, i) => {
const where = `reward.components[${i}]${component?.key ? ` (${component.key})` : ''}`;
for (const field of ['key', 'label', 'description']) {
report.check(
nonEmptyString(component?.[field]),
components.file,
weightRule,
`${where}: \`${field}\` is empty.`,
);
}
if (typeof component?.weight !== 'number' || !Number.isFinite(component.weight)) {
readable = false;
report.fail(
components.file,
weightRule,
`${where}: \`weight\` is ${isUnresolved(component?.weight) ? 'an imported constant' : JSON.stringify(component?.weight)}, ` +
'not a literal number. Weights are read off the page beside the code, so they are written out here.',
);
} else {
sum += component.weight;
}
report.check(
ROLES.includes(component?.role),
components.file,
weightRule,
`${where}: \`role\` must be one of ${ROLES.join(', ')}.`,
);
});
if (readable) {
report.check(
Math.abs(sum - 1) <= 1e-9,
components.file,
weightRule,
`the weights sum to ${sum} and must sum to 1.0 (within 1e-9). ` +
'A reward whose weights do not sum to one is not the reward the page shows you tuning.',
);
}
report.check(
list.some((c) => c?.role === 'counterweight'),
components.file,
'rule 10 (counterweight required)',
'no component has role "counterweight". An objective with nothing pulling against it is a metric to ' +
'game, and the demo exists to show the opposite. If the second term is one every good policy also ' +
'scores 1.0 on, it is a gate — and the demo still needs a real counterweight.',
);
const counterweight = list.find((c) => c?.role === 'counterweight');
if (counterweight) {
report.check(
nonEmptyString(counterweight.description),
components.file,
specRule,
`the counterweight (${counterweight.key}) has no description. It is the half of the reward a reader ` +
'does not expect, so it is the half that must be spelled out.',
);
}
}
/* -- 7/11/12. recorded runs --------------------------------------------- */
if (meta.status === 'live') {
const runRule = 'rule 7 (live demo has runs)';
if (manifest.error) {
report.fail(rel(abs('public', 'traces', 'manifest.json')), runRule, manifest.error);
} else {
const entry = manifest.byDemo.get(slug);
const runs = entry?.runs ?? [];
if (runs.length === 0) {
report.fail(
rel(abs('public', 'traces', 'manifest.json')),
runRule,
`demo "${slug}" is status "live" but has no runs in the manifest. ` +
'A live demo asserts recorded evidence; without a run there is nothing to replay.',
);
} else {
const episodes = [];
for (const run of runs) {
const label = `run ${run?.id ?? '(no id)'}`;
if (!nonEmptyString(run?.path)) {
report.fail(rel(abs('public', 'traces', 'manifest.json')), runRule, `${label}: \`path\` is empty.`);
continue;
}
const file = traceFile(run.path);
if (!report.check(exists(file), rel(file), runRule, `${label} points at ${run.path}, which does not exist on disk.`)) {
continue;
}
try {
const raw = JSON.parse(read(file));
for (const ep of Array.isArray(raw) ? raw : Array.isArray(raw?.episodes) ? raw.episodes : [raw]) {
episodes.push({ run, file, episode: ep });
}
} catch (error) {
report.fail(rel(file), runRule, `not valid JSON: ${error.message}`);
}
/* -- 12. an intervened run must say what was done to it ---------- */
if (run?.kind === 'intervened') {
report.check(
nonEmptyString(run.intervention),
rel(abs('public', 'traces', 'manifest.json')),
'rule 12 (intervened runs declare the intervention)',
`${label} has kind "intervened" but no \`intervention\`. Without it a prompt change reads as a ` +
'training result by omission, which is the single most misleading thing this page could do.',
);
}
}
/* -- 11. a failure, or a solve rate the page renders -------------- */
const failedRun = episodes.find((e) => e.episode?.outcome === 'failed');
if (!failedRun && episodes.length) {
const rateSources = [entry?.extras ?? {}, ...runs];
const rate = rateSources
.map((s) => s?.solveRate ?? s?.solve_rate)
.find((v) => typeof v === 'number' && Number.isFinite(v));
const rendered = walk(abs('src'), (f) => /\.tsx?$/.test(f)).some((f) => /\bsolve[_R]?ate|solveRate|solve_rate/.test(read(f)));
report.check(
typeof rate === 'number' && rendered,
rel(abs('public', 'traces', 'manifest.json')),
'rule 11 (a clean sweep is reported, not hidden)',
`demo "${slug}" ships no run with outcome "failed". That is allowed only if the page states the ` +
'solve rate: add a numeric `solveRate` beside the runs in the manifest AND render it. ' +
`Right now solveRate is ${typeof rate === 'number' ? rate : 'absent'} and a renderer for it was ` +
`${rendered ? 'found' : 'not found'} under src/. Hunting for a losing seed to make the demo look ` +
'honest is the failure mode this rule exists to block.',
);
}
}
}
}
/* -- 13. every step announces itself ------------------------------------ */
checkAnnounce(slug, report, manifest);
}
/* ---------------------------------------- 8. the shell knows no demo names */
const SHELL_DIR = abs('src', 'components', 'demo');
for (const file of walk(SHELL_DIR, (f) => /\.(tsx?|css)$/.test(f))) {
const lines = read(file).split('\n');
for (const slug of allDemoDirs()) {
if (slug.startsWith('_')) continue;
// Word-boundary on both sides so a slug like `wordle` is not found inside
// an unrelated identifier, and so `wordle-five` matches as one token.
const needle = new RegExp(`(?<![\\w-])${slug.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?![\\w-])`);
const hit = lines.findIndex((line) => needle.test(line));
if (hit !== -1) {
report.fail(
`${rel(file)}:${hit + 1}`,
'rule 8 (the shell is generic)',
`mentions the demo slug "${slug}". Nothing under src/components/demo/ may know which demo it is ` +
'rendering. If the shell needs to special-case something, that is a missing slot on DemoModule or a ' +
'missing capability in demo-kit — fix it there and every future demo gets it too.',
);
} else {
report.passed += 1;
}
}
}
/* --------------------------------- 9. demos reach only for the kit's barrel */
const FORBIDDEN_IMPORTS = [
{
test: (spec) => /^@\/lib\/demo-kit\/.+/.test(spec),
why: "imports a file INSIDE demo-kit. Import '@/lib/demo-kit' — the barrel is the contract, and anything " +
'you can only get by reaching past it is either private or belongs in the barrel.',
},
{
test: (spec) => /^@\/components\/demo(\/|$)/.test(spec),
why: 'imports the shared shell. The shell renders demos; demos never render the shell. This is the ' +
'dependency that, once it exists, makes the shell impossible to change without opening every demo.',
},
];
for (const slug of allDemoDirs()) {
for (const file of demoFiles(slug)) {
for (const spec of importSpecifiers(read(file))) {
const normalised = normaliseSpecifier(spec, file);
for (const rule of FORBIDDEN_IMPORTS) {
if (rule.test(normalised)) {
report.fail(
rel(file),
'rule 9 (demos import only the demo-kit barrel)',
`\`${spec}\`${normalised === spec ? '' : ` (resolves to ${normalised})`} ${rule.why}`,
);
} else {
report.passed += 1;
}
}
}
}
}
report.finish();
/* ------------------------------------------------------------- helpers */
/** Every module specifier a file imports, static, dynamic or side-effect. */
function importSpecifiers(src) {
const out = new Set();
for (const m of src.matchAll(/\bfrom\s*['"]([^'"]+)['"]/g)) out.add(m[1]);
for (const m of src.matchAll(/\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g)) out.add(m[1]);
for (const m of src.matchAll(/^\s*import\s+['"]([^'"]+)['"]/gm)) out.add(m[1]);
return [...out];
}
/**
* Rewrites a relative specifier into its `@/` form.
*
* Without this, `../../components/demo/Player` walks straight past a lint that
* only looks at the alias — and it is the form an editor's auto-import
* produces, so it is the form the violation actually arrives in.
*/
function normaliseSpecifier(spec, fromFile) {
if (!spec.startsWith('.')) return spec;
const resolved = path.resolve(path.dirname(fromFile), spec);
const inSrc = path.relative(abs('src'), resolved);
if (inSrc.startsWith('..')) return spec;
return `@/${inSrc.split(path.sep).join('/')}`.replace(/\.(tsx?|jsx?)$/, '');
}
/**
* Rule 13: every `DemoStep` an adapter produces sets a non-empty `announce`.
*
* Reduced motion clamps the tile flip to nothing, so for a screen-reader user
* the announcement IS the result. A missing one is not a degraded experience,
* it is a blank page that claims to be showing you something.
*
* Tried dynamically first, under tsx, against the demo's own recorded traces —
* a static check cannot see through a helper that builds the step object. When
* the module will not load in bare Node (a `?raw` import, a Vite-only alias,
* JSX pulling in something browser-shaped) it falls back to reading the source
* and SAYS SO in the report rather than quietly downgrading.
*/
function checkAnnounce(slug, report, manifest) {
const rule = 'rule 13 (every step announces itself)';
const dir = path.join(DEMOS_DIR, slug);
const candidates = ['adapt.ts', 'adapter.ts', 'adapters.ts', 'adapt.tsx', 'demo.tsx']
.map((n) => path.join(dir, n))
.filter((p) => exists(p) && /\badapt\b\s*[:=(]|function\s+adapt\b/.test(read(p)));
if (candidates.length === 0) {
report.fail(
rel(dir),
rule,
'no `adapt` implementation found. DemoModule.adapt is required; it is what turns a recorded episode ' +
'into the steps the player renders.',
);
return;
}
const traces = (manifest.byDemo.get(slug)?.runs ?? [])
.map((run) => (run?.path ? traceFile(run.path) : null))
.filter((p) => p && exists(p));
if (traces.length > 0) {
for (const module of candidates) {
const result = probeAdapter(module, traces);
if (result.loaded) {
if (result.ok) {
report.passed += 1;
} else {
for (const problem of result.problems) report.fail(rel(module), rule, problem);
}
return;
}
}
}
// Static fallback. It cannot prove `announce` is set on every step, only that
// the adapter sets it at all and never sets it to nothing.
const module = candidates[0];
const src = read(module);
const assigns = /\bannounce\s*:/.test(src);
report.check(
assigns,
rel(module),
rule,
'the adapter never assigns `announce`. Every DemoStep must carry one — with motion reduced, it is the ' +
'only thing that reports the result.',
);
if (assigns) {
const empty = /\bannounce\s*:\s*(?:''|""|``|null|undefined)\s*[,}\n]/.test(src);
report.check(!empty, rel(module), rule, 'the adapter assigns an empty `announce` somewhere. An empty announcement is a missing one.');
}
report.staticOnly(
`${slug}: rule 13 was checked by reading ${rel(module)}, not by running the adapter` +
`${traces.length === 0 ? ' (no recorded traces to feed it)' : ' (the module would not load under tsx in bare Node)'}.`,
);
}
/** Runs one adapter over real episodes under tsx. Never throws. */
function probeAdapter(modulePath, traces) {
const tsx = abs('node_modules', '.bin', 'tsx');
if (!exists(tsx)) return { loaded: false, reason: 'tsx is not installed' };
const probe = path.join(fs.mkdtempSync(path.join(os.tmpdir(), 'pig-adapt-')), 'probe.mts');
fs.writeFileSync(probe, adapterProbeSource(), 'utf8');
try {
const run = spawnSync(process.execPath, [tsx, probe, modulePath, ...traces], {
cwd: abs('.'),
encoding: 'utf8',
timeout: 30_000,
// tsx resolves the `@/` alias from a tsconfig, and the probe lives in
// a temp dir where it would never find ours.
env: { ...process.env, TSX_TSCONFIG_PATH: abs('tsconfig.json') },
});
if (run.status !== 0 || !run.stdout) return { loaded: false, reason: (run.stderr || '').trim().split('\n').slice(-1)[0] };
const line = run.stdout.trim().split('\n').pop();
const parsed = JSON.parse(line);
return parsed.loaded === false ? { loaded: false, reason: parsed.reason } : { loaded: true, ...parsed };
} catch {
return { loaded: false, reason: 'the probe did not produce parseable output' };
} finally {
fs.rmSync(path.dirname(probe), { recursive: true, force: true });
}
}
/**
* The probe, written to a temp file and executed by tsx.
*
* It lives here as a string rather than as a checked-in `.mts` because it is an
* implementation detail of this script, and a stray TypeScript file in
* `scripts/` would get swept into the typecheck it is deliberately outside of.
*/
function adapterProbeSource() {
return `
import { readFileSync } from 'node:fs';
const [modulePath, ...traces] = process.argv.slice(2);
const say = (o: unknown) => console.log(JSON.stringify(o));
let mod: any;
try {
mod = await import(modulePath);
} catch (error: any) {
say({ loaded: false, reason: String(error?.message ?? error).split('\\n')[0] });
process.exit(0);
}
const adapt =
typeof mod.adapt === 'function' ? mod.adapt :
typeof mod.default?.adapt === 'function' ? mod.default.adapt :
typeof mod.default === 'function' ? mod.default : null;
if (!adapt) {
say({ loaded: false, reason: 'no callable \\\`adapt\\\` export' });
process.exit(0);
}
const problems: string[] = [];
let steps = 0;
for (const trace of traces) {
const raw = JSON.parse(readFileSync(trace, 'utf8'));
const episodes = Array.isArray(raw) ? raw : Array.isArray(raw?.episodes) ? raw.episodes : [raw];
for (const episode of episodes) {
let produced: any;
try {
produced = adapt(episode);
} catch (error: any) {
problems.push(trace + ': adapt() threw ' + String(error?.message ?? error).split('\\n')[0]);
continue;
}
if (!Array.isArray(produced)) {
problems.push(trace + ': adapt() returned ' + typeof produced + ', not an array of DemoStep');
continue;
}
produced.forEach((step: any, i: number) => {
steps += 1;
if (typeof step?.announce !== 'string' || step.announce.trim() === '') {
problems.push(trace + ': step ' + i + ' has an empty \\\`announce\\\`');
}
});
}
}
say({ loaded: true, ok: problems.length === 0, problems: problems.slice(0, 10), steps });
`;
}
+181
View File
@@ -0,0 +1,181 @@
#!/usr/bin/env node
/**
* The attribution checker.
*
* This repo is public and Apache-2.0, and almost none of what makes the demo
* work is ours: the primitives are shadcn/ui over Radix, the answer list is
* Wordnik's filtered through SCOWL, the typeface is Manrope under the SIL Open
* Font License, the icons are lucide, the charts are recharts. Every one of
* those licences is permissive, and every one of them requires attribution.
*
* So `NOTICE` is not paperwork, it is a build artifact with a test. This script
* is that test: it fails if NOTICE is missing any source we actually ship, and
* it fails if a source is named without its licence beside it — because
* "uses Manrope" without "OFL-1.1" is not attribution, it is a mention.
*
* The OFL additionally requires that the licence text travel WITH the font, so
* `public/fonts/OFL.txt` is checked for separately. Shipping the .woff2 out of
* node_modules and leaving the licence behind is the single easiest way to
* violate the one licence on this list that has teeth.
*/
import fs from 'node:fs';
import { Report, abs, exists, read, rel } from './_lib.mjs';
const report = new Report('check-licenses');
const NOTICE = abs('NOTICE');
const OFL = abs('public', 'fonts', 'OFL.txt');
/**
* Every third-party source this site ships, and the licence each one must be
* named with. `used` is a cheap sanity check in the other direction: an entry
* whose artifact has left the repo is stale attribution, and stale attribution
* quietly becomes wrong attribution.
*/
const SOURCES = [
{
label: 'shadcn/ui',
match: /shadcn/i,
licence: /\bMIT\b/,
why: 'the UI primitives in src/components/ui are shadcn/ui components, hand-copied into this repo.',
used: () => exists(abs('components.json')),
},
{
label: 'Radix UI',
match: /radix/i,
licence: /\bMIT\b/,
why: 'every primitive with behaviour — dialog, tabs, tooltip, slider — is Radix underneath.',
used: () => exists(abs('node_modules', '@radix-ui')),
},
{
label: 'the Wordnik word list',
match: /wordnik/i,
licence: /\bMIT\b/,
why: 'the guess list is derived from Wordnik.',
used: () => hasWordFile(/wordnik/i),
},
{
label: 'SCOWL',
match: /\bSCOWL\b/i,
licence: /permissive|attribution|BSD|Kevin\s+Atkinson/i,
why: 'the answer list is filtered through SCOWL.',
used: () => hasWordFile(/scowl/i),
},
{
label: 'Manrope',
match: /manrope/i,
// The OFL is version-specific and the version matters: OFL-1.1 is the one
// Manrope ships under, and it is the one whose terms are quoted in OFL.txt.
licence: /OFL[-\s]?1\.1|SIL\s+Open\s+Font\s+License/i,
why: 'Manrope is the typeface, embedded as a variable woff2.',
used: () => exists(abs('node_modules', '@fontsource-variable', 'manrope')),
},
{
label: 'lucide',
match: /lucide/i,
licence: /\bISC\b/,
why: 'every icon on the site is a lucide icon.',
used: () => exists(abs('node_modules', 'lucide-react')),
},
{
label: 'recharts',
match: /recharts/i,
licence: /\bMIT\b/,
why: 'the reward and metric charts are recharts.',
used: () => exists(abs('node_modules', 'recharts')),
},
{
label: "PIG's own token layer",
match: /\bPIG\b|Prime\s+Intellect\s+Growth/i,
licence: /Apache[-\s]?2\.0/i,
why: 'the colour and motion tokens in src/index.css come from PIG and ship under Apache-2.0.',
used: () => exists(abs('src', 'index.css')),
},
];
/** True when the words directory still carries a file from this source. */
function hasWordFile(pattern) {
const dir = abs('envs', 'wordle_five', 'words');
return exists(dir) && fs.readdirSync(dir).some((name) => pattern.test(name));
}
if (!exists(NOTICE)) {
report.fail(
'NOTICE',
'NOTICE exists',
'there is no NOTICE file at the repo root. Every source below is shipped by this site and each of ' +
`their licences requires attribution: ${SOURCES.map((s) => s.label).join(', ')}.`,
);
report.finish();
}
const notice = read(NOTICE);
const lines = notice.split('\n');
for (const source of SOURCES) {
// Every line that names the source, not just the first. "PIG-Demo" in the
// copyright header matches the PIG entry three dozen lines before its actual
// attribution block, and a first-match-wins reader fails on a NOTICE that is
// completely correct.
const hits = [];
lines.forEach((line, i) => {
if (source.match.test(line)) hits.push(i);
});
if (hits.length === 0) {
report.fail(
'NOTICE',
'attribution complete',
`${source.label} is not named. It is shipped by this site — ${source.why} — and its licence requires ` +
'attribution. Add it with its licence identifier.',
);
continue;
}
report.passed += 1;
// The licence has to sit with the name, not merely somewhere in the file:
// a NOTICE that says "MIT" once at the top and lists nine projects under it
// is attributing all nine to whichever licence happens to be first.
const near = (i) => lines.slice(Math.max(0, i - 2), i + 6).join('\n');
report.check(
hits.some((i) => source.licence.test(near(i))),
`NOTICE:${hits[0] + 1}`,
'attribution names the licence',
`${source.label} is named on line(s) ${hits.map((i) => i + 1).join(', ')} but no matching licence appears ` +
`beside any of them (looking for ${source.licence}). A name without a licence is a mention, not an ` +
'attribution.',
);
if (!source.used()) {
report.warn(
`NOTICE names ${source.label}, but nothing in the repo appears to use it any more. ` +
'Stale attribution is how a NOTICE stops being trustworthy.',
);
}
}
/* -------------------------------------------------------- the OFL's own rule */
report.check(
exists(OFL),
rel(OFL),
'OFL text ships with the font',
'Manrope is under SIL OFL-1.1, which requires the licence text to travel with the font files. ' +
'Copy node_modules/@fontsource-variable/manrope/LICENSE to public/fonts/OFL.txt. ' +
'Shipping the woff2 and leaving the licence behind is the one violation on this list with teeth.',
);
if (exists(OFL)) {
const text = read(OFL);
report.check(
/SIL OPEN FONT LICENSE/i.test(text) && /Version 1\.1/i.test(text),
rel(OFL),
'OFL text ships with the font',
'the file exists but does not look like the SIL Open Font License 1.1. It must be the licence text ' +
'itself, not a pointer to it.',
);
}
report.finish();
+196
View File
@@ -0,0 +1,196 @@
#!/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 (lines ${open + 2}-${close}). The markers are exclusive, ` +
'so a pair on adjacent lines quotes nothing.',
);
}
}
report.finish();