#!/usr/bin/env node // tools/asset-catalog/scripts/lint-wiki-errata.mjs // Source: Phase 3 D-08 / SDOC-01. // // Asserts the file_text_* errata in decomp/wiki/16-bno-bnb-notes.md and // .planning/research/PITFALLS.md §A5 has not regressed. Ground truth: // extracted/server-5-4/scripts/0365-mb_backup.gml (`.bnb` writer using // file_text_open_write + @TOPIC/@REPLY markers) and 0367-users_restore.gml // (`.bnu` reader using file_text_open_read flat-loop). // // Usage: node tools/asset-catalog/scripts/lint-wiki-errata.mjs // Exit codes: 0 success, 1 errata regression, 2 usage error. import { existsSync, readFileSync } from 'node:fs'; const TARGETS = [ { path: 'decomp/wiki/16-bno-bnb-notes.md', label: 'wiki/16' }, { path: '.planning/research/PITFALLS.md', label: 'PITFALLS' }, ]; const arg = process.argv[2]; if (arg === '--help' || arg === '-h') { process.stdout.write( 'Usage: lint-wiki-errata.mjs\n' + ' Asserts file_text_* errata is present in:\n' + ' - decomp/wiki/16-bno-bnb-notes.md\n' + ' - .planning/research/PITFALLS.md (§A5)\n' + 'Exit codes: 0 success, 1 errata regression, 2 usage error.\n', ); process.exit(0); } if (arg) { process.stderr.write(`lint-wiki-errata: unexpected argument: ${arg}\n`); process.exit(2); } let errors = 0; for (const t of TARGETS) { if (!existsSync(t.path)) { process.stderr.write(`lint-wiki-errata: missing file ${t.path}\n`); errors++; continue; } const txt = readFileSync(t.path, 'utf-8'); if (!/file_text_\*/.test(txt)) { process.stderr.write( `lint-wiki-errata: ${t.label} (${t.path}) missing file_text_* primary claim ` + `(D-08 errata regression)\n`, ); errors++; } if (!/ERRATA 2026-05-03/.test(txt)) { process.stderr.write( `lint-wiki-errata: ${t.label} (${t.path}) missing ERRATA 2026-05-03 callout (D-08)\n`, ); errors++; } // Catch primary file_bin_* claims that lack a same-paragraph file_text_* // correction. Heuristic: any line that mentions file_bin_* with no // file_text_* on that line AND no file_text_* within +/- 3 lines is a // regression suspect. const lines = txt.split('\n'); for (let i = 0; i < lines.length; i++) { if (!/file_bin_\*/.test(lines[i])) continue; if (/file_text_\*/.test(lines[i])) continue; const start = Math.max(0, i - 3); const end = Math.min(lines.length, i + 4); const window = lines.slice(start, end).join('\n'); if (!/file_text_\*/.test(window)) { process.stderr.write( `lint-wiki-errata: ${t.label}:${i + 1} orphan file_bin_* claim ` + `(no file_text_* within ±3 lines)\n`, ); errors++; } } } if (errors > 0) { process.stderr.write(`lint-wiki-errata: ${errors} error(s)\n`); process.exit(1); } process.stdout.write( 'lint-wiki-errata: OK (file_text_* errata present in both targets)\n', ); process.exit(0);