feat(okr): sandbagging-vakt i alle tre rapportformene
This commit is contained in:
parent
052e4d4e86
commit
47e7ac9e2a
3 changed files with 180 additions and 31 deletions
|
|
@ -203,7 +203,7 @@ function seksjonsKropp(md, overskrift) {
|
|||
test('(8a) del III bygger paa aarsrapportens eget anker og en seksjon per Objective', () => {
|
||||
const md = delIII();
|
||||
assert.match(md, /[Aa]arets aktiviteter og resultater/, 'del III-ankeret mangler');
|
||||
const objectives = md.split('\n').filter((l) => /^##\s+(?!Flerarig|Aspirational)/.test(l));
|
||||
const objectives = md.split('\n').filter((l) => /^##\s+(?!Flerarig|Aspirational|Avvik)/.test(l));
|
||||
assert.equal(objectives.length, 2, `forventet 2 Objective-seksjoner, fant ${objectives.length}`);
|
||||
assert.match(md, /Trafikksikkerhet og tunnelsikkerhet/);
|
||||
assert.match(md, /Digitalisering av tjenester/);
|
||||
|
|
@ -302,6 +302,99 @@ test('(9d) etatsstyringsunderlag kaster ved tom eller ufullstendig syklus', () =
|
|||
assert.throws(() => etatsstyringsunderlag(undefined), /syklus/i);
|
||||
});
|
||||
|
||||
// --- (10) Sandbagging-vakten (steg 13) ---
|
||||
//
|
||||
// Briefens navngitte suksesskriterium: en aspirational-OKR med lav score
|
||||
// presenteres ALDRI som avvik. Regelen staar i okr-offentlig-governance.md:148-156;
|
||||
// her far den mekanisk handhevelse i alle tre rapportformene.
|
||||
//
|
||||
// Vakten er bygget i tre ledd, og rekkefolgen er ikke tilfeldig:
|
||||
// (10a) POSITIV -- et committed KR under kravet STAAR i avviks-seksjonen.
|
||||
// Uten dette leddet ville (10b) bestaatt trivielt paa en
|
||||
// seksjon som ikke finnes eller er tom, og vakten vaert prosa.
|
||||
// (10b) NEGATIV -- fixturens aspirational-KR med lav score staar der ikke.
|
||||
// (10c) MUTASJON -- samme KR, flyttet til committed, DUKKER OPP. Det beviser at
|
||||
// filteret henger paa TYPEN og ikke paa navnet, og at
|
||||
// seksjonslokatoren i (10b) faktisk fant noe aa lete i.
|
||||
//
|
||||
// De tre formene har strukturelt ulike avviks-regioner, saa lokatoren ankrer paa
|
||||
// overskriftsformen (`## Avvik...`), som er felles.
|
||||
|
||||
function avvikKropp(md) {
|
||||
const overskrift = md.split('\n').find((l) => /^##\s+Avvik/.test(l));
|
||||
assert.ok(overskrift, 'rapportformen har ingen avviks-seksjon aa filtrere');
|
||||
const kropp = seksjonsKropp(md, overskrift.trim());
|
||||
assert.ok(kropp && kropp.length > 0, 'avviks-seksjonen er tom');
|
||||
return kropp;
|
||||
}
|
||||
|
||||
// Fixturens KR-er, med de rollene steg 7 ga dem.
|
||||
const COMMITTED_UNDER_KRAV = 'Tunneler med oppgradert sikkerhetsutrustning';
|
||||
const ASPIRATIONAL_LAV_SCORE = ['Andel digitale soeknader', 'Gjennomsnittlig saksbehandlingstid'];
|
||||
|
||||
for (const [navn, bygger] of FORMER) {
|
||||
test(`(10a/${navn}) committed KR under kravet STAAR i avviks-seksjonen`, () => {
|
||||
const kropp = avvikKropp(byggFor(bygger));
|
||||
assert.match(
|
||||
kropp,
|
||||
new RegExp(COMMITTED_UNDER_KRAV),
|
||||
`committed KR under kravet mangler i avviks-seksjonen:\n${kropp}`,
|
||||
);
|
||||
});
|
||||
|
||||
test(`(10b/${navn}) aspirational KR med lav score staar IKKE i avviks-seksjonen`, () => {
|
||||
const kropp = avvikKropp(byggFor(bygger));
|
||||
for (const kr of ASPIRATIONAL_LAV_SCORE) {
|
||||
assert.doesNotMatch(
|
||||
kropp,
|
||||
new RegExp(kr),
|
||||
`aspirational KR presentert som avvik -- sandbagging-vakten er brutt:\n${kropp}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test(`(10c/${navn}) mutasjon: samme KR som committed ER et avvik`, () => {
|
||||
const syklus = lesSyklus(SYKLUS_DIR);
|
||||
const mutert = {
|
||||
...syklus,
|
||||
okrer: syklus.okrer.map((okr) => ({
|
||||
...okr,
|
||||
krer: okr.krer.map((kr) => ({ ...kr, type: 'committed' })),
|
||||
})),
|
||||
};
|
||||
const kropp = avvikKropp(bygger(mutert, { naa: NAA }));
|
||||
for (const kr of ASPIRATIONAL_LAV_SCORE) {
|
||||
assert.match(
|
||||
kropp,
|
||||
new RegExp(kr),
|
||||
`filteret henger ikke paa typen -- KR-et uteble ogsaa som committed:\n${kropp}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Uten den kausale begrunnelsen er vakten en norm mottakeren kan overproeve.
|
||||
// Bogsnes' poeng (okr-framework.md:158-161) er at re-bundling av
|
||||
// aspirational-scoren i rapporteringen henter sandbaggingen tilbake
|
||||
// strukturelt -- det argumentet holder for en Riksrevisjon-leser.
|
||||
test(`(10d/${navn}) rapporten baerer forventningen til aspirational, ikke bare tallet`, () => {
|
||||
const md = byggFor(bygger);
|
||||
assert.match(md, /0\.7/, 'forventningen om ~0.7 maa staa eksplisitt');
|
||||
assert.match(md, /sandbagging/i, 'begrunnelsen for at lav aspirational-score ikke er avvik mangler');
|
||||
});
|
||||
}
|
||||
|
||||
// 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.
|
||||
test('(10e) governance-fila begrunner regelen kausalt, ikke bare normativt', () => {
|
||||
const doc = readFileSync(
|
||||
join(ROOT, 'skills/okr-offentlig-sektor/references/okr-offentlig-governance.md'),
|
||||
'utf8',
|
||||
);
|
||||
assert.match(doc, /re-bundl/i, 'den kausale mekanismen (re-bundling) mangler');
|
||||
assert.match(doc, /okr-framework\.md/, 'begrunnelsen maa peke til kanon, ikke duplisere den');
|
||||
});
|
||||
|
||||
// --- (2d) Avvik-kolonnen maa vaere retningsagnostisk ---
|
||||
//
|
||||
// `naa >= target` ser riktig ut for oppadgaaende maal og er FEIL for
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue