fix(okr): siterte markoerverdier korrumperte begge rot-markoerene

Upstreams eneste kanoniske eksempel med verdi (okf/SPEC.md:773) skriver
`okf_version: "0.2"` -- sitert. parseExistingIndex fanget anfoerselstegnene
raatt, UPSTREAM_VERSION_RE avviste `"0.2"` som ikke-upstream-form, og
resolveMarkers behandlet den som en layout-verdi fra foer 1.8.1-splitten:

  inn:  okf_version: "0.2"
  ut:   okf_version: 0.1      <- nedgradert til vaar konstant
        okf_layout: "0.2"     <- ekte upstream-versjon i feil markoer

Begge markoerene oedelagt, og dataene ikke gjenopprettelige uten aa kjenne
originalen. Bugen er i released 1.8.1-kode og traff enhver bundle som hadde
skrevet markoeren slik upstream selv viser den.

Fiks: anfoerselstegnene er YAML-strengsyntaks, ikke del av verdien, saa de
strippes ved parse -- foer verdien tolkes og foer den emitteres. Verdien
emitteres normalisert (unquoted), slik at vaar egen utskrift bestaar en
form-sjekk som kjoeres paa raa streng. Unquote er konservativ: kun et
matchende par strippes, en halv sekvens bevares uroert.

Tester: 187 -> 192. Fire nye kjoert roede foer fiksen; den femte
(ubalansert-vakten) mutasjons-verifisert roed mot en graadig unquote.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmXFhc6v9cs4YZ5AWFnQs8
This commit is contained in:
Kjell Tore Guttormsen 2026-07-31 17:12:24 +02:00
commit 85143be7b1
2 changed files with 98 additions and 3 deletions

View file

@ -35,9 +35,23 @@ 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.
// `okf_version`-felt er en layout-verdi fra foer splitten. Testes ALLTID mot en
// unquotet verdi -- se unquote().
const UPSTREAM_VERSION_RE = /^\d+(?:\.\d+)+$/;
// En markoerverdi kan vaere sitert: upstreams eget kanoniske eksempel
// (`okf/SPEC.md:773`) skriver `okf_version: "0.2"`. Anfoerselstegnene er
// YAML-strengsyntaks, ikke del av verdien, saa de maa vekk FOER verdien tolkes
// (UPSTREAM_VERSION_RE) og foer den emitteres -- ellers leses en gyldig
// upstream-versjon som en layout-verdi og migreres til feil markoer.
// KUN et matchende par strippes: en halv anfoerselstegn-sekvens er en ugyldig
// verdi som skal bevares uroert, ikke gjettes paa.
function unquote(s) {
const q = s[0];
if ((q === '"' || q === "'") && s.length >= 2 && s.endsWith(q)) return s.slice(1, -1);
return s;
}
// 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
// filtreres i BEGGE enumererings-steder (subdir-listing + rekursjon).
@ -66,9 +80,9 @@ function parseExistingIndex(path) {
result.heading = line.slice(2).trim();
}
const ver = line.match(/^okf_version:\s*(.+)$/);
if (ver) result.okfVersion = ver[1].trim();
if (ver) result.okfVersion = unquote(ver[1].trim());
const lay = line.match(/^okf_layout:\s*(.+)$/);
if (lay) result.okfLayout = lay[1].trim();
if (lay) result.okfLayout = unquote(lay[1].trim());
const entry = line.match(/^\*\s*\[([^\]]*)\]\(([^)]+)\)(?:\s*-\s*(.*))?$/);
if (entry) {
if (!(entry[2] in result.descByLink)) result.linkOrder.push(entry[2]);