fix(okr): skjerp separasjonsvakten og lukk tre defekter i D6-leveransen

This commit is contained in:
Kjell Tore Guttormsen 2026-08-02 07:09:35 +02:00
commit d2ac153a68
5 changed files with 100 additions and 14 deletions

View file

@ -383,6 +383,33 @@ for (const [navn, bygger] of FORMER) {
});
}
// Avviks-seksjonen viser til «forventningen til aspirational under». Den
// seksjonen finnes bare naar syklusen HAR aspirational-KR. En syklus med bare
// committed -- fullt lovlig, og typisk for en ren etterlevelses-syklus -- ville
// faatt et styringsdokument som peker paa en seksjon som ikke er der. Fixturen
// kan ikke fange det; den har begge typene.
const KUN_COMMITTED = {
id: 'T-kun-committed',
okrer: [{
fil: 'okr-etterlevelse.md',
tittel: 'Etterlevelse',
krer: [{ n: 1, navn: 'Palagte tilsyn gjennomfoert (antall)', baseline: 0, target: 12, naa: 9, type: 'committed' }],
}],
};
for (const [navn, bygger] of FORMER) {
test(`(10f/${navn}) ingen henvisning til en aspirational-seksjon som ikke finnes`, () => {
const md = bygger(KUN_COMMITTED, { naa: NAA });
const viser = /forventningen til aspirational under/i.test(md);
const finnes = /^##+\s+Aspirational/m.test(md);
assert.equal(
viser && !finnes,
false,
'avviks-seksjonen viser til en aspirational-seksjon rapporten ikke har',
);
});
}
// Generatoren handhever regelen; governance-fila baerer begrunnelsen. Staar
// begrunnelsen bare i koden, kan en leser overproeve vakten uten aa moete
// argumentet -- og det er nettopp en Riksrevisjon-vendt leser som vil sporre.
@ -586,6 +613,35 @@ test('(7i) CLI: etatsstyring skrives til sin egen fil', async (t) => {
assert.equal(lesSyklus(dir).okrer.length, 2, 'rapporten ble lest som en OKR');
});
// Fixturens historikk er FLAT prosa-filer. Den EKTE formen kommer fra
// /okr:oppsett arkiver (commands/oppsett.md:514-518), som lager BEGGE deler:
// katalogen `historikk/[id]/` med syklusens filer, OG den flate
// `historikk/[id]-resultater.md` med sammendraget. Sammendraget er riktig
// granularitet for et flerarig perspektiv; katalogen er raamaterialet under.
// Uten denne testen ville fixtur, kode og (7h) vaert tre artefakter som er enige
// med hverandre -- og ingen av dem den kommandoen som faktisk skriver treet.
test('(7j) historikk leses fra arkiveringens EKTE form: sammendragsfil, ikke katalog', async (t) => {
const { rmSync, mkdirSync, mkdtempSync, writeFileSync, copyFileSync, readdirSync, readFileSync: les } = await import('node:fs');
const { tmpdir } = await import('node:os');
const rot = mkdtempSync(join(tmpdir(), 'okr-arkiv-'));
const dir = join(rot, 'syklus', 'T1-2026');
const hist = join(rot, 'historikk');
mkdirSync(dir, { recursive: true });
mkdirSync(join(hist, 'T3-2025'), { recursive: true });
for (const f of readdirSync(SYKLUS_DIR)) copyFileSync(join(SYKLUS_DIR, f), join(dir, f));
// Kopien av syklusen ligger i katalogen; sammendraget flatt ved siden av.
writeFileSync(join(hist, 'T3-2025', 'okr-gammel.md'), ['---', 'type: OKR', 'title: Gammel OKR', '---', ''].join('\n'));
writeFileSync(join(hist, 'T3-2025-resultater.md'), ['---', 'type: Retrospektiv', 'title: Resultater T3-2025', '---', '# Resultater T3-2025', ''].join('\n'));
t.after(() => rmSync(rot, { recursive: true, force: true }));
assert.equal((await kjoerCli([dir, 'arsrapport'])).code, 0);
const kropp = les(join(dir, 'rapport-arsrapport.md'), 'utf8');
assert.match(kropp, /Resultater T3-2025/, 'sammendragsfila fra arkiveringen ble ikke funnet');
// Den arkiverte syklus-katalogen er raamateriale, ikke en post i det flerarige
// perspektivet -- og `[id]/` ender ikke paa .md, saa den filtreres bort som seg selv.
assert.doesNotMatch(kropp, /Gammel OKR/, 'generatoren gikk inn i den arkiverte syklus-katalogen');
});
// Idempotens: rapporten skal kunne kjoeres om igjen uten aa endre bundlen, og
// uten aa bli lest som en OKR neste gang (filnavnet starter ikke med okr-).
test('(7f) CLI: to kjoeringer gir byte-identisk fil', async (t) => {