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>
This commit is contained in:
parent
8328d5d31e
commit
6028ac2f90
25 changed files with 214 additions and 53 deletions
81
tests/reference-integrity.test.mjs
Normal file
81
tests/reference-integrity.test.mjs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
// 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');
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue