feat: build steps 7a-10 — the site is complete and reviewable at 22 pages

Steps 7a through 10 as one authorised run. Nothing deployed (D11).

7a  Lighthouse returns as `lighthouse@13.4.1` + `chrome-launcher`, NOT
    `@lhci/cli`. AGENTS.md §7's advisory attribution was wrong: the carriers
    were @lhci/cli's own `tmp` and @puppeteer/browsers' `extract-zip`, not
    Lighthouse, which audits clean. A deliberate deviation from R11's literal
    trigger, recorded with what it costs. Local gate; CI has no Chrome.

7b  OG card generator (satori + sharp) discharges R15 — 20 typed cards plus
    per-article cards; the portrait stays on / and /about/ by Q40. Insights
    plumbing: ArticleCard, Prose, the index, the article route, articleGraph,
    and /'s section 7. Card copy is constrained structurally because text in a
    JPEG cannot be grepped by check:claims: every headline IS its page's <h1>,
    enforced by `npm run og:proof`.

7c  Five drafted launch articles, draft: true / reviewedByPouya: false. An
    independent compliance audit returned 76 findings and 57 unsourced
    assertions; all blocking and should-fix applied.

8   /contact/, the intake form, and backend/intake/ (undeployed). Plain HTML
    POST to a same-origin /api/intake with a 303 redirect, so the form works
    with zero JavaScript. docs/05 records three deliberate deviations.

9   /fees/ on Q59's ruling — overtime runs from the session cap, and the
    reservation point ships adjacent to the rate. One-page PDF bio discharges
    R16; /bio/ is its source, so the circulated artefact stays inside the
    review apparatus.

10  /legal/privacy/ and /legal/terms/, written to the backend as built. Three
    of the policy's statements are derived and cannot drift.

Also: /about/'s inverse credentials band (approved at step 6); Q59 closed;
R15 and R16 discharged; and a fix to shipped copy — /practice/energy/ asserted
the absence of a regulation the source extract says must not be asserted.

Review: adversarial-reviewer, two rounds (D20/D19). Round 1 returned 16
findings including two blocking — an invisible ghost button on /fees/ at
1.00:1 that Lighthouse scored 100, and a privacy policy that named one data
processor when there are two. All 16 acted on.

Lighthouse, 22 pages, mobile: performance 99-100, accessibility 100,
best practices 100, SEO 100 on every indexable page, CLS 0.000.

AGENTS.md entry (ah) has the detail, including four of my own verification
commands that were wrong and what each of them nearly caused.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Md3GndFqWPzK78xAoebsg5
This commit is contained in:
Pouya Lajevardi
2026-08-31 10:56:54 -04:00
co-authored by Claude Opus 5
parent 6cfe69033f
commit 210bc25a26
53 changed files with 8589 additions and 177 deletions
+325
View File
@@ -0,0 +1,325 @@
#!/usr/bin/env node
/**
* Proves the Open Graph cards, two ways. `npm run og:proof`, after a build.
*
* WHY THIS EXISTS AT ALL. `AGENTS.md` R15: *"Nobody on this project will ever
* see the defect. A link preview is rendered by LinkedIn, Slack and Teams for a
* reader who is not us."* Generating the cards does not fix that — it moves the
* invisible thing from "wrong image" to "wrong image, generated". So the two
* failures that would stay invisible are checked mechanically:
*
* 1. **Every page's `og:image` resolves to a file that exists in `dist/`.** A
* 404 preview image renders as a blank card, and nothing else in this repo
* would notice. Checked by reading the built HTML, not the source.
*
* 2. **Every card's headline and eyebrow are its page's own `<h1>` and first
* `.eyebrow`, character for character.** This is the compliance half. Text
* baked into a JPEG is text `npm run check:claims` cannot grep, and under D20
* that script is the only per-step claims control there is — so a card must
* never carry a claim its page does not already make in auditable HTML. The
* check enforces that structurally rather than trusting an author to
* remember it, and it fails in both directions: editing the page without the
* registry, or the registry without the page.
*
* It also catches the quiet one: a straight apostrophe in the registry
* against the typographic apostrophe the page renders. Found exactly that on
* the first run, on `/practice/insurance/`.
*
* It reads `src/data/og-cards.ts` DIRECTLY — Node strips the types — so there is
* no second list of cards to keep in step with the first.
*
* Optional: `npm run og:proof -- --sheet` writes a contact sheet of every card
* to `dist/og-proof.jpg` so the set can be looked at in one go. Not part of the
* check; a human still has to look.
*/
import { readdir, readFile, stat, writeFile } from 'node:fs/promises';
import { join, relative, sep } from 'node:path';
import sharp from 'sharp';
import {
OG_CARDS,
PORTRAIT_PAGES,
articleCard,
ogCardPath,
} from '../src/data/og-cards.ts';
const ROOT = process.cwd();
const DIST = join(ROOT, 'dist');
const SITE = 'https://adr.smlcompany.ca';
const strip = (html) =>
html
.replace(/<[^>]+>/g, '')
.replace(/&amp;/g, '&')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&quot;/g, '"')
.replace(/&#39;/g, "'")
.replace(/&nbsp;/g, ' ')
.replace(/\s+/g, ' ')
.trim();
async function pages(dir = DIST) {
const out = [];
for (const entry of await readdir(dir, { withFileTypes: true })) {
const full = join(dir, entry.name);
if (entry.isDirectory()) out.push(...(await pages(full)));
else if (entry.name === 'index.html') {
const rel = relative(DIST, dir).split(sep).filter(Boolean).join('/');
out.push({ path: rel ? `/${rel}/` : '/', file: full });
}
}
return out.sort((a, b) => a.path.localeCompare(b.path));
}
const problems = [];
const fail = (msg) => problems.push(msg);
try {
await stat(join(DIST, 'index.html'));
} catch {
console.error('dist/ is missing or empty. Run `npm run build` first.');
process.exit(2);
}
const built = await pages();
const seenCards = new Set();
let checkedHeadlines = 0;
let checkedArticles = 0;
/**
* An article's expected card, from the SAME `articleCard()` the endpoint calls,
* given the title in that article's own frontmatter. Read from the `.mdx` rather
* than from the built page, so the comparison has two independent sides: what
* the article says its title is, and what the route rendered as the `<h1>`.
*/
async function expectedArticleCard(path) {
const slug = path.replace(/^\/insights\/|\/$/g, '');
for (const ext of ['mdx', 'md']) {
try {
const src = await readFile(
join(ROOT, 'src', 'content', 'insights', `${slug}.${ext}`),
'utf8',
);
const m = /^title:\s*(.*)$/m.exec(src);
if (!m) break;
let title = m[1].trim();
// YAML scalar: strip one layer of quoting and unescape a doubled single
// quote, which is how YAML writes a literal apostrophe inside '…'.
if (
(title.startsWith("'") && title.endsWith("'")) ||
(title.startsWith('"') && title.endsWith('"'))
) {
title = title.slice(1, -1);
}
title = title.replace(/''/g, "'");
return articleCard(title);
} catch {
/* try the next extension */
}
}
return null;
}
for (const { path, file } of built) {
const html = await readFile(file, 'utf8');
// ---- 1. og:image exists -------------------------------------------------
const og = /<meta property="og:image" content="([^"]+)"/.exec(html);
if (!og) {
fail(`${path}: no og:image meta tag at all`);
continue;
}
const url = og[1];
if (!url.startsWith(SITE + '/')) {
fail(`${path}: og:image is not an absolute URL on ${SITE}${url}`);
continue;
}
const assetPath = url.slice(SITE.length);
try {
await stat(join(DIST, assetPath));
} catch {
fail(`${path}: og:image points at ${assetPath}, which is not in dist/`);
continue;
}
seenCards.add(assetPath);
// ---- 2. card copy is the page's own copy --------------------------------
const isPortrait = PORTRAIT_PAGES.includes(path);
const card = OG_CARDS[path];
if (isPortrait) {
if (card) fail(`${path}: in PORTRAIT_PAGES and in OG_CARDS — pick one`);
if (assetPath.startsWith('/og/')) {
fail(`${path}: is a portrait page but its og:image is a generated card`);
}
continue;
}
const expected = ogCardPath(path);
if (assetPath !== expected) {
fail(`${path}: og:image is ${assetPath}, expected ${expected}`);
}
/**
* ⚠️ AN ARTICLE IS CHECKED THE SAME WAY AS A REGISTRY PAGE, AND UNTIL
* 2026-08-31 IT WAS NOT CHECKED AT ALL.
*
* The first version of this script matched an article's card FILENAME and then
* `continue`d — skipping the headline and eyebrow comparisons entirely. So the
* one surface `check:claims` cannot reach was also the one surface this script
* did not compare, which is the opposite of what its own header claims and what
* `docs/04` says it enforces.
*
* `adversarial-reviewer` proved it rather than arguing it: with
* `headline: 'DELIBERATELY WRONG CARD TEXT — probe'` set in the endpoint and one
* article published, the card rendered that sentence in 68px Instrument Serif
* and this script printed `OK — every og:image resolves, and no card asserts
* anything its page does not`, exit 0. **`checkedHeadlines` stayed pinned at the
* registry size** no matter how many articles published — a coverage number
* that reads like completeness and falls further behind as the site grows,
* which is exactly the uniform-pass shape `CLAUDE.md` warns is the dangerous
* half.
*
* An article has no registry entry by design — its card comes from the
* collection — so the expectation comes from the page instead: the endpoint
* sets an article card's headline to `entry.data.title`, which is also the
* page's `<h1>`. Comparing the card's source of truth against the rendered
* `<h1>` is therefore the same check, and the eyebrow is the literal the
* endpoint sets.
*/
const isArticle = /^\/insights\/[^/]+\/$/.test(path);
if (!card && !isArticle) {
fail(`${path}: built, not a portrait page, and has no OG_CARDS entry`);
continue;
}
let expected_card = card;
if (!expected_card) {
expected_card = await expectedArticleCard(path);
if (!expected_card) {
fail(
`${path}: could not read a \`title:\` from this article's own .mdx, so ` +
'its card cannot be compared against anything. That is a failure, not ' +
'a skip — an unchecked card is the one surface check:claims cannot see.',
);
continue;
}
}
const expectedEyebrow = expected_card.eyebrow;
const h1 = /<h1[^>]*>([\s\S]*?)<\/h1>/.exec(html);
if (!h1) {
fail(`${path}: no <h1> to compare the card headline against`);
} else {
const text = strip(h1[1]);
const expectedHeadline = expected_card.headline;
if (text !== expectedHeadline) {
fail(
`${path}: card headline is not the page's <h1>.\n` +
` <h1>: ${JSON.stringify(text)}\n` +
` card: ${JSON.stringify(expectedHeadline)}`,
);
} else {
checkedHeadlines += 1;
if (isArticle) checkedArticles += 1;
}
}
const eyebrow = /<p class="eyebrow"[^>]*>([\s\S]*?)<\/p>/.exec(html);
if (!eyebrow) {
fail(`${path}: no .eyebrow to compare the card eyebrow against`);
} else {
const text = strip(eyebrow[1]);
if (text !== expectedEyebrow) {
fail(
`${path}: card eyebrow is not the page's first .eyebrow.\n` +
` page: ${JSON.stringify(text)}\n` +
` card: ${JSON.stringify(expectedEyebrow)}`,
);
}
}
}
/**
* ⚠️ AND THE COVERAGE IS ASSERTED, NOT REPORTED. Printing "20 headlines matched"
* beside a growing site is how the gap above stayed invisible: the number went up
* and never went up ENOUGH, and nothing said so. Every built page except the
* portrait pages carries a generated card, so the count must equal that or a page
* was silently skipped.
*/
const shouldCheck = built.filter(
({ path }) => !PORTRAIT_PAGES.includes(path),
).length;
if (checkedHeadlines !== shouldCheck) {
fail(
`only ${checkedHeadlines} of ${shouldCheck} non-portrait pages had their ` +
'card headline compared against their <h1>. A page was skipped, which is ' +
'the failure this assertion exists to make loud.',
);
}
// ---- 3. no card generated for a page that does not exist -------------------
// A stray card is not a shipped defect, but it is the signature of a page that
// was renamed or removed and a registry entry that was not — which the next
// person reads as "the card exists, so the page must".
let strays = [];
try {
const files = await readdir(join(DIST, 'og'));
strays = files
.filter((f) => f.endsWith('.jpg'))
.map((f) => `/og/${f}`)
.filter((p) => !seenCards.has(p));
} catch {
fail('dist/og/ does not exist — no cards were generated');
}
for (const s of strays) {
fail(`${s}: generated, but no built page references it`);
}
// ---- optional contact sheet ----------------------------------------------
if (process.argv.includes('--sheet')) {
const files = (await readdir(join(DIST, 'og')))
.filter((f) => f.endsWith('.jpg'))
.sort();
const COLS = 3;
const W = 400;
const H = 210;
const rows = Math.ceil(files.length / COLS);
const tiles = await Promise.all(
files.map(async (f, i) => ({
input: await sharp(join(DIST, 'og', f))
.resize(W, H)
.toBuffer(),
left: (i % COLS) * W,
top: Math.floor(i / COLS) * H,
})),
);
const sheet = await sharp({
create: {
width: COLS * W,
height: rows * H,
channels: 3,
background: '#ffffff',
},
})
.composite(tiles)
.jpeg({ quality: 82 })
.toBuffer();
await writeFile(join(DIST, 'og-proof.jpg'), sheet);
console.log(
`contact sheet: dist/og-proof.jpg — ${files.length} cards, ${COLS}x${rows}`,
);
}
console.log(
`og:proof — ${built.length} built pages, ${seenCards.size} distinct og:image ` +
`targets, ${checkedHeadlines} card headlines matched their page <h1> ` +
`(${checkedArticles} of them articles).`,
);
if (problems.length) {
console.error(`\nOG CARD PROBLEMS — ${problems.length}:`);
for (const p of problems) console.error(` - ${p}`);
process.exit(1);
}
console.log(
'OK — every og:image resolves, and no card asserts anything its page does not.',
);