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
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