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>
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
#!/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(" ")}`);
|
||||
Reference in New Issue
Block a user