okr/tests/reference-integrity.test.mjs
Kjell Tore Guttormsen 6028ac2f90 fix(okr): B1 KB/doc-hygiene + referanse-integritetstest (1.7.1-lane)
- Ny tests/reference-integrity.test.mjs: alle ${CLAUDE_PLUGIN_ROOT}-stier i
  commands/agents må finnes på disk OG være shippbare (aldri under gitignored
  .claude/). Fanger død-referanse-klassen permanent (TDD: rød på
  freshen-references før fiks). Suite 135 -> 138.
- Døde referanser lukket: freshen-references peker nå på metrics-library
  «Review-kadens»; metrics-library-provenanslinje uten død relativ sti.
- Kryssref-isolasjon: «Ressurser/Interne referanser»-seksjon i de 5 isolerte
  KB-filene (examples, oboard-guide, meeting-guides, calculator, cfr).
- «Sist oppdatert»-markør på alle 17 referansefiler (var 4).
- 19 -> 20 antipatterns (SKILL, kvalitet x2, help — help-forekomst funnet i
  sweep utover review-lista); «15. oktober» -> tidlig oktober (arshjul x2,
  framework); garblet prognoseformel rettet mot calculator-kanon (framework).
- Typonits: schuld/Næste (meeting-guides), resultatmål (dfo-mapping),
  sandbagging (trendanalytiker), Q1-2026 -> T1-2026 (fremdriftssporer).
- README: hooks-badge 4 -> 3, død Stop-rad fjernet.
- BACKLOG.md foldet inn i docs/roadmap.md (tema-nivå, ingen interne
  plandetaljer) og slettet; .gitignore ROADMAP.md ankret til rot (/ROADMAP.md)
  så docs/roadmap.md kan trackes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 03:43:22 +02:00

81 lines
3.1 KiB
JavaScript

// reference-integrity.test.mjs
// B1 (1.7.1): referanse-integritet for alle ${CLAUDE_PLUGIN_ROOT}-stier i
// commands/ og agents/. Fanger doed-referanse-klassen permanent (review 4:
// freshen-references pekte paa gitignored .claude/-sti som aldri shippes).
// To invarianter per referert sti:
// 1. Stien maa finnes paa disk (relativt til plugin-rot).
// 2. Stien maa vaere shippbar: aldri under .claude/ (gitignored, finnes
// lokalt men ikke i installert plugin — ren existsSync er falsk groenn).
// Glob-stier (* i siste segment) sjekkes som: katalog finnes + minst ett treff.
// Zero npm deps. Moenster: tests/package-shape.test.mjs (les fil, assert).
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync, readdirSync, existsSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
const SCAN_DIRS = ['commands', 'agents'];
// Stopper paa whitespace, backtick, anfoerselstegn, ), ] — tegnene som
// avslutter en sti i markdown-prosa/lenker. Ingen legitime stier her
// inneholder dem.
const PATH_RE = /\$\{CLAUDE_PLUGIN_ROOT\}\/([^\s`"')\]]+)/g;
function collectReferences() {
const refs = [];
for (const dir of SCAN_DIRS) {
for (const name of readdirSync(join(ROOT, dir))) {
if (!name.endsWith('.md')) continue;
const file = join(dir, name);
const body = readFileSync(join(ROOT, file), 'utf8');
for (const m of body.matchAll(PATH_RE)) {
refs.push({ file, rel: m[1] });
}
}
}
return refs;
}
function assertResolvable(rel) {
const starName = rel.lastIndexOf('*');
if (starName === -1) {
assert.ok(existsSync(join(ROOT, rel)), `finnes ikke: ${rel}`);
return;
}
// Glob: kun *-i-siste-segment brukes i repoet (f.eks. references/*.md).
const dir = dirname(rel);
assert.ok(!dir.includes('*'), `glob i katalogsegment stoettes ikke: ${rel}`);
assert.ok(existsSync(join(ROOT, dir)), `glob-katalog finnes ikke: ${dir}`);
const suffix = rel.slice(starName + 1);
const hits = readdirSync(join(ROOT, dir)).filter((n) => n.endsWith(suffix));
assert.ok(hits.length > 0, `glob uten treff: ${rel}`);
}
test('commands/agents refererer minst en plugin-rot-sti (regex-sanity)', () => {
const refs = collectReferences();
assert.ok(refs.length >= 10, `fant bare ${refs.length} referanser — regex broken?`);
});
test('alle ${CLAUDE_PLUGIN_ROOT}-stier er shippbare (aldri under .claude/)', () => {
const offenders = collectReferences().filter(({ rel }) =>
rel === '.claude' || rel.startsWith('.claude/'));
assert.deepEqual(
offenders.map((o) => `${o.file} -> ${o.rel}`),
[],
'gitignored .claude/-stier shippes aldri med pluginen'
);
});
test('alle ${CLAUDE_PLUGIN_ROOT}-stier finnes paa disk', () => {
const missing = [];
for (const { file, rel } of collectReferences()) {
try {
assertResolvable(rel);
} catch (e) {
missing.push(`${file} -> ${rel} (${e.message})`);
}
}
assert.deepEqual(missing, [], 'doede referanser funnet');
});