fix(okr): C7b remediering R1-R4 (release-relevante review-funn for 1.8.0)
Lukker de fire release-relevante MAJOR-funnene fra /trekreview (S33). Alle TDD
roed -> groenn, ingen fiks landet uten en test som feilet foerst. Suite 163 -> 167.
R1 (af16d5e4) hooks/scripts/coaching-hook.mjs:83 -- LEVENDE REGRESJON innfoert av
1.8.0. Denne releasen skrev status-malen (commands/sporing.md:86-88) om til den
kanoniske skalaen On Track/At Risk/Off Track, mens hooken fortsatt talte kun
/i fare|blokkert/i over tabellrader. En status generert under 1.8.0 ga derfor
atRiskCount = 0 og SessionStart-nudgen sluttet stille aa utloese seg. Hooken
teller naa de kanoniske etikettene; de to norske er BEHOLDT som bakover-
kompatibilitet for status-filer skrevet foer 1.8.0. Nudge-teksten bruker samme
kanoniske vokabular. Ny testcase mates av malen slik den faktisk genereres i dag;
den eksisterende casen beholder gammelt vokabular og daekker legacy-stien.
R2 (7ec575be) F-i-omskrivingen ga hver kommando en Kontekstbevissthet-blokk som
INSTRUERER Glob, men allowed-tools ble kun utvidet i kaskade.md. Glob lagt til i
export, gap, governance, innfoering, kvalitet, moeter, skriv, sporing. Ny vakt-
case (k): nevner BODY verktoeyet, maa frontmatter deklarere det (13 kommandoer
instruerer Glob; alle 13 dekket). Case (i) grepper kun etter fjernede fraser og
kunne ikke fange dette.
R3 (5e61ae0d) tests/package-shape.test.mjs asserterte KUN package.json -- som er
private:true og dermed den ene flaten som aldri shipper. En delvis bump ville
shippet groenn. Ny versjonssync-case dekker .claude-plugin/plugin.json, README-
badgen og begge SKILL.md, med forventet verdi UTLEDET fra package.json (ett sted
aa endre ved neste bump). Mutasjonsbevist: hver av de fire flatene tilbakestilt
til 1.7.1 en om gangen -> casen roed i alle fire tilfeller.
R4 (ccff16e1 + 231c53fc) Tre parallelle confidence-etikettsett overlevde F-c:
fremdriftssporer-agent.md:68 ("Paa sporet / I fare / Blokkert"), :98
("Confidence: [Hoey/Medium/Lav]" -- en annen akse: stoerrelse, ikke sannsynlighet)
og SKILL.md:48 ("blocked"; kanonisk er "off track"). Alle tre erstattet med
referanse til kanon (okr-framework.md:389-392). sporing.md og agenten den
delegerer til svarer naa i samme vokabular. Vakt-case (a)/(b) skanner naa samme
sett som (d) allerede brukte (+ agents/ + SKILL.md), samlet i canonScan(). Ny
case (a2) fanger etikettsett skrevet som bullet eller mal-linje -- tabell-
signaturen alene fanget dem ikke, og det var nettopp formen driften overlevde i.
Divergens gjenkjennes STRUKTURELT (skraastrek-enumerasjon av >= 2 etiketter, med
>= 1 ikke-kanonisk), saa loepende prosa som "For KR i fare" og "Blokkert av
eksterne faktorer" ikke gir falske positive. Verifisert: roed-listen var noeyaktig
de 3 kjente linjene, ingen andre.
CHANGELOG/README: vakt-antall 13 -> 15 cases, suite 149 -> 167, og de fire
fiksene lagt inn under [1.8.0] (Added + Fixed). Ingen versjonsbump.
GJENSTAAR fra reviewet (3 MAJOR, IKKE i denne bolgen): 607313e3 F-d-kadens-
moensteret kan ikke feile paa den kanoniske kadens-tabellen; 029ef814
freshen-references.md:24 baerer fortsatt en foreldet kontekstblokk og vaktens
exclude-kommentar kaller fila feilaktig "allerede-fikset" (den er utenfor
1.8.0-scope by design, jf. ba91fc2 -- kommentaren er usann, ikke ekskluderingen);
7579d59c "Outcome" er utestbar i 10-dims-casen pga. substring-containment.
Verify: node --test --test-reporter=tap tests/*.test.mjs -> 167 pass / 0 fail.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016pUfkQ1YyH75z9y6RBaBHG
This commit is contained in:
parent
37142236f2
commit
44ceec7205
16 changed files with 189 additions and 27 deletions
|
|
@ -10,10 +10,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
Minor-release **«Én kanon»**: metodekonsolidering av MAJOR-/metode-funnene fra dyp review 2026-07-16 (§3). Ingen nye kommandoer, agenter eller referansefiler — prinsippet er ÉN kanonisk definisjon per metodebegrep, der konsumentflater (kommandoer, agenter, øvrige referansefiler) refererer kanon i stedet for å redefinere den. Konsolideringen er låst av en ny strukturell konsistensvakt, ikke bare av prosa.
|
||||
|
||||
### Added
|
||||
- **Kanon-konsistensvakt** (`tests/canon-consistency.test.mjs`, 13 cases: 10 vakt-cases F-a…F-i + score-grenser, pluss 3 parser-sanity-cases) — deterministisk, offline, zero-dep. Asserter strukturelt at hvert metodebegrep har én kilde: nøyaktig én confidence-tabell (kanonisk i `okr-framework.md`), ingen team-check-in med månedlig kadens og ingen ledelsesreview med ukentlig, ingen score→modenhet-avledning, ingen parallelle scorebånd utenfor kvalitetsrubrikken, kvalitetssjekker-agenten dekker alle 10 rubrikk-dimensjoner, antipattern-antall utledet av dynamisk telling (aldri hardkodet tall), ingen foreldede kontekst-injeksjons-fraser i `commands/`. Skrevet RED-først (Step 1); alle 10 vakt-cases flippet grønne underveis i Wave 1-5.
|
||||
- **Kanon-konsistensvakt** (`tests/canon-consistency.test.mjs`, 15 cases: 12 vakt-cases — F-a…F-i, score-grenser, divergerende etikettsett og Glob-dekning — pluss 3 parser-sanity-cases) — deterministisk, offline, zero-dep. Asserter strukturelt at hvert metodebegrep har én kilde: nøyaktig én confidence-tabell (kanonisk i `okr-framework.md`), ingen team-check-in med månedlig kadens og ingen ledelsesreview med ukentlig, ingen score→modenhet-avledning, ingen parallelle scorebånd utenfor kvalitetsrubrikken, kvalitetssjekker-agenten dekker alle 10 rubrikk-dimensjoner, antipattern-antall utledet av dynamisk telling (aldri hardkodet tall), ingen foreldede kontekst-injeksjons-fraser i `commands/`. Skrevet RED-først (Step 1); alle 10 vakt-cases flippet grønne underveis i Wave 1-5.
|
||||
- **Referanse-integritet dekker skills-relative lenker** (`tests/reference-integrity.test.mjs`) — `SKILL.md`s 17 `references/<fil>.md`-lenker og krysslenkene mellom referansefilene valideres nå mot disk, slik at de nye krysslenkene konsolideringen innfører ikke kan råtne stille.
|
||||
- **Testsuite 149 → 163 cases**, alle grønne.
|
||||
- **Testsuite 149 → 167 cases**, alle grønne.
|
||||
- **`okr-sources.md` § 7 «Alternative rammeverk»** — NCT (Narrative, Commitments, Tasks) attribuert til Ravi Mehta / Reforge, med eksplisitt advarsel mot den vanlige feilattribusjonen til *Radical Focus* 2. utg.; Evidence-Based Management → 2024-guiden, der kun det verifiserte «what's new» er gjengitt.
|
||||
- **Vaktutvidelser etter post-implementasjons-review** (`/trekreview`, 2026-07-25): kanon-skanningen i case (a)/(b) bruker nå samme sett som case (d) allerede gjorde — `references/` + `commands/` + `agents/` + `SKILL.md` — slik at etikett-drift i agentene og skillen ikke lenger er usynlig for vakten. Ny case (a2) fanger parallelle confidence-etikettsett skrevet som bullet eller mal-linje (tabell-signaturen alene fanget dem ikke), ny case (k) krever at enhver kommando som instruerer `Glob` også deklarerer det i `allowed-tools`, og en ny versjonssync-case i `tests/package-shape.test.mjs` asserterer at `.claude-plugin/plugin.json`, README-badgen og begge `SKILL.md` bærer samme versjon som `package.json` — tidligere ble kun `package.json` sjekket, altså den ene flaten som er `private: true` og aldri shipper.
|
||||
- **Avveiningsveiledning i kvalitetsrubrikken** for KR som treffer BÅDE Outcome- og Uavhengighet-ankeret (samfunnseffekt-klassen) — tidligere manglet en done-condition for den kollisjonen.
|
||||
|
||||
### Changed
|
||||
|
|
@ -35,6 +36,9 @@ Minor-release **«Én kanon»**: metodekonsolidering av MAJOR-/metode-funnene fr
|
|||
- **NAV manglet som kildeoppslag** i `okr-sources.md` selv om README påstod bruken — lagt til med first-party-kilde (aksel.nav.no), scopet til team-/produktnivå siden etatsnivå-OKR ikke er dokumentert.
|
||||
- **`/okr:freshen-references`-omtalen presisert** (README + CLAUDE.md): kommandoen scorer 16 AV 17 domene-referansefiler — kvalitetsrubrikken er ekskludert fordi den selv er scoringsinstrumentet.
|
||||
- **Feil modulsti i dokumentasjonen**: `lib/innboks-convert.mjs` (CLAUDE.md og 1.7.0-noten under) → `lib/convert/index.mjs`. Modulen har aldri hatt det navnet; konverterings-adapterne har ligget i `lib/convert/` siden 1.7.0.
|
||||
- **SessionStart-nudgen for KR i fare var død under 1.8.0** (`hooks/scripts/coaching-hook.mjs`): status-malen ble i denne releasen skrevet om til den kanoniske skalaen (On Track / At Risk / Off Track), mens hooken fortsatt talte kun «i fare» og «blokkert» — en status-rapport generert under 1.8.0 ga derfor 0 treff og nudgen sluttet stille å utløses. Hooken teller nå de kanoniske etikettene; de norske er beholdt som bakover-kompatibilitet for status-filer skrevet før 1.8.0. Nudge-teksten bruker også kanonisk vokabular.
|
||||
- **F-c-resten i `agents/` og `SKILL.md`**: tre parallelle confidence-etikettsett overlevde konsolideringen — «På sporet / I fare / Blokkert» og «Confidence: [Høy/Medium/Lav]» (en helt annen akse: størrelse, ikke sannsynlighet) i `fremdriftssporer-agent.md`, og «on track / at risk / blocked» i `SKILL.md` (kanonisk er «off track»). Alle tre erstattet med referanse til den kanoniske tabellen. `sporing.md` og agenten den delegerer til svarer nå i samme vokabular.
|
||||
- **Åtte kommandoer instruerte `Glob` uten å deklarere det**: F-i-omskrivingen ga hver kommando en Kontekstbevissthet-blokk som bruker `Glob`, men `allowed-tools` ble kun utvidet i `kaskade.md`. `export.md`, `gap.md`, `governance.md`, `innføring.md`, `kvalitet.md`, `møter.md`, `skriv.md` og `sporing.md` har nå `Glob` i `allowed-tools`.
|
||||
|
||||
## [1.7.1] - 2026-07-17
|
||||
|
||||
|
|
|
|||
|
|
@ -301,7 +301,7 @@ The plugin core is zero-dependency (`node:` builtins only). The inbox-ingestion
|
|||
|
||||
| Version | Date | Highlights |
|
||||
|---------|------|------------|
|
||||
| **1.8.0** | 2026-07-25 | «Én kanon» — metodekonsolidering etter dyp review: én kanonisk confidence-tabell og én kadens-doktrine i `okr-framework.md` med konsument-dedup på tvers av referanser, kommandoer og agenter; score→modenhet-avledningen fjernet; `committed` = forpliktelse OG påvirkbarhet; kvalitetsvurdering ankret i rubrikken (alle 10 dimensjoner, ingen parallelle scorebånd); ekte antipattern-kategorier; milepæl-/binær-unntak; «align, don't cascade» i kaskadeflatene; markedsclaims kildebelagt + nye kilder (NCT, EBM 2024); ny konsistensvakt `tests/canon-consistency.test.mjs` (suite 149 → 163 cases) |
|
||||
| **1.8.0** | 2026-07-25 | «Én kanon» — metodekonsolidering etter dyp review: én kanonisk confidence-tabell og én kadens-doktrine i `okr-framework.md` med konsument-dedup på tvers av referanser, kommandoer og agenter; score→modenhet-avledningen fjernet; `committed` = forpliktelse OG påvirkbarhet; kvalitetsvurdering ankret i rubrikken (alle 10 dimensjoner, ingen parallelle scorebånd); ekte antipattern-kategorier; milepæl-/binær-unntak; «align, don't cascade» i kaskadeflatene; markedsclaims kildebelagt + nye kilder (NCT, EBM 2024); ny konsistensvakt `tests/canon-consistency.test.mjs` (suite 149 → 167 cases) |
|
||||
| **1.7.1** | 2026-07-17 | Patch: restsanering etter dyp review — KB/doc-hygiene med referanse-integritetstest (B1) + ingestion-kode-hygiene (B2): æ/ø-translitterering i slugify, ikke-destruktiv circuit-breaker (merge, config overlever), sanitizeEntry mot bidi/zero-width/Unicode-tag, `--okf-version`-bump, BOM/CRLF-toleranse, presis at-risk-telling, bøyningsformer i topic-guard |
|
||||
| **1.7.0** | 2026-07-17 | Innboks-ingestion: `/okr:innboks` med deterministisk pipeline (convert/split/frontmatter/gate/relasjoner/indeks), per-dokument sikkerhetsgate mot RAG-poisoning, `kilde: innboks`-provenans + untrusted-envelope i retrieval-skillen, 4 exact-pinnede pure-JS-avhengigheter (eneste zero-dep-unntak) |
|
||||
| **1.6.1** | 2026-06-26 | Patch: credibility-sanering etter 360-re-evaluering (C+ → A−) — ASCII-strippet kvalitetsrubrikk omskrevet til korrekt norsk, `okr-second-brain-search` `~`-glob søker nå hjemme-roten, uverifisert kb-search-sitat fjernet, regnefeil i oboard-eksempel rettet |
|
||||
|
|
|
|||
|
|
@ -65,7 +65,9 @@ Score = (Nåværende - Baseline) / (Target - Baseline)
|
|||
|
||||
3. **Vurder confidence**:
|
||||
- Basert på trend og gjenstående tid
|
||||
- På sporet / I fare / Blokkert
|
||||
- Sett nivået fra den **kanoniske confidence-tabellen i `okr-framework.md`**
|
||||
(On Track 🟢 / At Risk 🟡 / Off Track 🔴, sannsynlighet for å nå target).
|
||||
Innfør ingen egne nivåer eller terskler her.
|
||||
|
||||
4. **Generer prognose**:
|
||||
- Gitt nåværende trend, hva blir sluttresultat?
|
||||
|
|
@ -95,7 +97,7 @@ Score = (Nåværende - Baseline) / (Target - Baseline)
|
|||
| KR3: [kort] | X | Y | Z | 0.XX | ↗️/→/↘️ | ✅/⚠️/❌ |
|
||||
|
||||
**Samlet score:** 0.XX
|
||||
**Confidence:** [Høy/Medium/Lav]
|
||||
**Confidence:** [On Track 🟢 | At Risk 🟡 | Off Track 🔴]
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
name: okr:export
|
||||
description: Eksporter OKR-dokumenter (kvalitetsvurdering, gap-matrise, statusrapport, retrospektiv) til print-klar PDF
|
||||
allowed-tools: Read, Bash
|
||||
allowed-tools: Read, Bash, Glob
|
||||
argument-hint: "[dokumenttype eller filsti]"
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
name: okr:gap
|
||||
description: Automatisk gap-analyse mellom tildelingsbrev og gjeldende OKR
|
||||
allowed-tools: Read, AskUserQuestion, Task
|
||||
allowed-tools: Read, AskUserQuestion, Task, Glob
|
||||
argument-hint: "[tildelingsbrev-fil eller tomt for auto-deteksjon]"
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
name: okr:governance
|
||||
description: Koble OKR til tildelingsbrev, politisk styring og Riksrevisjon-krav
|
||||
allowed-tools: Read, AskUserQuestion, Task
|
||||
allowed-tools: Read, AskUserQuestion, Task, Glob
|
||||
argument-hint: "[tildelingsbrev, revisjonsrapport, eller spørsmål]"
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
name: okr:innføring
|
||||
description: Planlegg OKR-innføring, håndter motstand og vurder organisasjonens modenhet
|
||||
allowed-tools: Read, AskUserQuestion, Task
|
||||
allowed-tools: Read, AskUserQuestion, Task, Glob
|
||||
argument-hint: "[fase, utfordring, eller modenhetsvurdering]"
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
name: okr:kvalitet
|
||||
description: Vurder og forbedre eksisterende OKR med kvalitetssjekk og antipattern-deteksjon
|
||||
allowed-tools: Read, AskUserQuestion, Task
|
||||
allowed-tools: Read, AskUserQuestion, Task, Glob
|
||||
argument-hint: "[OKR å vurdere]"
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
name: okr:møter
|
||||
description: Planlegg og fasiliter OKR-møter, workshops og 1:1-samtaler
|
||||
allowed-tools: Read, AskUserQuestion, Task
|
||||
allowed-tools: Read, AskUserQuestion, Task, Glob
|
||||
argument-hint: "[møtetype eller kontekst]"
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
name: okr:skriv
|
||||
description: Skriv nye OKR med veiledning for Objectives og Key Results
|
||||
allowed-tools: Read, AskUserQuestion, Task
|
||||
allowed-tools: Read, AskUserQuestion, Task, Glob
|
||||
argument-hint: "[mål, strategi, eller tildelingsbrev-kontekst]"
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
name: okr:sporing
|
||||
description: Spor OKR-fremgang, beregn score og generer check-in rapporter
|
||||
allowed-tools: Read, AskUserQuestion, Task, ToolSearch
|
||||
allowed-tools: Read, AskUserQuestion, Task, ToolSearch, Glob
|
||||
argument-hint: "[OKR eller tall for oppdatering]"
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -77,10 +77,18 @@ try {
|
|||
// M1/m1 (B2): tell status-MARKERTE tabellrader, ikke raaforekomster --
|
||||
// markoer-ord i forklaringstekst/prosa skal ikke inflatere telleren.
|
||||
// En KR-rad i statusrapporten er en markdown-tabellrad (`| ... |`).
|
||||
//
|
||||
// R1 (1.8.0): status-malen bruker den kanoniske confidence-skalaen fra
|
||||
// okr-framework.md -- On Track / At Risk / Off Track. De to norske
|
||||
// etikettene beholdes som bakover-kompatibilitet for status-filer skrevet
|
||||
// foer 1.8.0. On Track matcher ingen av alternativene og telles ikke.
|
||||
const statusContent = readFileSync(statusPath, 'utf8');
|
||||
atRiskCount = statusContent
|
||||
.split('\n')
|
||||
.filter((line) => /^\s*\|.*\|\s*$/.test(line) && /i fare|blokkert/i.test(line))
|
||||
.filter(
|
||||
(line) => /^\s*\|.*\|\s*$/.test(line)
|
||||
&& /at risk|off track|i fare|blokkert/i.test(line),
|
||||
)
|
||||
.length;
|
||||
} catch { /* skip */ }
|
||||
}
|
||||
|
|
@ -120,7 +128,7 @@ try {
|
|||
parts.push('Midtveis i syklusen — tid for fremdriftssjekk.');
|
||||
parts.push('Anbefalt: /okr:sporing (statusoppdatering og scoring).');
|
||||
if (atRiskCount > 0) {
|
||||
parts.push(`OBS: ${atRiskCount} KR er merket som i fare/blokkert i siste status.`);
|
||||
parts.push(`OBS: ${atRiskCount} KR er merket At Risk/Off Track i siste status.`);
|
||||
}
|
||||
} else if (phase === 'late') {
|
||||
parts.push('Syklusen nærmer seg slutt — fokus på sluttspurt og forberedelse.');
|
||||
|
|
@ -129,7 +137,7 @@ try {
|
|||
parts.push('Mindre enn 2 uker igjen. Vurder /okr:oppsett arkiver for retrospektiv.');
|
||||
}
|
||||
if (atRiskCount > 0) {
|
||||
parts.push(`OBS: ${atRiskCount} KR er i fare — vurder tiltak eller juster forventninger.`);
|
||||
parts.push(`OBS: ${atRiskCount} KR er At Risk/Off Track — vurder tiltak eller juster forventninger.`);
|
||||
}
|
||||
} else {
|
||||
// between cycles
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ When users present existing OKR, evaluate against these criteria and provide con
|
|||
To help update OKR status:
|
||||
- Collect current numbers for each KR
|
||||
- Calculate progression (0.0-1.0 scale): 0.7 = expected for aspirational, 0.6-0.7 = sweet spot (Google/Doerr), <0.5 = needs intervention
|
||||
- Assess status: on track / at risk / blocked
|
||||
- Assess status using the canonical three-level scale in `references/okr-framework.md` — on track / at risk / off track — and introduce no parallel scale here
|
||||
- Suggest corrective actions and generate update text for meetings/reports
|
||||
|
||||
### 4. Cascade OKR
|
||||
|
|
|
|||
|
|
@ -31,6 +31,16 @@ function mdFiles(dir) {
|
|||
.map((n) => join(dir, n));
|
||||
}
|
||||
|
||||
const SKILL = 'skills/okr-offentlig-sektor/SKILL.md';
|
||||
|
||||
// R4 (review.md 231c53fc): kanon-skanningen skal dekke ALLE konsumentflater --
|
||||
// references/, commands/, agents/ OG SKILL.md (som ligger ETT nivaa over references/).
|
||||
// Case (d) brukte allerede dette settet; (a)/(b) var smalere enn sine egne case-navn
|
||||
// lovet, saa etikett-drift i agents/ + SKILL.md var usynlig. Ett felles sett, ett sted.
|
||||
function canonScan() {
|
||||
return [...mdFiles(REF), ...mdFiles('commands'), ...mdFiles('agents'), SKILL];
|
||||
}
|
||||
|
||||
// Kontig. pipe-tabell-blokker: hver blokk = sammenhengende linjer som (trimmet) starter med '|'.
|
||||
function mdTables(body) {
|
||||
const tables = [];
|
||||
|
|
@ -66,6 +76,36 @@ function isConfidenceTable(tableLines) {
|
|||
return /on track/i.test(txt) && /at risk/i.test(txt) && /off track/i.test(txt);
|
||||
}
|
||||
|
||||
// --- F-c: divergerende etikettSETT (R4 / review.md ccff16e1) ---
|
||||
// Et parallelt etikettsett gjenkjennes STRUKTURELT som en skraastrek-enumerasjon av
|
||||
// statusetiketter -- ikke som loepende prosa. "For KR i fare" og "Blokkert av eksterne
|
||||
// faktorer" er legitim prosa; det doktrinen forbyr er den parallelle SKALAEN
|
||||
// ("Paa sporet / I fare / Blokkert"). Kanoniske etiketter: okr-framework.md:389-392.
|
||||
const CANON_LABELS = ['on track', 'at risk', 'off track'];
|
||||
const DIVERGENT_LABELS = ['p\u00e5 sporet', 'i fare', 'blokkert', 'blocked'];
|
||||
// Magnitude-aksen (Hoey/Medium/Lav) er en ANNEN akse enn sannsynlighet. Kun flagget naar
|
||||
// linjen faktisk snakker om confidence -- en prioritetsskala med samme ord er legitim.
|
||||
const MAGNITUDE_SCALE = /(h\u00f8y|medium|lav)\s*\/\s*(h\u00f8y|medium|lav)/i;
|
||||
|
||||
// Segmenter mellom skraastreker; et segment "er" en etikett naar det (etter stripping av
|
||||
// listemarkoer, utheving, klammer og emoji-hale) starter/slutter paa etiketten.
|
||||
function labelSegments(line) {
|
||||
return line
|
||||
.split('/')
|
||||
.map((s) => s.replace(/[*_`[\]()]/g, '').replace(/^[\s\-+]*(?:\d+\.)?\s*/, '').trim().toLowerCase())
|
||||
.map((s) => {
|
||||
const all = [...CANON_LABELS, ...DIVERGENT_LABELS];
|
||||
return all.find((l) => s === l || s.startsWith(`${l} `) || s.endsWith(` ${l}`)) ?? null;
|
||||
});
|
||||
}
|
||||
|
||||
// Returnerer de divergerende etikettene i en enumerasjon (>= 2 etikett-segmenter), ellers [].
|
||||
function divergentEnumeration(line) {
|
||||
const hits = labelSegments(line).filter(Boolean);
|
||||
if (hits.length < 2) return [];
|
||||
return hits.filter((h) => DIVERGENT_LABELS.includes(h));
|
||||
}
|
||||
|
||||
// ==================== parser-sanity (jf. B1) ====================
|
||||
|
||||
test('parser-sanity: headingsOf finner 10 rubrikk-dimensjoner', () => {
|
||||
|
|
@ -86,10 +126,10 @@ test('parser-sanity: linesMatching finner check-in-linjer i okr-framework.md', (
|
|||
// ==================== F-a..F-i konsistensvakt ====================
|
||||
|
||||
// (a) F-c confidence EN gang. RED til calculator-dedup (Step 6).
|
||||
// R4: skanne-settet utvidet fra references/+commands/ til canonScan() (som case (d)).
|
||||
test('(a) F-c: noeyaktig EN confidence-tabell, kanonisk i okr-framework.md', () => {
|
||||
const scan = [...mdFiles(REF), ...mdFiles('commands')];
|
||||
const found = [];
|
||||
for (const f of scan) {
|
||||
for (const f of canonScan()) {
|
||||
if (mdTables(readDoc(f)).some(isConfidenceTable)) found.push(f);
|
||||
}
|
||||
assert.deepEqual(
|
||||
|
|
@ -99,10 +139,34 @@ test('(a) F-c: noeyaktig EN confidence-tabell, kanonisk i okr-framework.md', ()
|
|||
);
|
||||
});
|
||||
|
||||
// (a2) F-c divergerende etikettSETT (R4 / review.md ccff16e1). En tabell-signatur alene
|
||||
// fanger ikke parallelle skalaer skrevet som bullet/mal-linje -- det var nettopp formen
|
||||
// driften overlevde i (agents/ + SKILL.md).
|
||||
test('(a2) F-c: ingen parallelle confidence-etikettsett utenfor kanon', () => {
|
||||
const violations = [];
|
||||
for (const f of canonScan()) {
|
||||
const body = readDoc(f);
|
||||
body.split('\n').forEach((line, i) => {
|
||||
const divergent = divergentEnumeration(line);
|
||||
if (divergent.length > 0) {
|
||||
violations.push(`${f}:${i + 1}: divergerende etikettsett (${divergent.join(', ')}): ${line.trim()}`);
|
||||
}
|
||||
if (/confidence/i.test(line) && MAGNITUDE_SCALE.test(line)) {
|
||||
violations.push(`${f}:${i + 1}: magnitude-skala paa confidence-aksen: ${line.trim()}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
assert.deepEqual(
|
||||
violations,
|
||||
[],
|
||||
`kanonisk sett = On Track / At Risk / Off Track (okr-framework.md:389-392):\n${violations.join('\n')}`,
|
||||
);
|
||||
});
|
||||
|
||||
// (b) F-d kadens strukturell (kadens x publikum). RED til kadens-konsumenter (Step 11-12).
|
||||
// Team er default; publikum-markoer ledelse/ledergruppe hever til ledelses-review.
|
||||
test('(b) F-d: ingen team-check-in maanedlig, ingen ledelsesreview ukentlig', () => {
|
||||
const scan = [...mdFiles(REF), ...mdFiles('commands')];
|
||||
const scan = canonScan(); // R4: utvidet fra references/+commands/
|
||||
// Kadens-adjektiv DIREKTE foran check-in (ev. via okr/team-kvalifikator) = team-maanedlig.
|
||||
// Strukturell: fanger IKKE "maanedlig 30-min review" (korrekt dobbeltrytme i implementation.md)
|
||||
// eller "Maanedlig status | Oboard check-ins" (governance-tabell) — kun kadens->check-in-binding.
|
||||
|
|
@ -137,12 +201,7 @@ test('(d) F-g: prosa-omtaler av antipattern-antall == dynamisk telling (drift-la
|
|||
const categoryCount = linesMatching(anti, /^## \d+\./).length;
|
||||
assert.ok(antipatternCount > 0, `parser-sanity: fant ${antipatternCount} antipatterns`);
|
||||
assert.ok(categoryCount > 0, `parser-sanity: fant ${categoryCount} kategorier`);
|
||||
const scan = [
|
||||
...mdFiles(REF),
|
||||
...mdFiles('commands'),
|
||||
...mdFiles('agents'),
|
||||
'skills/okr-offentlig-sektor/SKILL.md',
|
||||
];
|
||||
const scan = canonScan();
|
||||
const drift = [];
|
||||
for (const f of scan) {
|
||||
for (const m of readDoc(f).matchAll(/(\d+)\s+antipatterns\b/gi)) {
|
||||
|
|
@ -225,3 +284,34 @@ test('(j) score-grenser: okr-calculator dokumenterer kapp [0,1.0] OG div-paa-nul
|
|||
if (!hasDivNull) problems.push('mangler div-paa-null-regel (Target==Baseline / 0 maalbare KR -> udefinert)');
|
||||
assert.deepEqual(problems, [], `score-grenser doc-invariant:\n${problems.join('\n')}`);
|
||||
});
|
||||
|
||||
// (k) R2 (review.md 7ec575be): F-i-omskrivingen ga hver kommando en Kontekstbevissthet-blokk
|
||||
// som INSTRUERER `Glob`, men allowed-tools ble ikke utvidet tilsvarende -- et direktiv
|
||||
// kommandoen ikke kan utfoere. Strukturell invariant: nevner BODY verktoeyet, maa
|
||||
// frontmatter deklarere det. Case (i) grepper kun etter fjernede fraser og fanger ikke dette.
|
||||
function frontmatterAndBody(rel) {
|
||||
const raw = readDoc(rel);
|
||||
const m = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(raw);
|
||||
return m ? { fm: m[1], body: m[2] } : { fm: '', body: raw };
|
||||
}
|
||||
|
||||
test('(k) R2: kommandoer som instruerer Glob deklarerer Glob i allowed-tools', () => {
|
||||
const missing = [];
|
||||
let instructing = 0;
|
||||
for (const f of mdFiles('commands')) {
|
||||
const { fm, body } = frontmatterAndBody(f);
|
||||
if (!/\bGlob\b/.test(body)) continue;
|
||||
instructing += 1;
|
||||
const declared = /^allowed-tools:\s*(.+)$/m.exec(fm);
|
||||
const tools = (declared ? declared[1] : '').split(',').map((t) => t.trim());
|
||||
if (!tools.includes('Glob')) {
|
||||
missing.push(`${f}: allowed-tools = ${declared ? declared[1].trim() : '(mangler)'}`);
|
||||
}
|
||||
}
|
||||
assert.ok(instructing >= 10, `parser-sanity: fant ${instructing} Glob-instruerende kommandoer`);
|
||||
assert.deepEqual(
|
||||
missing,
|
||||
[],
|
||||
`Glob instruert i body uten dekning i allowed-tools:\n${missing.join('\n')}`,
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -115,6 +115,39 @@ test('at-risk telles per status-RAD (tabell), ikke raaforekomster i prosa (B2/M1
|
|||
});
|
||||
});
|
||||
|
||||
// R1 (review.md af16d5e4): 1.8.0 skrev om status-malen (commands/sporing.md:86-88) til den
|
||||
// KANONISKE confidence-skalaen (okr-framework.md:389-392) — On Track / At Risk / Off Track.
|
||||
// Hooken talte fortsatt kun det gamle norske vokabularet, saa nudgen doede stille under 1.8.0.
|
||||
// Denne casen mater malen slik den faktisk genereres i dag; casen over beholder det gamle
|
||||
// vokabularet og daekker dermed bakover-kompatibilitet for arkiverte status-filer.
|
||||
test('at-risk telles paa kanonisk 1.8.0-vokabular (At Risk + Off Track, ikke On Track)', () => {
|
||||
withWork((work) => {
|
||||
writeConfig(work, '---\nid: "T2-2026"\n---\n');
|
||||
const statusDir = join(work, '.claude', 'okr', 'syklus', 'T2-2026');
|
||||
mkdirSync(statusDir, { recursive: true });
|
||||
// Emoji som \u-escapes: test-kilden holdes ASCII-ren (bash 3.2 set -u multibyte).
|
||||
const GUL = '\u{1F7E1}';
|
||||
const GROENN = '\u{1F7E2}';
|
||||
const ROED = '\u{1F534}';
|
||||
writeFileSync(join(statusDir, 'status.md'), [
|
||||
'# Status T2-2026',
|
||||
'',
|
||||
'| KR | Baseline | Target | Naa | Score | Status |',
|
||||
'|----|----------|--------|-----|-------|--------|',
|
||||
`| KR1: Redusere ulykker | 40 | 30 | 35 | 0.50 | At Risk ${GUL} |`,
|
||||
`| KR2: Fartshumper installert | 0% | 100% | 60% | 0.60 | On Track ${GROENN} |`,
|
||||
`| KR3: Foreldre-tilfredshet | 60% | 90% | 65% | 0.17 | Off Track ${ROED} |`,
|
||||
'',
|
||||
].join('\n'));
|
||||
const out = runHook(work, '2026-06-20');
|
||||
assert.match(
|
||||
out,
|
||||
/OBS: 2 KR er merket/,
|
||||
'At Risk + Off Track teller (2); On Track skal IKKE telle',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('OKR_NOW sen fase: late-coaching', () => {
|
||||
withWork((work) => {
|
||||
writeConfig(work, '---\nid: "T2-2026"\n---\n');
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// Step 10 (A1): package.json-kontrakten for det bevisste zero-dep-bruddet.
|
||||
// Verifiserer at dep-laget er EXACT-pinnet (ingen ^/~/*), at engines-gulvet er
|
||||
// satt (unpdf krever node >= 22), at pakken er ESM (type: module), at versjonen
|
||||
// er 1.8.0 (minor-lane C: En kanon), og at .npmrc slaar av
|
||||
// er 1.8.0 (minor-lane C: En kanon) paa ALLE shippede flater, og at .npmrc slaar av
|
||||
// install-scripts (Shai-Hulud / supply-chain). Zero npm deps i selve testen.
|
||||
// Moenster: tests/frontmatter.test.mjs (les fil, assert struktur).
|
||||
|
||||
|
|
@ -67,3 +67,28 @@ test('package-lock.json: finnes og pinner transitive deps med integrity', () =>
|
|||
assert.match(meta.version ?? '', /^\d/, `lockfile-oppfoering uten versjon: ${name}`);
|
||||
}
|
||||
});
|
||||
|
||||
// R3 (review.md 5e61ae0d): package.json er `private: true` og shipper ALDRI -- versjons-
|
||||
// assertet over voktet dermed den ene flaten brukeren aldri ser. Polyrepo-ritualet
|
||||
// (katalog-ref pinnet til release-tag) forutsetter at alle flater bumpes SAMTIDIG, saa en
|
||||
// delvis bump skal bli ROED. Forventet versjon utledes fra package.json (ett sted aa endre).
|
||||
const VERSION_SURFACES = [
|
||||
{ file: '.claude-plugin/plugin.json', re: /"version":\s*"([^"]+)"/ },
|
||||
{ file: 'README.md', re: /img\.shields\.io\/badge\/version-(\d+\.\d+\.\d+)-/ },
|
||||
{ file: 'skills/okr-offentlig-sektor/SKILL.md', re: /^version:\s*"?([^"\s]+)"?\s*$/m },
|
||||
{ file: 'skills/okr-second-brain-search/SKILL.md', re: /^version:\s*"?([^"\s]+)"?\s*$/m },
|
||||
];
|
||||
|
||||
test('versjonssync: alle shippede flater baerer package.json-versjonen', () => {
|
||||
const expected = readPkg().version;
|
||||
const drift = [];
|
||||
for (const { file, re } of VERSION_SURFACES) {
|
||||
const m = re.exec(readFileSync(join(ROOT, file), 'utf8'));
|
||||
if (!m) {
|
||||
drift.push(`${file}: fant ingen versjon (flaten flyttet? oppdater regexen)`);
|
||||
continue;
|
||||
}
|
||||
if (m[1] !== expected) drift.push(`${file}: ${m[1]} != ${expected}`);
|
||||
}
|
||||
assert.deepEqual(drift, [], `delvis versjonsbump (forventet ${expected}):\n${drift.join('\n')}`);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue