docs(plan): Q3 finding — method-spec §3 silently forbids hierarchical bundles
Answering question 3 from the OKF round (flat vs hierarchical bundles) turned
up a conflict that is real in code, not just between two spec texts:
method-spec.md:66-69 skip any link target containing a path separator
okf.py:123-125 `if "/" in target: continue` — implemented as written
okf-index.mjs:110 okr emits `${sd}/index.md`
okf-links.mjs:23 okr *requires* a leading `/`
So every link okr produces is skipped by the navigator, and okr cannot write a
flat bundle at all (routeLevel always returns a subdirectory). Worse, the
robustness rule at :72-73 mandates that the skip be silent — a hierarchical
bundle yields a read-context of the root index and nothing else, with no error.
On okr's own okf-realistic fixture all 8 concept files vanish.
The separator ban is the wrong proxy for the security property it wants: it
conflates "contains a separator" with "escapes the bundle". Method-spec already
carries the precise rule two lines below (:73, boundary-checked, fail-closed),
and okr has correct prior art (okf-links.mjs:20-26).
Direction: let navigation traverse hierarchy; keep door A flat in v1. This is
also what makes a shared Python/Node fixture suite possible at all.
Neither spec is edited. Decision is the operator's.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBJNsFsBRaAhqGKjjiGjoQ
This commit is contained in:
parent
a013e8b5c3
commit
5f046abcf4
1 changed files with 125 additions and 0 deletions
125
docs/plan/2026-07-20-q3-bundle-shape-navigation-conflict.md
Normal file
125
docs/plan/2026-07-20-q3-bundle-shape-navigation-conflict.md
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
# 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`:
|
||||
|
||||
```python
|
||||
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.** `catalog`s 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 *sikkerhets*regel: 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.md` → `b/index.md` → `a/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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue