import { describe, it, expect } from 'vitest'; import { readFileSync, readdirSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import { dirname, resolve } from 'node:path'; /** * No source file may contain a raw NUL byte. * * Not a style rule — REVIEWABILITY. Git decides a file is binary by sniffing for a NUL near the * start, and a binary file appears in every diff as `Bin 5078 -> 5609 bytes`: no lines, no blame, * nothing for a reviewer to read. Two files here carried one, each written where the six-character * escape sequence was intended, and the consequences were not theoretical: * * - `resolveField.ts` was binary to git for the whole of Epic 3. Three separate review passes * read "the diff", and this file was a single `Bin` line to all of them. * - Worse, an edit to it silently did nothing. The search anchor held a space where the file * held a NUL, so the replacement matched nothing and failed quietly — leaving a doc-comment * describing a cache key the code did not use, and a commit message claiming a fix that had * never landed. * * The second file's NUL sat at byte 8500, past where git sniffs, so it was not yet flagged binary. * That is worse rather than better: a latent tripwire that flips the whole file the moment * anything above it grows. * * Write the escape sequence when a separator is wanted. Identical at runtime; the file stays text. * * Lives in `apps/cli` rather than in `template-index`, for the same reason the corpus tests do: * walking a real filesystem is an adapter's job, and `template-index` is core, where AD-3 forbids * `node:fs`. The boundary rule caught this test's first home, which is the rule working. */ const here = dirname(fileURLToPath(import.meta.url)); const repoRoot = resolve(here, '../../../..'); describe('source files stay reviewable', () => { it('contains no raw NUL byte in any TypeScript source', () => { const files: string[] = []; const walk = (dir: string) => { for (const entry of readdirSync(dir, { withFileTypes: true })) { const full = resolve(dir, entry.name); if (entry.isDirectory()) { if (entry.name === 'node_modules' || entry.name === 'dist') continue; walk(full); } else if (entry.name.endsWith('.ts')) { files.push(full); } } }; /** * ⛔ `walk(resolve(repoRoot, 'packages'))` was here until Epic 2 Story 2.4, 2026-08-19, and it THREW * rather than finding nothing: `readdirSync` on an absent directory is `ENOENT`, so the whole test * failed over a directory this repository is now correct not to have. The fourteen * `@beehexa/hexasync-template-*` packages are in `hexasync-templates-vscode-ext`, which has its own * `test/sourceHygiene.spec.ts` — ⚠️ MEASURED, not assumed: its `SCAN_ROOTS` are `['src', 'test', * 'scripts']`, so `packages/**` is NOT covered there either. The claim "no source file carries a raw NUL * byte" therefore holds over `apps/` here and over the extension's own three roots there, and over the * fourteen packages NOWHERE. Owed work in that repository (one entry in `SCAN_ROOTS`), named here so it * is a known gap rather than a silent one. * * The floor below is untouched, and that is the point of setting it where the assertion actually needs * it: `apps/` alone carries far more than 30 TypeScript files, so removing a root did not require * weakening a number. */ walk(resolve(repoRoot, 'apps')); // Sanity: if the glob finds nothing, an empty offender list would prove nothing at all. expect(files.length).toBeGreaterThan(30); const offenders = files .filter((file) => readFileSync(file).includes(0x00)) .map((file) => file.slice(repoRoot.length + 1)); expect(offenders).toEqual([]); }); });