Files
zeekay 70349b34c7 beta: a post about one topic, with that topic's page and card
The share card offers Hanzo, the products and the betas the member asked
for, the rest under More by group. A topic picks the posts Shuffle offers,
the link (/beta/for/<topic>, tagged utm_content) and the card it unfurls
to: that page carries the topic's title, line and card, a canonical naming
itself and the one noindex (lib/publish SHARES), and sends a person on to
/beta with the query kept, so ref and the campaign land as before.

lib/hanzo/topics is the list, its words the site's and BETAS's own.
generate-og draws a card per topic from it, palette-quantized with sharp:
49 cards, 2.9 MB. The cards gate reads every card's rendered words.

Claude-Session: https://claude.ai/code/session_017vrVus6PPBgP5YRzKV1iqW
2026-10-26 12:03:39 -07:00

106 lines
4.4 KiB
TypeScript

import { test, expect } from '@playwright/test'
import { createHash } from 'node:crypto'
import { readFileSync } from 'node:fs'
import { getOgImageUrl, OG_IMAGE, OG_IMAGES_BY_PATH } from '../../lib/constants/og'
import { BACKING, scan } from '../gates/backing'
import { CARDS, MANIFEST, file, renderHtml } from '../../scripts/generate-og.mjs'
import { TOPICS, page } from '../../lib/hanzo/topics'
/**
* A SHARE CARD SAYS ONLY WHAT CAN BE CHECKED.
*
* Every card is drawn by scripts/generate-og.mjs, its words and its visuals
* alike, so this reads the whole generator. A card states what a page, the
* plan catalog (GET /v1/billing/plans), the model catalog (GET /v1/models) or
* /trust already states; it never names what backs Enso, Zen or Kai. A redrawn
* card takes a new address, because a share preview is cached by its address.
* Each image is held to the words it was drawn from by the hashes the
* generator records in scripts/og.json when it draws.
*/
const source = readFileSync(new URL('../../scripts/generate-og.mjs', import.meta.url), 'utf8')
/** Claims no page or catalog backs, and the made-up model names and scores the cards once drew. */
const UNBACKED = [
/zero retention/i,
/enterprise[- ]grade/i,
/SOC ?2/i,
/HIPAA/i,
/ISO ?27001/i,
/99\.9/,
/AES-256/i,
/in seconds/i,
/passkey/i,
/latency/i,
/\b\d+ ?ms\b/i,
/hidden fees/i,
/unlimited scale/i,
/peer[- ]reviewed/i,
/reproducible science/i,
/state[- ]of[- ]the[- ]art/i,
/most popular/i,
/\b\d+\+ (?:open |frontier )?models/i,
/zen[- ]?5[- ]ultra/i,
/enso[- ]?3/i,
/\b\d+\.\d+%/,
/claude|anthropic|deepseek|gemini|llama|mistral|openrouter/i,
/silicon/i,
/competitive equity/i,
/frontier hardware/i,
/decentralized/i,
/foundation for applied ai/i,
]
/** The words each card is drawn from: a share topic's come from lib/hanzo/topics and BETAS, not from the generator's text. */
const words = CARDS.map((card) => ({ name: card.name, html: renderHtml(card) }))
test('no card claims what nothing backs', () => {
for (const claim of UNBACKED) expect(source, String(claim)).not.toMatch(claim)
for (const { name, html } of words) for (const claim of UNBACKED) expect(html, `${name}: ${claim}`).not.toMatch(claim)
})
test('no card says what backs Enso, Zen or Kai, or names a lab that could', () => {
expect(scan(source, '.js')).toEqual([])
expect(source.match(BACKING)?.[0]).toBeUndefined()
for (const { name, html } of words) {
expect(scan(html, '.html'), name).toEqual([])
expect(html.match(BACKING)?.[0], name).toBeUndefined()
}
})
const sha = (bytes: string | Buffer) => createHash('sha256').update(bytes).digest('hex')
test('every image is the one its words drew', () => {
const drawn = JSON.parse(readFileSync(MANIFEST, 'utf8')) as Record<string, { html: string; png: string }>
expect(Object.keys(drawn).sort()).toEqual(CARDS.map((c) => c.name).sort())
for (const card of CARDS) {
const redraw = `node scripts/generate-og.mjs ${card.name}`
expect(sha(renderHtml(card)), `${card.name}'s words changed after it was drawn: ${redraw}`).toBe(drawn[card.name].html)
expect(sha(readFileSync(new URL(`../../${file(card.name)}`, import.meta.url))), `${file(card.name)} is not the image drawn: ${redraw}`).toBe(drawn[card.name].png)
}
})
test('every card takes the same new address', () => {
const version = OG_IMAGE.url.match(/\?v=(\d+)$/)?.[1]
expect(Number(version)).toBeGreaterThanOrEqual(3)
for (const [path, url] of Object.entries(OG_IMAGES_BY_PATH)) expect(url, path).toMatch(new RegExp(`^/og/[a-z0-9-]+\\.png\\?v=${version}$`))
// /contact has no card of its own, so it shares the default one.
expect(getOgImageUrl('/contact')).toBe(OG_IMAGE.url)
})
test('every card the pages name is one the generator draws', () => {
const drawn = new Set(CARDS.map((c) => `/${file(c.name).replace(/^public\//, '')}`))
for (const url of [OG_IMAGE.url, ...Object.values(OG_IMAGES_BY_PATH)]) expect(drawn.has(url.replace(/\?.*$/, '')), `${url} is drawn by scripts/generate-og.mjs`).toBe(true)
})
test('every share topic unfurls to a card of its own', () => {
const seen = new Map<string, string>()
for (const t of TOPICS) {
const url = getOgImageUrl(page(t.id))
expect(url, `${t.id} has no card of its own`).not.toBe(OG_IMAGE.url)
expect(seen.get(url), `${t.id} and ${seen.get(url)} share ${url}`).toBeUndefined()
seen.set(url, t.id)
}
expect(seen.size).toBe(TOPICS.length)
})