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
|
|
@ -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`);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue