397777089e
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
68 lines
2.7 KiB
JavaScript
68 lines
2.7 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Brain governance controller C6 — chain-map sync checker.
|
|
* Verifies tools/observer-chain-map.json against the L1-L13 table in
|
|
* docs/routing-off-phase.md. Sync is checked by L-number sets (both
|
|
* directions), not by node names — node_chosen values (skill-id) differ
|
|
* from the human display names in the .md table. Pure fs/regex, no LLM.
|
|
*/
|
|
import { readFileSync } from 'node:fs';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { dirname, join } from 'node:path';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const MD_PATH = join(__dirname, '..', 'docs', 'routing-off-phase.md');
|
|
const JSON_PATH = join(__dirname, 'observer-chain-map.json');
|
|
|
|
/** Extract the set of L-numbers ("L1".."L13") from the routing-off-phase.md table. */
|
|
export function parseChainsFromMd(md) {
|
|
const set = new Set();
|
|
for (const line of md.split(/\r?\n/)) {
|
|
const m = /^\|\s*(L\d+)\s*\|/.exec(line.trim());
|
|
if (m) set.add(m[1]);
|
|
}
|
|
return set;
|
|
}
|
|
|
|
/** Compare JSON L-numbers against the md set, both directions. */
|
|
export function checkSync(jsonMap, mdSet) {
|
|
const jsonSet = new Set();
|
|
for (const [node, chains] of Object.entries(jsonMap)) {
|
|
if (node === '_note') continue;
|
|
if (Array.isArray(chains)) for (const c of chains) jsonSet.add(c);
|
|
}
|
|
const jsonOnly = [...jsonSet].filter((c) => !mdSet.has(c)); // ссылки на несуществующие L
|
|
const mdOnly = [...mdSet].filter((c) => !jsonSet.has(c)); // потерянные цепочки
|
|
return { ok: jsonOnly.length === 0 && mdOnly.length === 0, jsonOnly, mdOnly };
|
|
}
|
|
|
|
/** CLI entry — exit 1 on drift with a human-readable message. */
|
|
function main() {
|
|
const md = readFileSync(MD_PATH, 'utf8');
|
|
const jsonMap = JSON.parse(readFileSync(JSON_PATH, 'utf8'));
|
|
const mdSet = parseChainsFromMd(md);
|
|
if (mdSet.size === 0) {
|
|
console.error(
|
|
'[chain-map-checker] не нашёл ни одной L-строки в routing-off-phase.md — формат таблицы изменился?'
|
|
);
|
|
process.exit(1);
|
|
}
|
|
const res = checkSync(jsonMap, mdSet);
|
|
if (res.ok) {
|
|
console.log(`[chain-map-checker] OK — ${mdSet.size} chains in sync`);
|
|
process.exit(0);
|
|
}
|
|
console.error('[chain-map-checker] дрейф маппинга chain-map <-> routing-off-phase.md:');
|
|
if (res.jsonOnly.length)
|
|
console.error(` JSON ссылается на отсутствующие в .md цепочки: ${res.jsonOnly.join(', ')}`);
|
|
if (res.mdOnly.length)
|
|
console.error(
|
|
` В .md есть цепочки без записи в JSON: ${res.mdOnly.join(', ')} — добавьте узлы в tools/observer-chain-map.json`
|
|
);
|
|
process.exit(1);
|
|
}
|
|
|
|
if (process.argv[1]?.endsWith('observer-chain-map-checker.mjs')) {
|
|
main();
|
|
}
|