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
218 lines
14 KiB
Markdown
218 lines
14 KiB
Markdown
# 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. **`slugify`s æ→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.mjs`s 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`.
|