import { existsSync } from 'node:fs' import { fileURLToPath } from 'node:url' /* * ═══════════════════════════════════════════════════════════════════════════ * Where the source-reading guards look, and what happens when a place is gone. * * ── Why this exists (2026-09-08) ─────────────────────────────────────────── * Four guards in this package read the *consumer's* source as well as their own, because the * defects they catch are committed by callers: a caller that fills a kernel kind with a * literal, a caller that reads the journal without saying it is folding, a caller that * ingests without writing to the ledger. * * The applications then moved to their own repository, and `../../operato-twin/server` stopped * existing. Two of the four failed with ENOENT. **The other two passed** — their directory * walker swallows a missing root: * * try { entries = readdirSync(dir) } catch { … } * * So half of each of those guards was scanning nothing and reporting green. A guard that * quietly stops guarding is worse than one that fails, because the failure is the only signal * that it was ever load-bearing. * * ── So a missing root is an error, not a skip ────────────────────────────── * `guardRoots` refuses to hand back a root that is not there. The message says which place is * gone and what to do about it, because the answer is not "delete the root" — the code being * guarded still exists, it is just in another repository now, and the guard has to follow it * there. * ═══════════════════════════════════════════════════════════════════════════ */ /** * Resolve the places a guard scans, and fail loudly if any of them is missing. * * @param base `import.meta.url` of the calling test. * @param places Paths relative to that test file. */ export function guardRoots(base: string, places: readonly string[]): string[] { const missing: string[] = [] const roots = places.map(place => { const path = fileURLToPath(new URL(place, base)) if (!existsSync(path)) missing.push(place) return path }) if (missing.length) { throw new Error( `guard root missing: ${missing.join(' · ')}\n` + 'This guard reads a consumer\'s source, and that source is no longer at this path.\n' + 'The applications moved to the operato-application repository (ADR-0041 era). Move the\n' + 'consumer half of this guard there rather than deleting the root — a root removed here\n' + 'is a rule that stops being checked, and nothing will say so.' ) } return roots } /** * Resolve one file a guard reads, and fail loudly if it is gone. * * Same reason as above, for the guards that read a single named file rather than walking a * tree. `readFileSync` would already throw here — this only replaces `ENOENT: no such file` * with a sentence that says what to do. */ export function guardFile(base: string, place: string): string { const path = fileURLToPath(new URL(place, base)) if (!existsSync(path)) { throw new Error( `guard file missing: ${place}\n` + 'This guard reads a consumer\'s source. That file moved to the operato-application\n' + 'repository — move this assertion there rather than deleting it.' ) } return path }