Files
hanzo.ai/scripts/audit-model-counts.mjs
zeekay d01992490a Every Enso figure is read from its research runs when a page opens, and the leaderboard snapshot is gone
/enso leads with three lines built from the enso.* runs: the router at
92.9% on GPQA Diamond, level with the pool's best single model, at $2.20,
5x less than Opus 4.8 (86.4%, $10.97); LiveCodeBench 93.7% at $3.06,
12.9x less than Opus 4.8 (72.0%, $39.43); and the pool's ceiling, 98.0%
(194/198) with four arms, said only as the ceiling the router is trained
toward. Under them a note says what that router was: enso-bench's at the
tag its run names, and the arms its notes count, not today's presets.
Every percentage and multiple is computed from the runs.

A board is each system's frontier run, else its v1 run (the same prompts;
v2 and max put others), over the whole set, none degraded. The GPQA and
LiveCodeBench boards draw on /enso, /models/enso and /benchmarks, the
GPQA one with the ceiling under it on /benchmarks/gpqa. /benchmarks' Enso
title, figure, hero tile and frontier (now accuracy against what the run
cost), the /models family card and the home tile read the same runs, and
each says the API is unreachable rather than show an older number.

lib/data/leaderboard.json and lib/leaderboard.ts are deleted: their Enso
tiers (98.0, 96.0, 92.9) were no run's, 98.0 being the pool's oracle.
With them go the vendor-reported field, the accuracy-at-cost scatter, the
reported-versus-measured browser, the shelf's Enso entries and the two
pages they generated, and the logo files only the scatter read. A preset
card keeps its name and rate and states no score. The gates serve
invented runs and hold every surface, the ceiling's figure and the
unreachable state to lib/enso.
2026-10-15 15:18:16 -07:00

94 lines
3.8 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* How many models Hanzo serves is ONE fact, so it is stated in ONE place.
*
* This site quoted it eighteen times in three different numbers — "100+", "130+"
* and "400+" — and none was reached by editing the others. The estate was worse:
* 85+, 200+ and a bare 340 on other surfaces. A reader comparing two pages saw us
* disagree about our own catalog, and no build ever noticed.
*
* The count is read when a page opens, from the price list's own summary
* (components/api/count, GET /v1/pricing/summary). This gate fails the build
* on a new hand-typed one, the way audit-price-literals.mjs does for a rate.
*
* Sibling of that script by design — same ratchet, same shrink-only ALLOWED, same
* self-exemption. It states the rule, so its own prose is the specification.
*/
import { execFileSync } from "node:child_process";
import { readFileSync } from "node:fs";
import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { code, isCode } from "./source.mjs";
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const EXT = new Set([".ts", ".tsx", ".js", ".jsx", ".mdx"]);
const SELF = "scripts/audit-model-counts.mjs";
// A bare two-to-four digit count immediately followed by "model(s)". Deliberately
// narrow: "100+ capabilities" and "260+ MCP tools" are different facts with
// different owners, and widening this to every number would make the gate noise.
// "Model Context Protocol" is a proper noun, not a count of models — "260+ MCP
// tools" is a different fact with a different owner, and matching it would make
// this gate cry wolf on the one thing it must be trusted about.
const COUNT = /\b\d{2,4}\s*\+?\s*(?:AI |frontier |premium )?models?\b(?!\s+Context\s+Protocol)/i;
// The ratchet. Path → why it still holds a literal. SHRINK ONLY.
const ALLOWED = {
"app/(marketing)/press/page.tsx":
"Dated press-release titles and blurbs (Feb 27 2026, '100+ Models, Zero Markup'). A press release is a record of what was said on a day, not a live claim — rewriting the number would falsify the artifact. These must NOT be converted.",
};
function tracked() {
return execFileSync("git", ["ls-files", "-z"], { cwd: ROOT, maxBuffer: 64 << 20 })
.toString()
.split("\0")
.filter((p) => p && EXT.has(p.slice(p.lastIndexOf("."))));
}
/** The lines as a reader of the site sees them: comments are not copy. */
const copy = (rel) => {
const text = readFileSync(resolve(ROOT, rel), "utf8");
return (isCode(rel) ? code(text) : text).split("\n");
};
const offenders = [];
for (const rel of tracked()) {
if (rel === SELF || ALLOWED[rel]) continue;
copy(rel).forEach((line, i) => {
if (COUNT.test(line)) offenders.push(`${rel}:${i + 1}: ${line.trim().slice(0, 100)}`);
});
}
if (offenders.length) {
console.error(
`\nThe model count is typed into ${offenders.length} place(s) that should be reading it.\n\n` +
offenders.map((o) => ` ${o}`).join("\n") +
`\n\nDraw <Models /> (components/api/count: the price list's count, read when\n` +
`the page opens). It is not typed.\n` +
`Do not add a line to ALLOWED — that list only shrinks.\n`
);
process.exit(1);
}
// An entry that no longer holds a literal is a rule protecting nothing.
const stale = Object.keys(ALLOWED).filter((p) => {
try {
return !copy(p).some((l) => COUNT.test(l));
} catch {
return true;
}
});
if (stale.length) {
console.error(
`\n${stale.length} ALLOWED entr(y/ies) no longer hold a model count:\n\n` +
stale.map((p) => ` ${p}`).join("\n") +
`\n\nDelete them from ALLOWED — the list is meant to shrink, and an entry that\n` +
`protects nothing hides the next real copy.\n`
);
process.exit(1);
}
console.log(
`model counts: ${Object.keys(ALLOWED).length} known holder(s) remaining, no new copies.`
);