Files
tera/scripts/check-no-binaries.mjs
T
Karti Tripathi 44c5a79424
gates / clean-clone (push) Successful in 15s
gates / zero-config-boot (push) Successful in 9s
gates / no-binary-art (push) Successful in 4s
SoCal, the whole bay, a moon, and gates that actually run
Six agents in parallel, and the two city packs independently reported the same
blocker: `focusRegions` and `coarseFactor` existed on the `City` type and
nothing implemented them. Uniform lattices would have been 2.9M points for
Southern California and 3.7M for the expanded bay. Both packs were unloadable
as written.

`buildAxis` is the answer, and it is honest about its limits: refinement is per
axis, not per rectangle, so a focus region sharpens its whole row *and* its
whole column. Two regions at opposite corners refine nearly everything between
them. Measured, not guessed — the bay went 0.53M points with one region and
1.64M with three, for detail nobody is looking at from a board this wide. One
region each, coarse factor ten, and the builds land at 3.8 s and 2.3 s.

Then three things that were only ever right because San Francisco was the only
city. `maxDistance: 340` and a 170-unit shadow box were constants tuned for a
230-unit board; the bay is 1003 units across and the camera physically could
not retreat far enough to frame it. Fog distances were scene units pinned to
the same assumption. And `minVisibilityM` defaulted to 4.5 km of honest
weather, which over ninety-four kilometres of bay correctly hides three
quarters of it — the night view was a black rectangle for a completely
reasonable reason. All three now derive from the board.

The moon is a real ephemeris and its light is a deliberate lie: 1.15, against a
physical ratio of one to four hundred thousand. What is being reproduced is
what a moonlit night looks like on a screen in a lit room.

The CI gate caught itself, which is the part worth keeping. Port 8431 was
already held by a server from an earlier session, so the boot check polled a
healthy stranger while the process it started died on EADDRINUSE. It now
refuses to run rather than pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 03:13:32 -07:00

161 lines
5.4 KiB
JavaScript

#!/usr/bin/env node
/**
* The no-binary-art gate: `src/**` holds no committed binary art, ever.
*
* This is the check that backs the licensing argument in ARCHITECTURE.md §3 —
* every mesh is a function composing cached unit primitives and every texture is
* drawn on a 2D canvas from seeded noise, which is what gives the repo zero
* asset-licensing exposure. A committed `.glb` or `.png` is a claim about
* provenance that nobody in CI can verify, so the answer is that they do not
* arrive at all.
*
* Two things about the scope, both of them corrections of an earlier design that
* CONTRACT.md §7 calls out by name.
*
* The enumeration is `git ls-files` and never a filesystem walk. A self-hoster
* is told, by CONTRIBUTING.md and by the office packs, to drop their own
* legally-clean art into `public/props/` and friends; a walk would find it and
* fail their build over files this repo does not distribute and has no opinion
* about. Tracked files are the only files a licence claim can be made about, so
* tracked files are the only files enumerated. If git is missing, this exits
* non-zero rather than falling back to a walk — a gate that quietly changes what
* it measures is worse than one that stops.
*
* And it is strict over `src/**` only. That is where the procedural-assets
* promise lives. `public/props/`, `public/kits/`, `public/offices/` and `docs/`
* are hard-exempt below and git-ignored besides: they are self-hoster space.
*
* Runs by hand as `node scripts/check-no-binaries.mjs` from anywhere in the
* working tree.
*/
import { execFileSync } from "node:child_process";
// ---- The rule ----
/**
* Art and binary-asset extensions. Lowercased at the comparison, so a `.PNG`
* off a Windows checkout is caught too.
*/
const DENIED_EXTENSIONS = new Set([
".glb",
".gltf",
".png",
".jpg",
".jpeg",
".webp",
".hdr",
".exr",
".ttf",
".otf",
".woff",
".woff2",
".mp3",
".wav",
".fbx",
".obj",
]);
/** The pathspec handed to `git ls-files`. Everything outside it is unexamined. */
const SCOPE = "src";
/**
* Self-hoster space, exempt even if something puts it inside the scope later.
* These are repo-relative prefixes, matched against the paths git reports.
*/
const EXEMPT_PREFIXES = [
"public/props/",
"public/kits/",
"public/offices/",
"docs/",
];
// ---- Enumeration ----
function repoRoot() {
try {
return execFileSync("git", ["rev-parse", "--show-toplevel"], {
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"],
}).trim();
} catch {
fail(
"could not ask git for the repo root.",
"This check enumerates tracked files with `git ls-files` and deliberately has no",
"filesystem-walk fallback, because a walk would fail a self-hoster's build over",
"their own untracked art. Run it inside a git checkout.",
);
}
}
function trackedFiles(root, pathspec) {
const out = execFileSync("git", ["ls-files", "-z", "--", pathspec], {
cwd: root,
encoding: "utf8",
// A NUL-separated listing of a repo this size is tiny, but the default 1 MB
// ceiling is close enough to be worth not thinking about again.
maxBuffer: 64 * 1024 * 1024,
});
return out.split("\0").filter((path) => path !== "");
}
/**
* The extension, lowercased, or the empty string. A leading dot with no stem —
* `src/.keep` — is a dotfile and has no extension, which is why this compares
* against the last slash rather than reaching for `path.extname`.
*/
function extensionOf(path) {
const dot = path.lastIndexOf(".");
const slash = path.lastIndexOf("/");
if (dot <= slash + 1) return "";
return path.slice(dot).toLowerCase();
}
function isExempt(path) {
return EXEMPT_PREFIXES.some((prefix) => path.startsWith(prefix));
}
// ---- Reporting ----
function fail(...lines) {
console.error(`\ncheck-no-binaries: FAIL — ${lines[0]}\n`);
for (const line of lines.slice(1)) console.error(line);
console.error("");
process.exit(1);
}
// ---- Run ----
const root = repoRoot();
const tracked = trackedFiles(root, SCOPE);
const offenders = tracked
.filter((path) => !isExempt(path))
.filter((path) => DENIED_EXTENSIONS.has(extensionOf(path)));
if (offenders.length > 0) {
const listed = offenders.map((path) => ` ${path} (${extensionOf(path)})`);
fail(
`${offenders.length} binary asset${offenders.length === 1 ? "" : "s"} tracked under ${SCOPE}/.`,
...listed,
"",
"Apache 2.0 is a promise that everything in this repo is ours to give away, and a",
"committed binary asset is a provenance claim no reviewer can check. Assets here are",
"procedural TypeScript: meshes compose cached unit primitives, textures are drawn on a",
"2D canvas from seeded noise. See ARCHITECTURE.md §3 and CONTRACT.md §3.",
"",
"If this is your own art for your own deployment, it does not belong in src/ at all —",
`put it under ${EXEMPT_PREFIXES.join(", ")}, which are git-ignored self-hoster space, and`,
"this check will never see it.",
);
}
console.log(
`check-no-binaries: ok — ${tracked.length} tracked file${tracked.length === 1 ? "" : "s"} under ` +
`${SCOPE}/, none matching ${DENIED_EXTENSIONS.size} denied extensions.`,
);
console.log(
` enumerated with \`git ls-files\`, so untracked local art is invisible to this check.`,
);
console.log(` denied: ${[...DENIED_EXTENSIONS].join(" ")}`);
console.log(` exempt: ${EXEMPT_PREFIXES.join(" ")}`);