Files
zeekay d18bf9e445 models: ours wear Hanzo's H, Zen wears Zoo's mark, and Zen credits Zoo Labs
On /models Enso, Kai and the creative models draw Hanzo's H, and every
Zen model (the catalogue's, Zen 7, the Enso-vs-Zen card) draws Zoo Labs
Foundation's venn in the page's ink. The Zen band says its weights are
made with Zoo Labs Foundation (zoo.ngo) and published at
huggingface.co/zenlm. The marks gate holds Enso and Kai to the H.

Claude-Session: https://claude.ai/code/session_01BLLMyBFSvAfCt7ZGqYuiEz
2026-10-22 00:53:03 -07:00

86 lines
4.0 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test, expect } from '@playwright/test'
import { serveExport } from './export'
/**
* A family wears the mark of the lab that MAKES it.
*
* Enso and Kai are ours and wear Hanzo's H on /models (owner, 2026-10-22), as
* the landing's boards already did. Zen is Zoo Labs Foundation's — we serve it, we did not train
* it — and /models says so in its own hero copy. The marks have twice said
* otherwise, and both times a green build and a green typecheck agreed:
*
* The Hanzo H on everything. Our gateway namespace holds every family under
* `hanzo`, so reading the lab first drew our H on Zen, on GPT and on Claude.
* Fixed by reading the family before the lab, and `markOf` still does.
*
* Our own house glyph on Zen. @hanzo/logo grouped a ZEN_MARK beside ENSO_MARK
* under HOUSE_MARKS, so Zen wore an ensō — Hanzo's brush ring, left open —
* while the paragraph above it credited Zoo. The page was drawing one house
* where the text named two. @hanzo/logo carries Hanzo's marks now; Zoo's venn
* is vendored here beside the other labs', and `zen -> zoo` is one row in OF.
*
* Neither glyph may decay to the monogram either. That fallback is for a lab
* with no logo, and a mark silently becoming a letter is how a wrong import
* would look on this page: still laid out, still green, just no longer a logo.
*
* The venn's disc MUST be cut by an SVG `<clipPath>`, in user units. A CSS
* `clip-path: circle(11.5px …)` resolves against the RENDERED box instead of
* the viewBox, so it cuts correctly at 96px and not at all at 18px — the mark
* squares off into a scribble exactly where it is smallest and most used.
*/
/** Zoo's venn: the cut, its three petals and the ring around them. */
const VENN = 1 + 3 + 1
/** Hanzo's H: @hanzo/logo's paths, scaled into the 24-unit box. */
const HANZO = (mark: string) => mark.startsWith('<g transform="scale(0.358') && !mark.includes('clipPath')
/** The lab each family belongs to, and the shape that lab's mark must have. */
const MAKER = {
enso: HANZO,
kai: HANZO,
/** The venn, cut in user units — an SVG clipPath the group actually points at. */
zen: (mark: string) =>
mark.includes('<clipPath') &&
mark.includes('clip-path="url(#zoo-cut)"') &&
(mark.match(/<circle/g) ?? []).length === VENN,
} as const
test('Enso and Kai wear Hanzo’s H, Zen wears Zoo’s venn, and none is a letter', async ({ page }) => {
test.setTimeout(3 * 60 * 1000)
const server = await serveExport()
try {
await page.goto(server.url + '/models.html', { waitUntil: 'domcontentloaded' })
// Read every glyph on the page with the model it stands for, rather than
// naming the models: the row most worth measuring is the one added next.
const drawn = await page.evaluate(() => {
const glyphs = [...document.querySelectorAll('svg[focusable="false"], span[aria-hidden]')]
return glyphs.flatMap((g) => {
const href = (g.closest('a') as HTMLAnchorElement | null)?.getAttribute('href')
const id = href?.match(/^\/models\/[^/]+\/(.+)$/)?.[1]
if (!id) return []
return [{ id, family: id.match(/^[a-z]+/)?.[0] ?? '', letter: g.tagName === 'SPAN', mark: g.innerHTML }]
})
})
for (const family of Object.keys(MAKER) as (keyof typeof MAKER)[]) {
const rows = drawn.filter((d) => d.family === family)
expect(rows.length, `no ${family} model on /models, so its mark is unmeasured`).toBeGreaterThan(0)
const letters = rows.filter((d) => d.letter).map((d) => d.id)
expect(letters, `${family} fell back to a monogram`).toEqual([])
const wrong = rows.filter((d) => !MAKER[family](d.mark)).map((d) => d.id)
expect(wrong, `${family} is not wearing its maker's mark`).toEqual([])
}
// The two may not collapse into one another.
const glyphs = (family: string) => new Set(drawn.filter((d) => d.family === family).map((d) => d.mark))
const zen = glyphs('zen')
expect([...glyphs('enso')].some((m) => zen.has(m)), 'Enso and Zen drew the same glyph').toBe(false)
} finally {
await server.close()
}
})