feat(okr): okf-check leser okf_version fra frontmatter og broedtekst
This commit is contained in:
parent
d63438581d
commit
049259d9a6
2 changed files with 155 additions and 11 deletions
|
|
@ -58,23 +58,51 @@ function walkConcepts(root) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Les rotens to markoerer (markdown-tekst i index.md, ikke frontmatter).
|
// Les rotens to markoerer fra index.md. `okf_version` = upstream OKF-versjon
|
||||||
// `okf_version` = upstream OKF-versjon (spec §3), `okf_layout` = emitterens egen
|
// (spec §3), `okf_layout` = emitterens egen layout-revisjon (spec §12, valgfri).
|
||||||
// layout-revisjon (spec §12, valgfri). Fravaerende markoer -> null. Ren ekko:
|
// Fravaerende markoer -> null. Ren ekko: verdiene valideres ikke (spec §3 er
|
||||||
// verdiene valideres ikke (spec §3 er ikke haandhevende paa form ennaa).
|
// ikke haandhevende paa form ennaa).
|
||||||
//
|
//
|
||||||
// Verdien unquotes med SAMME konservative regel som produsent-siden (okf-index
|
// TO PLASSERINGER, bevisst (D8 steg 20). Beslutning 6 flytter `okf_version` til
|
||||||
// unquote(), delt import -- ikke en kopi). Uten den ga samme fil to lesninger:
|
// frontmatter mens `okf_layout` blir i broedteksten, men bundles paa disk
|
||||||
// produsenten tolket `okf_version: "0.2"` som 0.2, checkeren ekkoet «"0.2"».
|
// migrerer ikke samtidig -- en bundle skrevet av en eldre okf-index baerer
|
||||||
// Anfoerselstegn er YAML-strengsyntaks, ikke del av verdien -- og siden
|
// fortsatt begge i broedteksten. Lesingen proever derfor frontmatter FOERST og
|
||||||
// upstreams eneste kanoniske eksempel med verdi (SPEC.md:773) er sitert, er
|
// faller tilbake til broedteksten. Presedensen er en REGEL her, ikke en
|
||||||
// dette den formen en spec-tro bundle faktisk har paa disk.
|
// bivirkning av at frontmatter tilfeldigvis staar oeverst i fila.
|
||||||
|
//
|
||||||
|
// De to lagene har ULIKE tolkningsregler, og det er hele grunnen til at de er
|
||||||
|
// skilt:
|
||||||
|
// - frontmatter ER YAML -> parseFrontmatter haandterer sitering OG trailing
|
||||||
|
// ` # kommentar` (lib/frontmatter.mjs:39). En raa `^key:`-scan lot
|
||||||
|
// kommentaren lekke inn i verdien.
|
||||||
|
// - broedteksten er IKKE YAML -> der er unquote() alene riktig regel, delt
|
||||||
|
// import fra okf-index (produsent-siden) og ikke en kopi. Uten den ga samme
|
||||||
|
// fil to lesninger: produsenten tolket `okf_version: "0.2"` som 0.2,
|
||||||
|
// checkeren ekkoet «"0.2"». Upstreams eneste kanoniske eksempel med verdi
|
||||||
|
// (SPEC.md:773) er sitert, saa det er formen en spec-tro bundle har paa disk.
|
||||||
function rootMarkers(root) {
|
function rootMarkers(root) {
|
||||||
const idx = join(root, 'index.md');
|
const idx = join(root, 'index.md');
|
||||||
if (!existsSync(idx)) return { okfVersion: null, okfLayout: null };
|
if (!existsSync(idx)) return { okfVersion: null, okfLayout: null };
|
||||||
const raw = readFileSync(idx, 'utf8');
|
const raw = readFileSync(idx, 'utf8');
|
||||||
|
const { raw: fmRaw, get: fmGet } = parseFrontmatter(raw);
|
||||||
|
// Broedteksten = alt UNDER frontmatter-blokken, slik at de to lagene kan feile
|
||||||
|
// hver for seg (mutasjon M1 roedner (20a)/(20d) NETTOPP fordi fallbacken ikke
|
||||||
|
// ser frontmatter-linjene).
|
||||||
|
//
|
||||||
|
// AERLIG OM DEKNINGEN: uttrekket er ikke produksjonsobserverbart i dag. Naar
|
||||||
|
// noekkelen finnes i frontmatter i en form parseFrontmatter i det hele tatt
|
||||||
|
// returnerer, vinner fm-laget foer fallbacken kjoeres -- saa `body = raw`
|
||||||
|
// roedner ingen test (maalt, ikke antatt). Det beholdes likevel: fjernes det,
|
||||||
|
// ligger en latent defekt og venter paa at lib/frontmatter.mjs's list-key-
|
||||||
|
// kontrakt (:13-15) blir sann. I dag returnerer get() paa en list-key foerste
|
||||||
|
// LIST-ELEMENT, ikke null, fordi `\s*` i :29-regexen spiser linjeskiftet.
|
||||||
|
// Fikses det (patch-lane), begynner fallbacken aa kjoere for list-keys -- og
|
||||||
|
// uten dette uttrekket ville den plukket «- a» ut av frontmatter.
|
||||||
|
const body = fmRaw === null ? raw : raw.slice(raw.indexOf('\n---', 3) + 4);
|
||||||
const pick = (key) => {
|
const pick = (key) => {
|
||||||
const m = raw.match(new RegExp(`^${key}:\\s*(.+)$`, 'm'));
|
const fromFm = fmGet(key);
|
||||||
|
if (fromFm !== null) return unquote(fromFm);
|
||||||
|
const m = body.match(new RegExp(`^${key}:\\s*(.+)$`, 'm'));
|
||||||
return m ? unquote(m[1].trim()) : null;
|
return m ? unquote(m[1].trim()) : null;
|
||||||
};
|
};
|
||||||
return { okfVersion: pick('okf_version'), okfLayout: pick('okf_layout') };
|
return { okfVersion: pick('okf_version'), okfLayout: pick('okf_layout') };
|
||||||
|
|
|
||||||
|
|
@ -899,3 +899,119 @@ test('okf-check: rot-index uten okf_layout ekkoer MANGLER for den, ikke for okf_
|
||||||
rmSync(dir, { recursive: true, force: true });
|
rmSync(dir, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// --- D8 steg 20: okf-check leser markoerene fra BEGGE plasseringene ---
|
||||||
|
// Beslutning 6 flytter `okf_version` til rot-indeksens frontmatter (steg 21),
|
||||||
|
// mens `okf_layout` blir staaende i broedteksten. Bundles migrerer IKKE
|
||||||
|
// samtidig -- en bundle skrevet av en eldre okf-index baerer fortsatt begge i
|
||||||
|
// broedteksten -- saa LESINGEN maa tolerere begge former gjennom migrasjonen.
|
||||||
|
// Presedens: frontmatter foerst, deretter broedteksten UNDER frontmatter-blokken.
|
||||||
|
// Fortsatt ren ekko (ingen verdivalidering) og fortsatt ingen PRESENCE-krav.
|
||||||
|
//
|
||||||
|
// De tre casene er valgt for aa DISKRIMINERE de to lagene. S49-laerdommen:
|
||||||
|
// en assertion som ikke kan skille HVILKET lag som fanget gjoer en tolagsvakt
|
||||||
|
// til en ettlagsvakt uten at noe blir roedt. Her roedner hvert lag for seg:
|
||||||
|
// (20a) kun frontmatter -> roedner naar frontmatter-laget fjernes
|
||||||
|
// (20b) frontmatter UTEN noekkelen, verdi i -> roedner naar broedtekst-laget fjernes
|
||||||
|
// broedteksten (den naive "har fila frontmatter?
|
||||||
|
// bruk kun den"-regresjonen)
|
||||||
|
// (20c) begge, ULIKE verdier -> pinner presedensen; roedner naar
|
||||||
|
// rekkefoelgen snus
|
||||||
|
// Uten (20b) ville steg 20 bestaatt UTEN kodeendring: dagens rootMarkers()
|
||||||
|
// regex-scanner raa filtekst med `m`-flagg og treffer allerede frontmatter-
|
||||||
|
// linjer -- foerste treff vinner, og frontmatter staar oeverst.
|
||||||
|
|
||||||
|
// Rot-index med VILKAARLIG innhold (helleren writeRootIndex laaser formen).
|
||||||
|
function writeRawRootIndex(dir, content) {
|
||||||
|
mkdirSync(dir, { recursive: true });
|
||||||
|
writeFileSync(join(dir, 'index.md'), content);
|
||||||
|
}
|
||||||
|
|
||||||
|
test('(20a) okf-check: okf_version i rot-indeksens FRONTMATTER leses', () => {
|
||||||
|
const dir = tmpRoot();
|
||||||
|
try {
|
||||||
|
buildCleanIngest(dir);
|
||||||
|
writeRawRootIndex(dir, '---\nokf_version: 0.1\n---\n\n# Bundle\n\nokf_layout: kb-layout-2026-06\n');
|
||||||
|
const r = checkBundle(dir);
|
||||||
|
assert.equal(r.okfVersion, '0.1', 'frontmatter-plassert okf_version skal leses');
|
||||||
|
assert.equal(r.okfLayout, 'kb-layout-2026-06', 'broedtekst-plassert okf_layout skal fortsatt leses');
|
||||||
|
assert.equal(runCheck(dir).status, 0, 'migrert form skal gi exit 0');
|
||||||
|
} finally {
|
||||||
|
rmSync(dir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('(20b) okf-check: frontmatter UTEN okf_version faller tilbake til broedteksten', () => {
|
||||||
|
const dir = tmpRoot();
|
||||||
|
try {
|
||||||
|
buildCleanIngest(dir);
|
||||||
|
// Umigrert bundle som likevel har en frontmatter-blokk: verdien staar i
|
||||||
|
// broedteksten. En lesing som slaar seg til ro med "fila HAR frontmatter"
|
||||||
|
// mister markoeren her -- det er regresjonen dette steget er likest til.
|
||||||
|
writeRawRootIndex(dir, '---\ntitle: Bundle\n---\n\n# Bundle\n\nokf_version: 0.1\nokf_layout: kb-layout-2026-06\n');
|
||||||
|
const r = checkBundle(dir);
|
||||||
|
assert.equal(r.okfVersion, '0.1', 'broedtekst-verdien skal finnes selv naar frontmatter finnes uten noekkelen');
|
||||||
|
assert.equal(r.okfLayout, 'kb-layout-2026-06');
|
||||||
|
} finally {
|
||||||
|
rmSync(dir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('(20c) okf-check: staar markoeren BEGGE steder, vinner frontmatter', () => {
|
||||||
|
const dir = tmpRoot();
|
||||||
|
try {
|
||||||
|
buildCleanIngest(dir);
|
||||||
|
// Overgangstilstand: en delvis migrert index kan baere begge. Frontmatter er
|
||||||
|
// maalformen, saa den er den autoritative -- broedteksten er restene.
|
||||||
|
writeRawRootIndex(dir, '---\nokf_version: 0.9\n---\n\n# Bundle\n\nokf_version: 0.1\nokf_layout: kb-layout-2026-06\n');
|
||||||
|
const r = checkBundle(dir);
|
||||||
|
assert.equal(r.okfVersion, '0.9', 'frontmatter skal ha presedens over broedteksten');
|
||||||
|
} finally {
|
||||||
|
rmSync(dir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('(20d) okf-check: sitert okf_version i frontmatter unquotes som paa produsent-siden', () => {
|
||||||
|
const dir = tmpRoot();
|
||||||
|
try {
|
||||||
|
buildCleanIngest(dir);
|
||||||
|
// Upstreams eget kanoniske eksempel (SPEC.md:773) er sitert. Samme fil skal
|
||||||
|
// ikke gi to lesninger avhengig av hvor markoeren staar.
|
||||||
|
writeRawRootIndex(dir, '---\nokf_version: "0.2"\n---\n\n# Bundle\n');
|
||||||
|
assert.equal(checkBundle(dir).okfVersion, '0.2', 'anfoerselstegn er YAML-syntaks, ikke del av verdien');
|
||||||
|
} finally {
|
||||||
|
rmSync(dir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('(20e) okf-check: markoer helt fravaerende gir fortsatt exit 0 (ingen PRESENCE-krav)', () => {
|
||||||
|
const dir = tmpRoot();
|
||||||
|
try {
|
||||||
|
buildCleanIngest(dir);
|
||||||
|
writeRawRootIndex(dir, '---\ntitle: Bundle\n---\n\n# Bundle\n');
|
||||||
|
const r = checkBundle(dir);
|
||||||
|
assert.equal(r.okfVersion, null, 'fravaerende markoer skal vaere null');
|
||||||
|
assert.equal(r.okfLayout, null);
|
||||||
|
assert.equal(runCheck(dir).status, 0, 'okf_version er MAY i specen -- fravaer er ikke en feil');
|
||||||
|
} finally {
|
||||||
|
rmSync(dir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('(20f) okf-check: frontmatter-markoer tolkes som YAML -- trailing kommentar lekker ikke inn i verdien', () => {
|
||||||
|
const dir = tmpRoot();
|
||||||
|
try {
|
||||||
|
buildCleanIngest(dir);
|
||||||
|
// Dette er caset som SKILLER de to lagene, og det eneste som roedner uten
|
||||||
|
// kodeendring: frontmatter ER YAML, saa ` # ...` er en kommentar og ikke del
|
||||||
|
// av verdien (lib/frontmatter.mjs:39, comment-leak-fiksen). Broedtekst-
|
||||||
|
// markoeren er IKKE YAML -- der er unquote() alene fortsatt riktig regel.
|
||||||
|
// Uten denne casen kunne steg 20 «bestaa» med dagens ettlags-regex, som
|
||||||
|
// scanner raa filtekst og ikke vet hva en frontmatter-blokk er.
|
||||||
|
writeRawRootIndex(dir, '---\nokf_version: 0.1 # upstream OKF-versjon\n---\n\n# Bundle\n');
|
||||||
|
assert.equal(checkBundle(dir).okfVersion, '0.1', 'YAML-kommentaren skal strippes i frontmatter');
|
||||||
|
} finally {
|
||||||
|
rmSync(dir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue