okr/docs/llm-ingestion-okf-fase4-kartlegging-2026-07.md
Kjell Tore Guttormsen 056b6522f1 docs(okr): trinn C-tillegg til fase-4-kartlegging (adopsjonsrunden)
Deltakelse i den koordinerte OKF-adopsjonsrunden (ni repo). Varig innhold
festet i eget repo per postkasse-regel 2; svaret selv ligger i den
midlertidige postkassen.

Nytt i §6: primitiv-hypotesen bekreftet mot egen kode, med forbehold om at
orkestreringen (gate-foer-relasjoner, gate-scoping, claimed-register,
per-dokument-rollback) er den dyre delen og maa foelge med som referanse-
orkestrator. Flat-vs-hierarkisk indeks: format kan deles, kontrakt ikke --
to lenkekonvensjoner i samme bundle kollapser i en flat modell. Konvergens
med to andre repo paa writer-primitivet (frontmatter-passthrough, som vi
allerede har). Bundle-plassering: compliant, men cwd-binding flytter
lekkasjeflaten uten aa fjerne den.

Ingen kodeendring.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGkEqS3Zxf7rVuUQHrsQG2
2026-07-20 08:53:07 +02:00

14 KiB

llm-ingestion-okf — fase 4-kartlegging (okr-siden)

Status: planned — kartlegging og kravgrunnlag. Ingen kode endret, ingenting wiret. Dato: 2026-07-20. Grunnlag: ground-truth-lesing av all OKF-kode i dette repoet (suite 149/149 grønn ved kartleggingstidspunkt).

Node-halvdelen av llm-ingestion-okf finnes ikke ennå. Dette dokumentet er forarbeid: hva okr faktisk har, hva som er generelt vs. okr-spesifikt, og hva et delt bibliotek må oppfylle før okr kan vendore det i stedet for å eie egen kode.


1. Kartlegging — hva finnes i dag

Zero-runtime-dependency Node/ESM. Alle moduler er både importerbare (named exports) og CLI-kjørbare (import.meta.url === process.argv[1]-vakt). Node >= 22.

Modul Linjer Rolle Løftbarhet
lib/frontmatter.mjs 70 Flat FM-parse/skriv. BOM+CRLF-tolerant, siterte verdier bevarer intern #, multi-linje list-verdier tolereres Generell
lib/okf-links.mjs 39 Den ENE lenke-allow-listen: trygg bundle-lenke = leading /, .md, ingen scheme/../backslash/NUL. resolveBundleLink re-sjekker confinement Generell
lib/okf-vocab.mjs 73 Lukket TYPE/TAGS-vokab + routeLevel(type) okr-spesifikk data, generell mekanisme
lib/innboks-split.mjs 100 Ren funksjon: #/##-heading-split → konsepter. Preamble bevares, flat-fallback Generell (én no-detalj)
lib/innboks-frontmatter.mjs 96 Regelbasert FM-projeksjon. timestamp = kilde-mtime (idempotens by construction) Blandet
lib/innboks-relations.mjs 49 Relasjoner innen kjøringens sett, exact-title-substring → root-lenker. Assert zero-dangling Generell
lib/innboks-write.mjs 186 Atomisk skriv (temp+rename), path-confinement, realpath/symlink-guard, reservert-navn-guard, kuratert-fil-vern (kilde: innboks), kryss-kilde-claimed-register Generell (én okr-konstant)
lib/convert/index.mjs 132 docx/eml/pdf/txt/md → md. Lenke-nøytralisering i alle fem lenkeformer Generell
scripts/okf-check.mjs 160 Bundle-validering. Default tolerant (kun type kreves); --strict-ingest = vokab + lenke-allow-liste + files-scoping Generell
scripts/okf-index.mjs 162 Per-nivå index.md, idempotent, sanitizeEntry (C0/C1, zero-width, bidi, Unicode-tags, link-nøytralisering), atomisk Generell
scripts/innboks-ingest.mjs 240 Orkestrator: discover → per-dok staging → gate → discard-on-fail → relasjoner → index → sluttsjekk Generell arkitektur

Arkitektur-invarianter verdt å bevare i et løft:

  • Ingen LLM. Fil-settet er en funksjon av (drop-zone-innhold + mtime) alene → run1 og run2 byte-identisk. Veggklokke brukes aldri.
  • Per-dokument-isolasjon (Map-Reduce): ett fiendtlig dokument discardes alene og forgifter ikke de andre. Relasjoner emitteres ETTER gaten, så hvert mål garantert er på disk.
  • Strict-gaten scopes til kjøringens skrevne filer, ALDRI hele roten — ellers feller den legitimt håndkuratert innhold med lovlige eksterne lenker.
  • Ikke-destruktiv: originalene blir liggende i drop-zonen; hver original får en peker-fil.

2. Hva er okr-spesifikt vs. generelt

Generelt (kan løftes tilnærmet uendret)

frontmatter.mjs, okf-links.mjs, innboks-split.mjs, innboks-relations.mjs, okf-index.mjs, okf-check.mjs-kjernen, convert/-adapterne, og hele skrive-/ confinement-maskineriet i innboks-write.mjs. Ingenting her vet at domenet er OKR.

okr-spesifikt (må parametriseres før løft)

  1. TYPE_VOCAB/TAGS_VOCAB/LEVEL_BY_TYPE (okf-vocab.mjs) — norske, lukkede, OKR-domene. Mekanismen (lukket vokab + type→nivå-ruting) er generell; innholdet er ikke. Må inn som konfigurasjon, ikke kode. Merk at spec-en er engelskspråklig konvensjon mens vokabet er norsk — eierskapet (bibliotek-default vs. konsument-profil) må avklares.
  2. HOME_ORG-konstanten (innboks-write.mjs:44, ~/.claude/okr/org) — okrs to-rot- modell. Generaliseres til en «forbudte skriverøtter»-liste fra kaller.
  3. Type-utledningen (deriveType, innboks-frontmatter.mjs) — matcher vokab-termer som helt ord i tittel + kilde-basename, med \p{L}\p{N}-lookaround fordi \b ikke håndterer æ/ø/å. Regelen er generell, vokabet ikke.
  4. slugifys æ→ae/ø→oe-translitterering (innboks-split.mjs:15-25) — nordisk, ikke universelt. NFKD dekomponerer ikke æ/ø, så uten dette faller bokstaven bort. Et delt bibliotek trenger en pluggbar translitterasjonstabell, ellers regresserer norsk input.
  5. Faste nivånavn (strategisk-kontekst/historikk/dokumenter) og .claude/okr/innboks/ som drop-zone. Rene konfig-verdier.
  6. Norske feilmeldinger og kilde: innboks-provenansnøkkelen. Meldingsspråk må være pluggbart hvis biblioteket skal tjene ikke-norske konsumenter; kilde-nøkkelen er en OKF-extension key okr er avhengig av for kuratert-fil-vernet.

lib/convert/ spesielt

Adapterne er domene-agnostiske og det mest direkte gjenbrukbare i repoet. Men:

  • Fire exact-pinnede deps (mammoth 1.12.0, turndown 7.2.4, postal-mime 2.7.5, unpdf 1.6.2). okrs zero-dep-posisjon er bevart ved at de er lazy-importert med norsk installasjonshint, og at txt/md kjører helt dependency-fritt. Et delt bibliotek må bevare denne egenskapen — deps må være valgfrie (peer/optional), aldri obligatoriske. Vendored-vs-peer er en åpen beslutning som må tas sammen, ikke arves.
  • turndown er pinnet til headingStyle: 'atx'. Default er setext, som heading-splitten aldri ser. Denne koblingen mellom konverter og splitter må dokumenteres i kontrakten, ellers reintroduseres bugen i en delt utgave.
  • unpdf kalles med eksplisitt isEvalSupported: false (CVE-2024-4367), selv om det er default i 1.6.2. Belte + seler — må overleve løftet.
  • PDF gir flat tekst uten headings (kjent v1-caveat): PDF-dokumenter blir alltid ett konsept.
  • neutralizeExternalLinks er sikkerhetskritisk og hører sammen med okf-links.mjs. Emit-siden og validerings-siden deler allow-liste nettopp for at de aldri skal divergere. Splittes de i et løft, er divergens et spørsmål om tid.

3. Forutsetninger for at okr kan vendore en delt utgave

Rangert, alle må være oppfylt:

  1. Vokabet er data, ikke kode. Bibliotekets API tar TYPE/TAGS/routeLevel som argument. Uten dette må okr forke uansett.
  2. Zero-dep-kjerne bevart. txt/md-stien må kjøre uten npm-installasjon. Binærformat-deps optional/lazy med samme feilmeldings-kvalitet (klar hint, aldri traceback).
  3. Sikkerhetsegenskapene er testbart bevart. Path-confinement, realpath/symlink-guard, reservert-navn-guard, kuratert-fil-vern, sanitizeEntry-settet (C0/C1, ZWSP, bidi, Unicode tag-blokk), lenke-nøytralisering i alle fem former, --strict-ingest-scoping. Alle 149 testene her er tilgjengelige som seed; et løft som ikke porter dem er ikke ferdig. Særlig: gate-scoping (files) og discard-on-fail-isolasjon er lærte lekser fra B5/B2 — de er ikke kosmetikk.
  4. Idempotens by construction. mtime-basert timestamp, deterministisk sortering, stabil kryss-kilde-disambiguering. Et bibliotek som introduserer veggklokke er ubrukelig for okr.
  5. Pluggbar slugify-translitterering (æ/ø/å), ellers regresjon på norsk input.
  6. Pluggbar meldingsstreng/språk, eller minst en onNotice-seam som i dag.
  7. Versjonspinning etter polyrepo-disiplinen. okr vendorer mot en tag, ikke mot main.
  8. Guard-grensen respekteres. Dør A er ugatet; llm-ingestion-okf er plumbing. okrs egen strict-gate er ikke en sikkerhetsguard i guard-repoets forstand og skal ikke forveksles med en. Vil vi ha guard-gating, går det via llm-ingestion-guard på kallstedet — og det er en separat beslutning, ikke en del av fase 4.

4. okf_version — hvordan okr bruker feltet i dag (kjent avvik)

Faktisk bruk, verifisert:

  • Verdien er kb-layout-2026-06 (scripts/okf-index.mjs:27, eksportert som OKF_VERSION).
  • Den bor som markdown-tekstlinje i rot-index.md, ikke i frontmatter — index.md er OKF-reservert og bærer aldri frontmatter. Kun rot-index; undernivåer har den aldri (testet eksplisitt).
  • okf-check.mjs ekkoer verdien for menneskelig sammenligning. Den validerer den ikke, sammenligner den ikke mot noe, og feiler aldri på den. Hooks/scripts er no-network, så auto-fetch mot en standard er utelukket by design.
  • okf-index.mjs bevarer eksisterende verdi ved re-kjøring; en eksplisitt --okf-version <ver> vinner (bump-mekanismen). Begge grener er testet.
  • Feltet er per rot: prosjekt-.claude/okr/ og home-~/.claude/okr/org/ har hver sin.

Avviket: spec-en sier 0.1; okr sier kb-layout-2026-06. Dette er ikke bare ulik verdi — det er to ulike typer felt. okrs verdi navngir hvilket layout-mønster treet følger (datert konvensjons-snapshot); spec-ens 0.1 er et spec-versjonsnummer. De kan begge være riktige og likevel uforenlige i ett felt.

okrs posisjon: dette avgjøres av catalog som konvensjonseier, ikke av okr og ikke av biblioteket. okr har lav byttekostnad — feltet er ren ekko-tekst uten validerings-semantikk, så en verdiendring koster én konstant + fixture-oppdateringer. Men hvis begge betydningene skal bæres, trenger vi to felt (f.eks. okf_version = spec-versjon, okf_layout = layout-snapshot), og det er en spec-endring som må gå via commons/catalog.


5. Hva som aldri bør flyttes til biblioteket

  • okf-vocab.mjss innhold — norsk OKR-domenevokab hører hjemme i konsumenten eller i en profil, aldri som bibliotek-default.
  • Orkestratoren innboks-ingest.mjs som helhet — fase-rekkefølgen er generell og verdt å dele som mønster, men den konkrete drop-zone-plasseringen, to-rot-modellen og peker-filkonvensjonen er okrs. Del kjeden, ikke policyen.
  • Hook-integrasjonen (inject-okr-context.mjs, coaching-hook.mjs) og okr-second-brain-search-skillen — ren plugin-UX.
  • Alt sikkerhetsansvar. Guard-grensen står: llm-ingestion-guard eier sikkerhet, llm-ingestion-okf er plumbing, og okr reimplementerer ingen av delene.

6. Tillegg fra adopsjonsrunden 2026-07-20 (trinn C)

Skrevet etter deltakelse i den koordinerte runden (ni repo, postkasse ~/repos/_okf-interim/, midlertidig). Svaret vårt ligger i svar/okr.md der; det varige innholdet er dette:

6.1 Primitiv-modellen er riktig — men orkestreringen er den dyre delen

okr er allerede primitiver + én komponerende kaller: splitConcepts, projectFrontmatter, resolveRelations, writeConcepts, generateIndexes, checkBundle, convert, isSafeBundleLink — og innboks-ingest.mjs som gjør ingenting primitivene ikke eksponerer.

Fire orkestrerings-invarianter er ikke-åpenbare og ble kjøpt dyrt (B5, B2). De må følge med et delt bibliotek som referanse-orkestrator, ikke bare som primitiver:

  1. Gate FØR relasjoner (ellers dangling lenker fra discardede dokumenter).
  2. Gate scopet til kjøringens skrevne filer (ellers felles håndkuratert innhold).
  3. Kryss-kilde-claimed-register (kollisjon er en egenskap ved kjøringen, ikke en primitiv).
  4. Per-dokument-rollback (derfor returnerer writeConcepts {concepts, pointers}).

Begrensning i vår stemme: okr har ingen pull-sti (no-network by design), så vi kan ikke uttale oss om henting-som-primitiv.

6.2 Flat vs. hierarkisk indeks — format deles, kontrakt gjør det ikke

Dør A produserer flate bundles; vår form er hierarkisk. Forskjellen er ikke kosmetisk:

  • To lenkekonvensjoner i samme bundle: index-entries er nivå-relative, body-relasjoner er bundle-rot-relative (leading /). I en flat bundle kollapser de til det samme — en port fra en flat kontrakt får dette stille galt.
  • okf_version kun i rot-index forutsetter en rot distinkt fra andre nivåer.
  • routeLevel(type) gjør nivået semantisk avledet; flat materialisering har ingen tilsvarende operasjon.

Posisjon: flat = degenerert hierarkisk (dybde 1). Kontrakten må formuleres som «én index.md per nivå», ellers kan ikke Node konformere uten å brekke okr. Krav til cross-runtime-fixtures: minst én hierarkisk fixture, ellers beviser parity-testing ingenting.

6.3 Konvergens med to andre repo på writer-primitivet

portfolio-optimiser-claude og ms-ai-architect ber begge om frontmatter-som-input uten connector. Vi har det bygget: writeConcepts tar concept.frontmatter som ferdig serialisert streng og skriver verbatim (fileContent = frontmatter + body, ingen re-serialisering — dokumentert designvalg). Tre av ni konvergerer; løses F1 som en markdown-connector i dør A, får ingen av de tre noe.

6.4 Bundle-plassering — vi er compliant, men cwd-binding er en svak garanti

Ingen bruker-eid bundle ligger i plugin-treet (.claude/okr/ er cwd-relativ i brukerens eget prosjekt; ~/.claude/okr/org/ er home; KB-referansene er plugin-eide og blir). Org-profil- migreringen i 1.6.x er vår egen referanse for konfigurerbar sti-oppløsning.

Åpent, meldt videre: cwd-binding flytter lekkasjeflaten, den fjerner den ikke — .claude/okr/ lander i hvilket som helst repo brukeren står i, inkludert offentlige. Grensetilfelle: tillitsmodellen vår er asymmetrisk innenfor én bundle (innboks fiendtlig, resten kuratert — derfor kilde: innboks-guarden). Blir hele bundlen dør C-«eksternt innhold», må den grensen tegnes på nytt.

7. Neste steg (ikke utført)

  1. Operatør melder kartleggingen inn til biblioteket som kravgrunnlag (§3 er kravlisten).
  2. Catalog avklarer okf_version-semantikken (§4) — blokkerer løftet.
  3. Parser-sett + vendored-vs-peer avgjøres i samråd (§2, lib/convert/).
  4. Først når Node-halvdelen finnes med §3 oppfylt: vurder adopsjon i egen sesjon.

Ingen av disse stegene endrer kode i okr. Markørlinjen i STATE.md står på planned.