portfolio-optimiser/shared/docs/plan/2026-07-20-q3-bundle-shape-navigation-conflict.md

7.1 KiB

Q3 — flate vs. hierarkiske bundles: en reell konflikt i method-spec §3

Status: funn + retningsvurdering. Spec-en er IKKE endret. Beslutning tas av operatøren. Foranledning: spørsmål 3 fra llm-ingestion-okf (postkasse _okf-interim, trinn C): dør A produserer flate bundles; second-brain-siden (okr, catalog) bruker hierarkisk form med per-nivå-indekser. Sier spec-en noe om dette, og bør den?


1. Funnet: spec-en sier noe — og den forbyr hierarki, stilltiende

Spørsmålet var stilt som om spec-en er taus. Den er ikke det.

method-spec.md:66-69 (§3 Step 1):

Navigation starts at index.md and follows its intra-bundle markdown cross-links (](target.md)). Targets containing a path separator are out-of-bundle and MUST be skipped.

Dette er verifisert implementert i MAF-halvdelen, portfolio-optimiser/src/portfolio_optimiser/okf.py:123-125:

for target in _LINK_RE.findall(index.body):
    if "/" in target or target in seen:
        continue  # only same-dir bundle files; de-dup repeated links

Og okr-siden produserer nøyaktig det som forkastes:

  • okr/scripts/okf-index.mjs:110 emitterer underkatalog-pekere som `${sd}/index.md`.
  • okr/lib/okf-links.mjs:23 krever ledende /: if (!target.startsWith('/')) return false; — «maa vaere bundle-root-relativ».

Sammensatt: hver eneste lenke okr produserer inneholder /, og hver eneste av dem forkastes av navigatoren. okr kan ikke engang skrive flatt — routeLevel() (lib/okf-vocab.mjs:71, konsumert i lib/innboks-frontmatter.mjs:74) ruter alltid til en underkatalog. Dette er ikke et kantttilfelle; det er okrs normaltilstand.

Det verste er at feilen er stille. method-spec.md:72-73 krever det: «A broken or bundle-escaping cross-link MUST be tolerated — skipped, never raised (OKF robustness rule).» En hierarkisk bundle gir altså ikke en feilmelding — den gir en read-context som inneholder rot-indeksen og ingenting annet. På okrs eget fixture tests/fixtures/okf-realistic/ forsvinner alle 8 konseptfiler, fordi ingen av dem ligger i roten. Symptomet operatøren ville sett er «modellen kjente ikke innholdet», ikke «bundelen er ugyldig».

To spec-er kolliderer på samme paragrafnummer — ved et sammentreff. catalogs svar siterer «§3 MUST» for per-nivå-indeks. Det er catalog/docs/okf-second-brain/spec.md:45, et annet dokument enn method-spec.md §3. De er ikke to lesninger av én regel; de er to normative regimer som tilfeldigvis møtes på «§3». Verdt å holde fra hverandre i all videre diskusjon.

2. Hvorfor regelen er gal — den er feil proxy for riktig egenskap

Separator-forbudet ble åpenbart skrevet som en sikkerhetsregel: hindre at en lenke rømmer bundelen. Men den forveksler «inneholder separator» med «rømmer bundelen». sub/index.md rømmer ingenting. ../../etc/passwd gjør det.

Og method-spec har allerede den riktige mekanismen, to linjer lenger ned (:73):

path resolution MUST be boundary-checked against the bundle directory, fail-closed.

Så §3 Step 1 bærer både den presise regelen og en grov proxy — og det er proxyen som gjør skaden. Den presise regelen alene er tilstrekkelig.

Den korrekte formen finnes allerede som fungerende prior art hos okr (lib/okf-links.mjs:20-26): avvis null-byte, backslash, URL-scheme, ..-segment; krev .md; krev bundle-rot-relativ form. Dette er resolve-og-grensesjekk gjort riktig. Retningen er derfor ikke å oppfinne noe, men å løfte disse semantikkene inn i method-spec §3 Step 1 og la separator-forbudet gå ut.

3. Retning: la navigatoren traversere hierarki; la dør A fortsatt produsere flatt

To beslutninger som med fordel holdes fra hverandre:

(a) Navigasjon (method-spec §3 Step 1) bør støtte hierarki. Alternativet — flat-only for alltid — betyr at okr/catalogs output aldri kan mate optimiser-loopen, og at de to halvdelene ikke kan dele fixture-suite i det hele tatt (se §4). Kostnaden ved å åpne er håndterbar; se åpne punkter under.

(b) Dør A (ingest-spec §5) bør forbli flat i v1. ingest-{id}.md uten katalogkomponent, extraction-id-grammatikken utelukker /. En flat bundle er et gyldig degenerert tre, så ingenting brekker. Hierarkisk produksjon er et utvidelsespunkt, ikke v1 — og å holde det utenfor gjør at F1 (fritekst) og Q3 (form) forblir uavhengige beslutninger. Det er navigatoren som må lese begge former, ikke materialisereren som må produsere begge.

Hva som må avklares hvis (a) velges — ikke gratis

  • Lenkesyntaks, normativt. To former er i omløp: okr emitterer rot-relativt /foo/bar.md; catalog spec.md:76 tillater både rot-relativt og relativt. Spec-en MÅ velge. Anbefaling: støtt begge, med / = bundle-rot-relativ, IKKE filsystem-absolutt. Den lesningen er nøyaktig der en naiv implementasjon blir en path traversal-sårbarhet (open("/etc/passwd")), så den må være normativ og dekkes av en load-bearing test.
  • Traverseringsrekkefølge. Dagens «index first, then links in first-seen order» er definert for én flate. For et tre må dybde-først vs. bredde-først pinnes, ellers er read-context ikke deterministisk og golden-suiten brekker.
  • Dedup på resolvert sti, ikke rå target — ellers teller ./a.md og a.md som to oppføringer, og sykler (a/index.mdb/index.mda/index.md) termineres ikke.
  • Verdict-eksklusjonen må holde på alle nivåer. method-spec §3 Step 1 utelukker type: verdict fra read-context; med hierarki må det gjelde rekursivt, ikke bare i roten. Dette er lekkasjeflaten hvis den glemmes.
  • Read-context-rendering. ## {type}: {title}-seksjoner har ingen nivåforestilling. Anbefaling: behold flat seksjonsrendering også for hierarkiske bundles (nivået er navigasjonsstruktur, ikke presentasjonsstruktur) — men si det eksplisitt.

4. Konsekvens for delt fixture-suite (det spørsmålet egentlig gjaldt)

Direkte svar: Python- og Node-halvdelen kan dele fixture-suite kun hvis traverserings- kontrakten er én kontrakt.

  • Velges flat-only, er okrs okf-realistic ikke en gyldig input til Python-navigatoren, og de to halvdelene deler ikke bundle-fixtures i det hele tatt — bare ingest-fixtures.
  • Velges hierarki, MÅ fixture-suiten inneholde en hierarkisk case, og begge halvdeler MÅ produsere identisk read-context fra den. okr har allerede kandidaten ferdig (tests/fixtures/okf-realistic/, tre nivåer dypt, per-nivå-indekser), og et flatt motstykke (okf-minimal/). De bør adopteres som delte fixtures, ikke gjenskapes.

Med andre ord: (a) er det som gjør delt fixture-suite mulig i første omgang. Det er hovedargumentet, sterkere enn eleganse.

5. Løse tråder som ikke er mine å lukke

  • Versjonsdrift: catalog/docs/okf-second-brain/spec.md:47 oppgir rotmarkøren som «currently 0.1», mens okr/scripts/okf-index.mjs:27 setter OKF_VERSION = 'kb-layout-2026-06'. Spec og referanse-implementasjon er ikke i sync. Meldt, ikke rørt.
  • okrs lukkede vokabular håndheves kun på skrivestien (lib/okf-vocab.mjs:6-8); lesesiden aksepterer ukjente typer. Relevant for catalogs krav (i) — se svarfilen.