feat(okr): 1.8.1 okf_version/okf_layout-splitt (OKF-spec 12)
Rot-index.md bar en markoer som dekket to urelaterte konsepter: den upstream
OKF-versjonen bundelen sikter mot OG pluginens egen layout-revisjon. Specen
(catalog/docs/okf-second-brain/spec.md 12, log.md 2026-07-23) skiller dem i to
markoerer. Denne releasen migrerer emitteren og sjekkeren over.
- okf-index.mjs: emitterer okf_version: 0.1 (upstream, paakrevd per 3) +
okf_layout: kb-layout-2026-06 (vaar revisjon, valgfri per 12). Ny konstant
OKF_LAYOUT; OKF_VERSION baerer naa upstream-verdien. Begge rot-eksklusive.
- Migrasjonssti: en ikke-upstream verdi i okf_version FLYTTES verbatim til
okf_layout ved neste kjoering. Spec-konform okf_version roeres aldri;
eksisterende okf_layout bevares. Byte-idempotent.
- okf-check.mjs: ekkoer begge markoerene (fravaerende -> MANGLER). Rent ekko,
ingen ny haandheving -- spec 3 er ikke haandhevende paa form ennaa.
- CLI: --okf-layout er kanon. --okf-version beholdt som deprecated alias
(verdien var alltid en layout-revisjon) m/ varsel til stderr, ALDRI stdout.
Samme aliasing for generateIndexes({ okfLayout }) mot { okfVersion }.
- Doc-flater (CLAUDE.md, second-brain SKILL.md, commands/oppsett.md) beskriver
naa to markoerer der de beskrev en.
Suite 167 -> 179, alle groenne. Versjonssync 1.8.1 over alle shippede flater.
Laaser opp catalog + llm-ingestion-okf, som begge ventet paa denne migrasjonen.
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
44ceec7205
commit
0059da7bef
16 changed files with 333 additions and 59 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "okr",
|
||||
"version": "1.8.0",
|
||||
"version": "1.8.1",
|
||||
"description": "Expert OKR guidance for Norwegian public sector. Write, review, cascade, track and govern OKR based on Google/Doerr methodology adapted for 4-month tertial cycles.",
|
||||
"author": {
|
||||
"name": "Kjell Tore Guttormsen"
|
||||
|
|
|
|||
18
CHANGELOG.md
18
CHANGELOG.md
|
|
@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.8.1] - 2026-07-25
|
||||
|
||||
Patch-release: **`okf_version` / `okf_layout`-splitt**. Rot-`index.md` bar én markør som dekket to urelaterte konsepter — den upstream OKF-versjonen bundelen sikter mot *og* pluginens egen layout-revisjon. OKF-specen §12 (katalog-eid) skiller dem i to markører; denne releasen migrerer emitteren og sjekkeren over. Ingen nye kommandoer, agenter eller referansefiler.
|
||||
|
||||
### Changed
|
||||
- **`scripts/okf-index.mjs` emitterer to markør-linjer på rot-`index.md`** — `okf_version: 0.1` (upstream Google OKF-versjonen, verdisett eid av Google, påkrevd per spec §3) og `okf_layout: kb-layout-2026-06` (pluginens egen layout-revisjon, valgfri per §12, verdisett eid av emitteren; trigger ingen kryss-plugin-rekjekk). Konstanten `OKF_VERSION` bærer nå upstream-verdien; ny `OKF_LAYOUT` bærer layout-revisjonen. Undernivå-index er uendret — begge markørene er rot-eksklusive.
|
||||
- **`scripts/okf-check.mjs` ekkoer begge markørene** (`okf_version` + `okf_layout`, fraværende → `MANGLER`). Rent ekko: verdiene valideres fortsatt ikke, siden spec §3 ennå ikke er håndhevende på form. Retur-objektet fra `checkBundle()` bærer `okfLayout` ved siden av `okfVersion`.
|
||||
- **`--okf-layout <ver>` er det kanoniske CLI-flagget** for å bumpe layout-revisjonen. `--okf-version` beholdes som **deprecated alias** — verdien kallere sendte inn der var alltid en layout-revisjon — og skriver et deprecation-varsel til **stderr**, aldri stdout (scriptets maskinlesbare flate). Samme aliasing gjelder `generateIndexes(root, { okfLayout })` mot det gamle `{ okfVersion }`. Manglende flagg-verdi er fortsatt bruksfeil (exit 2, ingen skriving).
|
||||
|
||||
### Fixed
|
||||
- **Migrasjonssti for eksisterende bundles** — en rot-`index.md` som bærer en ikke-upstream verdi i `okf_version` (formen fra før 1.8.1) får verdien **flyttet verbatim** til `okf_layout`, og `okf_version` settes til upstream-verdien. Den funne verdien erstattes aldri av vår egen konstant, en allerede spec-konform `okf_version` (`0.1`, `0.2`, …) røres aldri, og en eksisterende `okf_layout` bevares. Migrasjonen skjer ved neste `okf-index`-kjøring; ingen manuell handling kreves.
|
||||
|
||||
### Added
|
||||
- **Testsuite 167 → 179 cases**, alle grønne. 12 nye i `tests/okf-check.test.mjs`: begge markørene på rot og ingen på undernivå (inkl. at markør-blokken ikke etterlater en tom linje der), migrasjon av legacy-form, verbatim-bevaring av en *fremmed* layout-verdi, ikke-migrering av spec-konform `okf_version`, verbatim-bevaring av allerede splittet form, `--okf-layout`-bump + idempotent vedlikehold, deprecated-alias-atferd, stderr-vs-stdout-separasjon for deprecation-varselet, bruksfeil ved manglende flagg-verdi, og begge markørene i `okf-check`-ekkoet inkludert `MANGLER` for utelatt layout.
|
||||
|
||||
### Documentation
|
||||
- `CLAUDE.md`, `skills/okr-second-brain-search/SKILL.md` og `commands/oppsett.md` beskriver nå to markører der de tidligere beskrev én.
|
||||
|
||||
## [1.8.0] - 2026-07-25
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# OKR Offentlig Sektor v1.8.0
|
||||
# OKR Offentlig Sektor v1.8.1
|
||||
|
||||
Expert OKR guidance for Norwegian public sector. Google/Doerr methodology adapted for 4-month tertial cycles.
|
||||
|
||||
|
|
@ -64,11 +64,11 @@ Cycle archival: `/okr:oppsett arkiver` — moves `syklus/` to `historikk/`, gene
|
|||
|
||||
Context files carry OKF v0.1 Documents-style frontmatter (conforms to the OKF v0.1 minimal contract, canonized in `catalog/docs/okf-second-brain/spec.md`): a Title-Case `type` (`Organisasjonsprofil`, `Tildelingsbrev`, `Virksomhetsplan`, `Overordnede OKR`, `OKR`, `Retrospektiv`, `Status`) plus recommended `resource`/`title`/`description`/`tags`/`timestamp`. Only `type` is de-facto required. The flat frontmatter parser reads only single-line keys (`type`, profile/config fields); tree files may carry multi-line `tags` lists without breaking it. Files written by inbox ingestion additionally carry the provenance extension key `kilde: innboks`.
|
||||
|
||||
**Two bundle roots** with distinct lifecycles: project `.claude/okr/` (cwd-bound cycle/work) and home `~/.claude/okr/org/` (reinstall-surviving org identity). Each root carries its own `index.md` (no frontmatter; `# H1` + `* [title](link) - description`) and an `okf_version` marker. `okf-index`/`okf-check` run per root; retrieval Globs both (project preferred, else home).
|
||||
**Two bundle roots** with distinct lifecycles: project `.claude/okr/` (cwd-bound cycle/work) and home `~/.claude/okr/org/` (reinstall-surviving org identity). Each root carries its own `index.md` (no frontmatter; `# H1` + `* [title](link) - description`) and, on the root index, two distinct markers (OKF spec §12): `okf_version` (the upstream OKF version targeted, currently `0.1`) and `okf_layout` (this plugin's own layout revision, currently `kb-layout-2026-06`). `okf-index`/`okf-check` run per root; retrieval Globs both (project preferred, else home).
|
||||
|
||||
- `lib/frontmatter.mjs` — shared frontmatter parser/writer; both hooks route through it (strips trailing ` #…` only from unquoted values).
|
||||
- `scripts/okf-index.mjs` — regenerate a root's per-level `index.md` (verbatim OKF index format).
|
||||
- `scripts/okf-check.mjs` — validate each concept file carries `type:` (exit 1 + count otherwise); report `okf_version` per root.
|
||||
- `scripts/okf-check.mjs` — validate each concept file carries `type:` (exit 1 + count otherwise); echo `okf_version` + `okf_layout` per root (pure echo, no value validation).
|
||||
- `scripts/compose-org-profile.mjs` — build the nested org-profile YAML with top-level OKF keys (`type`/`resource`/`timestamp`) before piping to `write-org-profile.mjs`.
|
||||
- `scripts/innboks-ingest.mjs` — inbox-ingestion orchestrator (`/okr:innboks`): walks `.claude/okr/innboks/`, converts (txt/md/docx/eml/pdf via `lib/convert/index.mjs`), heading-splits, stamps OKF frontmatter + `kilde: innboks`, routes by type, then gates each document with `okf-check --strict-ingest` — a failing document is discarded alone (staging + per-document rollback), survivors get relations and index entries. Non-destructive (originals stay in the inbox), idempotent by construction (timestamp = source mtime), path-confined (realpath). Binary formats need `npm install --ignore-scripts` (4 exact-pinned pure-JS deps); txt/md run dependency-free.
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
*AI-generated: all code produced by Claude Code through dialog-driven development. [Full disclosure →](../../README.md#ai-generated-code-disclosure)*
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
|
@ -301,6 +301,7 @@ The plugin core is zero-dependency (`node:` builtins only). The inbox-ingestion
|
|||
|
||||
| Version | Date | Highlights |
|
||||
|---------|------|------------|
|
||||
| **1.8.1** | 2026-07-25 | Patch: `okf_version`/`okf_layout`-splitt (OKF-spec §12) — rot-`index.md` bærer nå to distinkte markører i stedet for én som bar to urelaterte konsepter: `okf_version` = upstream OKF-versjonen (`0.1`, verdisett eid av Google), `okf_layout` = pluginens egen layout-revisjon (`kb-layout-2026-06`). Automatisk migrasjonssti for eksisterende bundles (layout-verdi i `okf_version` flyttes verbatim); `okf-check` ekkoer begge; `--okf-layout` erstatter `--okf-version` (beholdt som deprecated alias m/ stderr-varsel) |
|
||||
| **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) |
|
||||
|
|
|
|||
|
|
@ -180,7 +180,8 @@ node ${CLAUDE_PLUGIN_ROOT}/scripts/okf-index.mjs .claude/okr
|
|||
```
|
||||
|
||||
(Idempotent: bygger `index.md`-entries fra konsept-filenes `title`/`description`,
|
||||
bevarer menneske-skrevne overskrifter og rotens `okf_version`.)
|
||||
bevarer menneske-skrevne overskrifter og rotens markører `okf_version` +
|
||||
`okf_layout`.)
|
||||
|
||||
### Fase 4 — Struktur (3 min)
|
||||
|
||||
|
|
|
|||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "okr-offentlig-sektor",
|
||||
"version": "1.8.0",
|
||||
"version": "1.8.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "okr-offentlig-sektor",
|
||||
"version": "1.8.0",
|
||||
"version": "1.8.1",
|
||||
"dependencies": {
|
||||
"mammoth": "1.12.0",
|
||||
"postal-mime": "2.7.5",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "okr-offentlig-sektor",
|
||||
"version": "1.8.0",
|
||||
"version": "1.8.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "OKR-plugin for norsk offentlig sektor. Pure-JS konverterings-deps for innboks-ingestion (bevisst zero-dep-brudd, se README).",
|
||||
|
|
|
|||
|
|
@ -5,8 +5,10 @@
|
|||
// - >= 1 fil uten type -> exit 1 + teller + navngir filene.
|
||||
// - 0 filer uten type -> exit 0.
|
||||
// Anbefalte felt (resource/title/description/timestamp) rapporteres som ADVARSEL,
|
||||
// ikke feil. Rotens `okf_version` ekkoes for menneskelig sammenligning mot
|
||||
// gjeldende standard (ingen auto-fetch — hooks/scripts er no-network; SC7 myket).
|
||||
// ikke feil. Rotens to markoerer (`okf_version` = upstream OKF-versjon, spec §3;
|
||||
// `okf_layout` = emitterens egen layout-revisjon, spec §12) ekkoes for menneskelig
|
||||
// sammenligning mot gjeldende standard (ingen auto-fetch — hooks/scripts er
|
||||
// no-network; SC7 myket).
|
||||
//
|
||||
// Kjoeres PER ROT (prosjekt `.claude/okr/` + home `~/.claude/okr/org/`).
|
||||
// Zero npm dependencies (node:-builtins).
|
||||
|
|
@ -55,12 +57,19 @@ function walkConcepts(root) {
|
|||
return out;
|
||||
}
|
||||
|
||||
// Les rotens okf_version (markdown-tekst i index.md, ikke frontmatter). null hvis fravaerende.
|
||||
function rootOkfVersion(root) {
|
||||
// Les rotens to markoerer (markdown-tekst i index.md, ikke frontmatter).
|
||||
// `okf_version` = upstream OKF-versjon (spec §3), `okf_layout` = emitterens egen
|
||||
// layout-revisjon (spec §12, valgfri). Fravaerende markoer -> null. Ren ekko:
|
||||
// verdiene valideres ikke (spec §3 er ikke haandhevende paa form ennaa).
|
||||
function rootMarkers(root) {
|
||||
const idx = join(root, 'index.md');
|
||||
if (!existsSync(idx)) return null;
|
||||
const m = readFileSync(idx, 'utf8').match(/^okf_version:\s*(.+)$/m);
|
||||
return m ? m[1].trim() : null;
|
||||
if (!existsSync(idx)) return { okfVersion: null, okfLayout: null };
|
||||
const raw = readFileSync(idx, 'utf8');
|
||||
const pick = (key) => {
|
||||
const m = raw.match(new RegExp(`^${key}:\\s*(.+)$`, 'm'));
|
||||
return m ? m[1].trim() : null;
|
||||
};
|
||||
return { okfVersion: pick('okf_version'), okfLayout: pick('okf_layout') };
|
||||
}
|
||||
|
||||
// strictIngest (default AV): paa skrivestien handheves det lukkede vokabularet
|
||||
|
|
@ -117,7 +126,7 @@ export function checkBundle(root, { strictIngest = false, files } = {}) {
|
|||
warnings,
|
||||
strictErrors,
|
||||
strictIngest,
|
||||
okfVersion: rootOkfVersion(root),
|
||||
...rootMarkers(root),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -147,6 +156,7 @@ if (isMain) {
|
|||
for (const e of r.strictErrors) out.push(` x ${e}`);
|
||||
}
|
||||
out.push(` okf_version: ${r.okfVersion || 'MANGLER (rot-index uten okf_version)'}`);
|
||||
out.push(` okf_layout: ${r.okfLayout || 'MANGLER (valgfri markoer)'}`);
|
||||
out.push(` Advarsler (anbefalte felt): ${r.warnings.length}`);
|
||||
for (const w of r.warnings) out.push(` ! ${w}`);
|
||||
const failed = r.missingType.length > 0 || (strictIngest && r.strictErrors.length > 0);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@
|
|||
// Layout»-index-form:
|
||||
// # Overskrift
|
||||
//
|
||||
// okf_version: <ver> (KUN rot-index)
|
||||
// okf_version: <upstream> (KUN rot-index -- OKF-versjonen bundelen sikter mot)
|
||||
// okf_layout: <revisjon> (KUN rot-index -- vaar egen layout-revisjon, valgfri)
|
||||
//
|
||||
// * [title](relativ.md) - description
|
||||
// Ingen frontmatter paa index.md (OKF-reservert). Konsept-filers `title`/
|
||||
|
|
@ -13,7 +14,7 @@
|
|||
// sin egen index.md. Kjoeres PER ROT (de to bundlene har ulik livssyklus).
|
||||
//
|
||||
// Idempotens / vedlikehold (NFR): en eksisterende index.md sin `# overskrift`,
|
||||
// rotens `okf_version`-verdi, og menneske-skrevne beskrivelser for underkatalog-
|
||||
// rotens markoer-verdier, og menneske-skrevne beskrivelser for underkatalog-
|
||||
// pekere bevares; konsept-entries regenereres alltid fra frontmatter (autoritativ
|
||||
// kilde). Skriving er atomisk (temp + renameSync), jf. write-org-profile.mjs.
|
||||
//
|
||||
|
|
@ -24,7 +25,18 @@ import { join, basename } from 'node:path';
|
|||
import { fileURLToPath } from 'node:url';
|
||||
import { parseFrontmatter } from '../lib/frontmatter.mjs';
|
||||
|
||||
export const OKF_VERSION = 'kb-layout-2026-06';
|
||||
// To distinkte markoerer (OKF-spec §12) -- ett felt skal ikke baere to urelaterte
|
||||
// konsepter. `okf_version` = upstream Google OKF-versjonen bundelen sikter mot
|
||||
// (verdisett eid av Google, enkeltverdi; paakrevd per §3). `okf_layout` = VAAR
|
||||
// egen layout-revisjon (valgfri; verdisett eid av emitteren, trigger ingen
|
||||
// kryss-plugin-rekjekk). Foer 1.8.1 baar `okf_version` layout-verdien alene --
|
||||
// se resolveMarkers() for migrasjonsstien.
|
||||
export const OKF_VERSION = '0.1';
|
||||
export const OKF_LAYOUT = 'kb-layout-2026-06';
|
||||
|
||||
// Upstream-versjoner er numerisk punktnotasjon (`0.1`). Alt annet i et
|
||||
// `okf_version`-felt er en layout-verdi fra foer splitten.
|
||||
const UPSTREAM_VERSION_RE = /^\d+(?:\.\d+)+$/;
|
||||
|
||||
// Drop-zone (raa innboks-filer) + skjulte kataloger (.cache osv.) er ikke nivaaer:
|
||||
// de skal verken faa egen index.md eller listes som underkatalog-peker. Maa
|
||||
|
|
@ -39,10 +51,12 @@ function titleFromName(name) {
|
|||
return spaced.charAt(0).toUpperCase() + spaced.slice(1);
|
||||
}
|
||||
|
||||
// Parse en eksisterende index.md for bevaring: overskrift, okf_version, og
|
||||
// beskrivelser pr. lenke (for underkatalog-pekere). Kaster aldri.
|
||||
// Parse en eksisterende index.md for bevaring: overskrift, begge rot-markoerene,
|
||||
// og beskrivelser pr. lenke (for underkatalog-pekere). Kaster aldri.
|
||||
function parseExistingIndex(path) {
|
||||
const result = { heading: null, okfVersion: null, descByLink: {} };
|
||||
const result = {
|
||||
heading: null, okfVersion: null, okfLayout: null, descByLink: {},
|
||||
};
|
||||
if (!existsSync(path)) return result;
|
||||
for (const line of readFileSync(path, 'utf8').split('\n')) {
|
||||
if (result.heading === null && line.startsWith('# ')) {
|
||||
|
|
@ -50,12 +64,33 @@ function parseExistingIndex(path) {
|
|||
}
|
||||
const ver = line.match(/^okf_version:\s*(.+)$/);
|
||||
if (ver) result.okfVersion = ver[1].trim();
|
||||
const lay = line.match(/^okf_layout:\s*(.+)$/);
|
||||
if (lay) result.okfLayout = lay[1].trim();
|
||||
const entry = line.match(/^\*\s*\[([^\]]*)\]\(([^)]+)\)(?:\s*-\s*(.*))?$/);
|
||||
if (entry) result.descByLink[entry[2]] = { title: entry[1], desc: (entry[3] || '').trim() };
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Avgjoer de to rot-markoerene fra en eksisterende index + en evt. eksplisitt
|
||||
// layout. MIGRASJONSSTI (1.8.1): baerer `okf_version` en ikke-upstream verdi og
|
||||
// `okf_layout` mangler, FLYTTES den funne verdien til `okf_layout` (verbatim --
|
||||
// ikke erstattet av vaar konstant) og `okf_version` settes til upstream-verdien.
|
||||
// En allerede spec-konform `okf_version` (0.1, 0.2, ...) roeres aldri, og en
|
||||
// eksisterende `okf_layout` bevares (idempotent vedlikehold).
|
||||
function resolveMarkers(existing, explicitLayout) {
|
||||
let version = existing.okfVersion;
|
||||
let layout = existing.okfLayout;
|
||||
if (layout === null && version !== null && !UPSTREAM_VERSION_RE.test(version)) {
|
||||
layout = version;
|
||||
version = null;
|
||||
}
|
||||
return {
|
||||
version: version || OKF_VERSION,
|
||||
layout: explicitLayout || layout || OKF_LAYOUT,
|
||||
};
|
||||
}
|
||||
|
||||
// Saner en frontmatter-avledet tittel/beskrivelse for trygg, idempotent emit:
|
||||
// noytraliser markdown-lenker (RAG-injeksjon), strip kontrolltegn + strooe ]/)
|
||||
// som ville korrumpert round-trip-parsen (parseExistingIndex), kollaps whitespace,
|
||||
|
|
@ -83,10 +118,11 @@ function entryLine(title, link, desc) {
|
|||
return d ? `* [${t}](${link}) - ${d}` : `* [${t}](${link})`;
|
||||
}
|
||||
|
||||
// Generer og skriv index.md for EN katalog (ikke rekursivt). isRoot styrer okf_version.
|
||||
// explicitVersion (B2): en eksplisitt oppgitt versjon VINNER over eksisterende
|
||||
// rot-verdi (bump-mekanisme); ellers bevares eksisterende (idempotent vedlikehold).
|
||||
function writeIndexFor(dir, isRoot, okfVersion, explicitVersion) {
|
||||
// Generer og skriv index.md for EN katalog (ikke rekursivt). isRoot styrer de to
|
||||
// markoer-linjene. explicitLayout (B2): en eksplisitt oppgitt layout-revisjon
|
||||
// VINNER over eksisterende rot-verdi (bump-mekanisme); ellers bevares eksisterende
|
||||
// (idempotent vedlikehold).
|
||||
function writeIndexFor(dir, isRoot, explicitLayout) {
|
||||
const existing = parseExistingIndex(join(dir, 'index.md'));
|
||||
const dirents = readdirSync(dir, { withFileTypes: true });
|
||||
const subdirs = dirents
|
||||
|
|
@ -103,7 +139,8 @@ function writeIndexFor(dir, isRoot, okfVersion, explicitVersion) {
|
|||
|
||||
const lines = [`# ${heading}`, ''];
|
||||
if (isRoot) {
|
||||
lines.push(`okf_version: ${explicitVersion ? okfVersion : (existing.okfVersion || okfVersion)}`, '');
|
||||
const { version, layout } = resolveMarkers(existing, explicitLayout);
|
||||
lines.push(`okf_version: ${version}`, `okf_layout: ${layout}`, '');
|
||||
}
|
||||
|
||||
for (const sd of subdirs) {
|
||||
|
|
@ -124,11 +161,13 @@ function writeIndexFor(dir, isRoot, okfVersion, explicitVersion) {
|
|||
}
|
||||
|
||||
// Generer index.md for rot + alle underkataloger, rekursivt.
|
||||
// opts.okfVersion er et DEPRECATED alias for opts.okfLayout: verdien kallere har
|
||||
// sendt inn her var alltid en layout-revisjon, ogsaa foer splitten (§12).
|
||||
export function generateIndexes(root, opts = {}) {
|
||||
const explicitVersion = typeof opts.okfVersion === 'string' && opts.okfVersion !== '';
|
||||
const okfVersion = explicitVersion ? opts.okfVersion : OKF_VERSION;
|
||||
const raw = opts.okfLayout ?? opts.okfVersion;
|
||||
const explicitLayout = typeof raw === 'string' && raw !== '' ? raw : undefined;
|
||||
const walk = (dir, isRoot) => {
|
||||
writeIndexFor(dir, isRoot, okfVersion, explicitVersion);
|
||||
writeIndexFor(dir, isRoot, explicitLayout);
|
||||
for (const e of readdirSync(dir, { withFileTypes: true })) {
|
||||
if (e.isDirectory() && isWalkableDir(e.name)) walk(join(dir, e.name), false);
|
||||
}
|
||||
|
|
@ -141,22 +180,32 @@ export function generateIndexes(root, opts = {}) {
|
|||
const isMain = process.argv[1]
|
||||
&& fileURLToPath(import.meta.url) === process.argv[1];
|
||||
if (isMain) {
|
||||
// B2: flagg-tolerant parsing -- `--okf-version <ver>` godtas foer ELLER etter
|
||||
// rot-argumentet, og manglende flagg-verdi er en bruksfeil (exit 2), ikke krasj.
|
||||
// B2: flagg-tolerant parsing -- flagget godtas foer ELLER etter rot-argumentet,
|
||||
// og manglende flagg-verdi er en bruksfeil (exit 2), ikke krasj.
|
||||
// 1.8.1: `--okf-layout` er kanon; `--okf-version` beholdes som deprecated alias
|
||||
// (verdien var alltid en layout-revisjon) og varsler paa stderr -- ALDRI stdout,
|
||||
// som er scriptets maskinlesbare flate.
|
||||
const usage = () => {
|
||||
process.stderr.write('Bruk: node okf-index.mjs <bundle-rot> [--okf-version <ver>]\n');
|
||||
process.stderr.write('Bruk: node okf-index.mjs <bundle-rot> [--okf-layout <ver>]\n');
|
||||
process.exit(2);
|
||||
};
|
||||
const args = process.argv.slice(2);
|
||||
let okfVersion;
|
||||
const vi = args.indexOf('--okf-version');
|
||||
if (vi !== -1) {
|
||||
okfVersion = args[vi + 1];
|
||||
if (!okfVersion || okfVersion.startsWith('--')) usage();
|
||||
args.splice(vi, 2);
|
||||
let okfLayout;
|
||||
for (const flag of ['--okf-layout', '--okf-version']) {
|
||||
const i = args.indexOf(flag);
|
||||
if (i === -1) continue;
|
||||
const val = args[i + 1];
|
||||
if (!val || val.startsWith('--')) usage();
|
||||
if (flag === '--okf-version') {
|
||||
process.stderr.write(
|
||||
'Advarsel: --okf-version er utgaatt og setter layout-revisjonen. Bruk --okf-layout.\n',
|
||||
);
|
||||
}
|
||||
if (okfLayout === undefined) okfLayout = val;
|
||||
args.splice(i, 2);
|
||||
}
|
||||
const root = args[0];
|
||||
if (!root) usage();
|
||||
generateIndexes(root, { okfVersion });
|
||||
generateIndexes(root, { okfLayout });
|
||||
process.stdout.write(`OKF-index generert for ${root}\n`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
name: okr-offentlig-sektor
|
||||
description: >-
|
||||
OKR (Objectives and Key Results) for Norwegian public sector: writing OKR, reviewing OKR quality, cascading OKR from strategy to team, tracking progress, running OKR meetings, translating tildelingsbrev to OKR. Also CFR, OKR antipatterns, scoring, Oboard. Triggers on: "OKR", "skriv OKR", "vurder OKR", "OKR-scoring", "kaskadere OKR", "tildelingsbrev til OKR", "OKR for offentlig sektor".
|
||||
version: "1.8.0"
|
||||
version: "1.8.1"
|
||||
---
|
||||
|
||||
# OKR Skill for Offentlig Sektor (Norge)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ description: >-
|
|||
prompt. Triggers on: "våre mål", "overordnede mål", "mål dette tertialet",
|
||||
"tertialmål", "tildelingsbrev", "hva sier OKR-ene våre", "forrige syklus",
|
||||
"strategi", "styringssignaler".
|
||||
version: "1.8.0"
|
||||
version: "1.8.1"
|
||||
---
|
||||
|
||||
# OKR Second-Brain Search
|
||||
|
|
@ -41,8 +41,9 @@ both, project first:
|
|||
2. **Home bundle** — `~/.claude/okr/org/` (organization identity, survives
|
||||
reinstall).
|
||||
|
||||
Each root carries its own `index.md` per level and an `okf_version` marker on its
|
||||
root `index.md`. Project content overrides home content on conflict
|
||||
Each root carries its own `index.md` per level, and its root `index.md` carries two
|
||||
markers: `okf_version` (the upstream OKF version targeted) and `okf_layout` (this
|
||||
plugin's own layout revision). Project content overrides home content on conflict
|
||||
(most-specific-wins, mirroring the hook's resolution).
|
||||
|
||||
## OKF layout (what you are searching)
|
||||
|
|
|
|||
3
tests/fixtures/okf-minimal/index.md
vendored
3
tests/fixtures/okf-minimal/index.md
vendored
|
|
@ -1,5 +1,6 @@
|
|||
# OKF second brain (minimal testfixtur)
|
||||
|
||||
okf_version: kb-layout-2026-06
|
||||
okf_version: 0.1
|
||||
okf_layout: kb-layout-2026-06
|
||||
|
||||
* [Tildelingsbrev](tildelingsbrev.md) - Styringssignal med unik testtoken.
|
||||
|
|
|
|||
3
tests/fixtures/okf-realistic/index.md
vendored
3
tests/fixtures/okf-realistic/index.md
vendored
|
|
@ -1,6 +1,7 @@
|
|||
# OKF second brain - Vegdirektoratet (realistisk testfixtur)
|
||||
|
||||
okf_version: kb-layout-2026-06
|
||||
okf_version: 0.1
|
||||
okf_layout: kb-layout-2026-06
|
||||
|
||||
* [Strategisk kontekst](strategisk-kontekst/index.md) - Tildelingsbrev, virksomhetsplan og profil.
|
||||
* [Syklus T1-2026](syklus/T1-2026/index.md) - Aktive OKR og statusrapport.
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ function makeProjectTree(work, count) {
|
|||
mkdirSync(lvl, { recursive: true });
|
||||
writeFileSync(
|
||||
join(work, '.claude', 'okr', 'index.md'),
|
||||
'# OKR-rot\n\nokf_version: kb-layout-2026-06\n',
|
||||
'# OKR-rot\n\nokf_version: 0.1\nokf_layout: kb-layout-2026-06\n',
|
||||
);
|
||||
for (let i = 0; i < count; i++) {
|
||||
writeFileSync(join(lvl, `konseptfil-${i}.md`), `---\ntype: OKR\n---\n# K${i}\n`);
|
||||
|
|
@ -52,7 +52,7 @@ function makeHomeBundle(home, navn = 'HomeCapOrg') {
|
|||
const org = join(home, '.claude', 'okr', 'org');
|
||||
mkdirSync(org, { recursive: true });
|
||||
writeFileSync(join(org, 'profil.md'), `---\nnavn: "${navn}"\n---\n`);
|
||||
writeFileSync(join(org, 'index.md'), '# Org-rot\n\nokf_version: kb-layout-2026-06\n');
|
||||
writeFileSync(join(org, 'index.md'), '# Org-rot\n\nokf_version: 0.1\nokf_layout: kb-layout-2026-06\n');
|
||||
}
|
||||
|
||||
function runHook(cwd, home, input = OKR_PROMPT) {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { execFileSync, spawnSync } from 'node:child_process';
|
||||
import {
|
||||
mkdtempSync, cpSync, writeFileSync, readFileSync, existsSync, readdirSync, rmSync, mkdirSync,
|
||||
} from 'node:fs';
|
||||
|
|
@ -122,6 +122,23 @@ test('okf-index: rot-index baerer okf_version, undernivaa gjoer ikke', () => {
|
|||
}
|
||||
});
|
||||
|
||||
test('okf-index: undernivaa-index har verken markoer eller tom markoer-linje', () => {
|
||||
const dir = tmpRoot();
|
||||
try {
|
||||
cpSync(REALISTIC, dir, { recursive: true });
|
||||
generateIndexes(dir);
|
||||
const sub = readFileSync(join(dir, 'strategisk-kontekst', 'index.md'), 'utf8');
|
||||
assert.ok(!/okf_layout/.test(sub), 'undernivaa skal IKKE ha okf_layout');
|
||||
// Markoer-blokken er rot-eksklusiv: undernivaaet gaar rett fra H1 til entries.
|
||||
const lines = sub.split('\n');
|
||||
assert.match(lines[0], /^# /, 'linje 1 = overskrift');
|
||||
assert.equal(lines[1], '', 'linje 2 = blank');
|
||||
assert.match(lines[2], /^\* \[/, 'linje 3 = foerste entry (ingen markoer-blokk)');
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('okf-index: konsept-tittel/beskrivelse hentes fra frontmatter', () => {
|
||||
const dir = tmpRoot();
|
||||
try {
|
||||
|
|
@ -165,7 +182,7 @@ test('okf-check: konsept-fil uten type: -> exit != 0 + teller > 0 + navngir file
|
|||
|
||||
test('okf-check: rapporterer okf_version fra rot-index', () => {
|
||||
const { stdout } = runCheck(REALISTIC);
|
||||
assert.match(stdout, /okf_version:\s*kb-layout-2026-06/, 'skal ekko okf_version for menneskelig sammenligning');
|
||||
assert.match(stdout, /okf_version:\s*0\.1/, 'skal ekko okf_version for menneskelig sammenligning');
|
||||
});
|
||||
|
||||
// --- okf-check --strict-ingest (Step 8): lukket vokab + lenke-allow-liste ---
|
||||
|
|
@ -316,17 +333,20 @@ test('okf-index: innboks/ + dot-katalog -> ingen egen index.md OG ikke listet i
|
|||
|
||||
const INDEX_CLI = join(ROOT, 'scripts', 'okf-index.mjs');
|
||||
|
||||
test('okf-index CLI: eksplisitt --okf-version bumper eksisterende rot-index (flagg foer rot)', () => {
|
||||
// 1.8.1: verdien flagget baerer er en LAYOUT-revisjon (den var det ogsaa foer
|
||||
// splitten) -- flagget godtas fortsatt her (deprecated alias), men landet den
|
||||
// skriver er `okf_layout`. Se alias-testene i 1.8.1-seksjonen nederst.
|
||||
test('okf-index CLI: eksplisitt layout-flagg bumper eksisterende rot-index (flagg foer rot)', () => {
|
||||
const dir = tmpRoot();
|
||||
try {
|
||||
buildCleanIngest(dir); // rot-index har allerede okf_version: kb-layout-2026-06
|
||||
execFileSync('node', [INDEX_CLI, '--okf-version', 'kb-layout-2027-01', dir], { encoding: 'utf8' });
|
||||
execFileSync('node', [INDEX_CLI, '--okf-layout', 'kb-layout-2027-01', dir], { encoding: 'utf8' });
|
||||
const root = readFileSync(join(dir, 'index.md'), 'utf8');
|
||||
assert.match(root, /^okf_version: kb-layout-2027-01$/m, 'eksplisitt versjon skal vinne over eksisterende');
|
||||
// Uten flagg bevares den bumpede versjonen (idempotent vedlikehold, som foer).
|
||||
assert.match(root, /^okf_layout: kb-layout-2027-01$/m, 'eksplisitt layout skal vinne over eksisterende');
|
||||
// Uten flagg bevares den bumpede verdien (idempotent vedlikehold, som foer).
|
||||
execFileSync('node', [INDEX_CLI, dir], { encoding: 'utf8' });
|
||||
const root2 = readFileSync(join(dir, 'index.md'), 'utf8');
|
||||
assert.match(root2, /^okf_version: kb-layout-2027-01$/m, 'implisitt kjoering bevarer eksisterende versjon');
|
||||
assert.match(root2, /^okf_layout: kb-layout-2027-01$/m, 'implisitt kjoering bevarer eksisterende layout');
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
|
|
@ -456,3 +476,175 @@ test('checkBundle { files }: strict validerer KUN oppgitte filer, aldri hele rot
|
|||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
// --- 1.8.1: okf_version / okf_layout-splitt (OKF-spec §12) ---
|
||||
// `okf_version` = upstream Google OKF-versjon ALENE (verdisett eid av Google,
|
||||
// enkeltverdi). `okf_layout` = vaar EGEN layout-revisjon (valgfri, verdisett eid
|
||||
// av emitteren). Rot-index baerer BEGGE. Migrasjonssti: en eksisterende rot-index
|
||||
// som baerer en layout-verdi i `okf_version` (vaar form foer 1.8.1) splittes.
|
||||
// Kilde: catalog/docs/okf-second-brain/spec.md §3 + §12, log.md 2026-07-23.
|
||||
|
||||
// Skriv en rot-index med et vilkaarlig sett markoer-linjer (legacy/splittet/tom).
|
||||
function writeRootIndex(dir, markerLines) {
|
||||
mkdirSync(dir, { recursive: true });
|
||||
writeFileSync(join(dir, 'index.md'), `# Bundle\n\n${markerLines.join('\n')}\n`);
|
||||
}
|
||||
|
||||
test('okf-index: rot-index baerer BEGGE markoerene, undernivaa ingen av dem', () => {
|
||||
const dir = tmpRoot();
|
||||
try {
|
||||
cpSync(REALISTIC, dir, { recursive: true });
|
||||
generateIndexes(dir);
|
||||
const root = readFileSync(join(dir, 'index.md'), 'utf8');
|
||||
assert.match(root, /^okf_version: 0\.1$/m, 'rot skal baere upstream-versjonen alene');
|
||||
assert.match(root, /^okf_layout: kb-layout-2026-06$/m, 'rot skal baere vaar layout-revisjon');
|
||||
const sub = readFileSync(join(dir, 'strategisk-kontekst', 'index.md'), 'utf8');
|
||||
assert.ok(!/^okf_version:/m.test(sub), 'undernivaa skal IKKE ha okf_version');
|
||||
assert.ok(!/^okf_layout:/m.test(sub), 'undernivaa skal IKKE ha okf_layout');
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('okf-index: legacy rot-index (layout-verdi i okf_version) migreres til splittet form', () => {
|
||||
const dir = tmpRoot();
|
||||
try {
|
||||
writeRootIndex(dir, ['okf_version: kb-layout-2026-06']);
|
||||
generateIndexes(dir);
|
||||
const root = readFileSync(join(dir, 'index.md'), 'utf8');
|
||||
assert.match(root, /^okf_version: 0\.1$/m, 'okf_version skal settes til upstream-verdien');
|
||||
assert.match(root, /^okf_layout: kb-layout-2026-06$/m, 'layout-verdien skal flyttes hit, ikke tapes');
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('okf-index: en FREMMED layout-verdi i okf_version bevares gjennom migrasjonen', () => {
|
||||
const dir = tmpRoot();
|
||||
try {
|
||||
// Migrasjonen skal flytte verdien, ikke erstatte den med vaar egen konstant.
|
||||
writeRootIndex(dir, ['okf_version: kb-layout-2025-01']);
|
||||
generateIndexes(dir);
|
||||
const root = readFileSync(join(dir, 'index.md'), 'utf8');
|
||||
assert.match(root, /^okf_version: 0\.1$/m);
|
||||
assert.match(root, /^okf_layout: kb-layout-2025-01$/m, 'den FUNNE verdien skal flyttes verbatim');
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('okf-index: spec-konform okf_version (upstream-form) migreres IKKE til okf_layout', () => {
|
||||
const dir = tmpRoot();
|
||||
try {
|
||||
writeRootIndex(dir, ['okf_version: 0.2']);
|
||||
generateIndexes(dir);
|
||||
const root = readFileSync(join(dir, 'index.md'), 'utf8');
|
||||
assert.match(root, /^okf_version: 0\.2$/m, 'upstream-verdi skal bevares, ikke overskrives');
|
||||
assert.match(root, /^okf_layout: kb-layout-2026-06$/m, 'layout fylles fra konstant');
|
||||
assert.ok(!/^okf_layout: 0\.2$/m.test(root), 'upstream-verdien skal ALDRI havne i okf_layout');
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('okf-index: eksisterende splittet form bevares verbatim (idempotent vedlikehold)', () => {
|
||||
const dir = tmpRoot();
|
||||
try {
|
||||
writeRootIndex(dir, ['okf_version: 0.3', 'okf_layout: kb-layout-2099-12']);
|
||||
generateIndexes(dir);
|
||||
const root = readFileSync(join(dir, 'index.md'), 'utf8');
|
||||
assert.match(root, /^okf_version: 0\.3$/m, 'eksisterende okf_version bevares');
|
||||
assert.match(root, /^okf_layout: kb-layout-2099-12$/m, 'eksisterende okf_layout bevares');
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('okf-index CLI: --okf-layout bumper layout og lar okf_version staa', () => {
|
||||
const dir = tmpRoot();
|
||||
try {
|
||||
buildCleanIngest(dir); // legacy rot-index: okf_version: kb-layout-2026-06
|
||||
execFileSync('node', [INDEX_CLI, '--okf-layout', 'kb-layout-2027-01', dir], { encoding: 'utf8' });
|
||||
const root = readFileSync(join(dir, 'index.md'), 'utf8');
|
||||
assert.match(root, /^okf_layout: kb-layout-2027-01$/m, 'eksplisitt layout skal vinne');
|
||||
assert.match(root, /^okf_version: 0\.1$/m, 'okf_version skal vaere upstream-verdien');
|
||||
// Uten flagg bevares den bumpede layouten (idempotent vedlikehold).
|
||||
execFileSync('node', [INDEX_CLI, dir], { encoding: 'utf8' });
|
||||
const root2 = readFileSync(join(dir, 'index.md'), 'utf8');
|
||||
assert.match(root2, /^okf_layout: kb-layout-2027-01$/m, 'implisitt kjoering bevarer bumpet layout');
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('okf-index CLI: --okf-version er deprecated alias for --okf-layout (m/ stderr-varsel)', () => {
|
||||
const dir = tmpRoot();
|
||||
try {
|
||||
buildCleanIngest(dir);
|
||||
// Aliaset maa fortsatt sette LAYOUT -- det var alltid det verdien betydde.
|
||||
execFileSync('node', [INDEX_CLI, '--okf-version', 'kb-layout-2027-05', dir], {
|
||||
encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
||||
});
|
||||
const root = readFileSync(join(dir, 'index.md'), 'utf8');
|
||||
assert.match(root, /^okf_layout: kb-layout-2027-05$/m, 'aliaset skal sette layout');
|
||||
assert.match(root, /^okf_version: 0\.1$/m, 'aliaset skal IKKE sette okf_version');
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('okf-index CLI: --okf-version skriver deprecation-varsel til stderr, ikke stdout', () => {
|
||||
const dir = tmpRoot();
|
||||
try {
|
||||
buildCleanIngest(dir);
|
||||
const res = spawnSync(
|
||||
'node', [INDEX_CLI, '--okf-version', 'kb-layout-2027-06', dir],
|
||||
{ encoding: 'utf8' },
|
||||
);
|
||||
assert.equal(res.status, 0, 'aliaset skal fortsatt lykkes');
|
||||
assert.match(res.stderr, /--okf-version/, 'varselet skal navngi det utgaaende flagget');
|
||||
assert.match(res.stderr, /--okf-layout/, 'varselet skal peke paa erstatteren');
|
||||
assert.ok(!/--okf-layout/.test(res.stdout), 'varselet skal IKKE forurense stdout');
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('okf-index CLI: --okf-layout uten verdi -> bruksfeil exit 2, ingen skriving', () => {
|
||||
const dir = tmpRoot();
|
||||
try {
|
||||
buildCleanIngest(dir);
|
||||
const before = readFileSync(join(dir, 'index.md'), 'utf8');
|
||||
let status = 0;
|
||||
try {
|
||||
execFileSync('node', [INDEX_CLI, dir, '--okf-layout'], { encoding: 'utf8', stdio: 'pipe' });
|
||||
} catch (e) {
|
||||
status = e.status;
|
||||
}
|
||||
assert.equal(status, 2, 'manglende flagg-verdi skal gi bruksfeil exit 2');
|
||||
assert.equal(readFileSync(join(dir, 'index.md'), 'utf8'), before, 'rot-index skal vaere uendret');
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('okf-check: ekkoer BEGGE markoerene fra rot-index', () => {
|
||||
const { stdout } = runCheck(REALISTIC);
|
||||
assert.match(stdout, /okf_version:\s*0\.1/, 'skal ekko upstream-versjonen');
|
||||
assert.match(stdout, /okf_layout:\s*kb-layout-2026-06/, 'skal ekko vaar layout-revisjon');
|
||||
});
|
||||
|
||||
test('okf-check: rot-index uten okf_layout ekkoer MANGLER for den, ikke for okf_version', () => {
|
||||
const dir = tmpRoot();
|
||||
try {
|
||||
buildCleanIngest(dir);
|
||||
writeRootIndex(dir, ['okf_version: 0.1']); // splittet form, layout utelatt (lovlig: valgfri)
|
||||
const r = checkBundle(dir);
|
||||
assert.equal(r.okfVersion, '0.1', 'okf_version skal parses');
|
||||
assert.equal(r.okfLayout, null, 'fravaerende okf_layout skal vaere null, ikke undefined');
|
||||
const { stdout } = runCheck(dir);
|
||||
assert.match(stdout, /okf_layout: MANGLER/, 'ekkoet skal si fra at layout-markoeren mangler');
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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) paa ALLE shippede flater, og at .npmrc slaar av
|
||||
// er 1.8.1 (patch-lane: okf_layout-migrasjon) 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).
|
||||
|
||||
|
|
@ -24,10 +24,10 @@ function readPkg() {
|
|||
return JSON.parse(readFileSync(PKG, 'utf8'));
|
||||
}
|
||||
|
||||
test('package.json: type module + version 1.8.0 (minor-lane)', () => {
|
||||
test('package.json: type module + version 1.8.1 (patch-lane)', () => {
|
||||
const pkg = readPkg();
|
||||
assert.equal(pkg.type, 'module');
|
||||
assert.equal(pkg.version, '1.8.0');
|
||||
assert.equal(pkg.version, '1.8.1');
|
||||
});
|
||||
|
||||
test('package.json: engines.node-gulv satt (unpdf krever >= 22)', () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue