Merge commit '8a86f2ab6d'
This commit is contained in:
commit
2d91943fab
29 changed files with 2712 additions and 21 deletions
8
shared/.gitignore
vendored
Normal file
8
shared/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# Operational continuity state — LOCAL-ONLY, never committed.
|
||||
# Rationale: commons is subtree-consumed (a committed root STATE.md would land in every
|
||||
# consumer's shared/STATE.md) and is intended for open publication (STATE must never reach
|
||||
# a public mirror). The guard roll-up greps STATE.md locally, so local-only is sufficient.
|
||||
/STATE.md
|
||||
|
||||
# Local-scoped notes/overrides convention (KTG global): never mirrored.
|
||||
*.local.md
|
||||
|
|
@ -20,6 +20,15 @@ so the only thing that differs is the agent framework itself.
|
|||
bundle (OKF / LLM-wiki): one office building, one LED-retrofit measure, with a seed expert
|
||||
verdict encoding the realization gap and a golden-suite of expected validator outcomes. A
|
||||
small **dev fixture** for exercising the agentic loop; a realistic full-scale example comes later.
|
||||
- [`examples/nav-golden-hierarchy/`](examples/nav-golden-hierarchy/) and
|
||||
[`examples/nav-golden-escape/`](examples/nav-golden-escape/) — the **nav-golden** fixture class:
|
||||
`bundle/` in, `expected-read-context.md` out, exercising the navigation contract (method-spec §3
|
||||
Step 1) with one positive case (hierarchy, dedup, cycle termination, verdict exclusion) and one
|
||||
negative case (`..` and root-relative escapes, so a gate built on them can actually go red). Each
|
||||
case documents itself in its own `README.md`. **Informative listing only** — no normative file
|
||||
refers to this class, and the comparison rule is deliberately not pinned (each case's README
|
||||
leaves a gate free to compare byte-exact or after trailing-whitespace normalization), so these
|
||||
are not ground truth in the sense method-spec §7 uses the word.
|
||||
- [`skills/expert-reviewer/`](skills/expert-reviewer/) — the **expert-reviewer persona** as a
|
||||
framework-neutral Agent Skill: a `SKILL.md` persona prompt (energy-advisor / M&V role + the
|
||||
realization-gap methodology the validator cannot compute) and a canonical
|
||||
|
|
|
|||
174
shared/docs/plan/2026-07-16-llm-ingestion-guard-adoption.md
Normal file
174
shared/docs/plan/2026-07-16-llm-ingestion-guard-adoption.md
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
# Adoption plan — `llm-ingestion-guard` at the ingest persist gate
|
||||
|
||||
> **Status:** plan only. This document decides *when* and *where* the write-time
|
||||
> ingestion guard (`llm-ingestion-guard`, alpha — this plan pins no version; see
|
||||
> §3.2 for the `allow_reserved` default, which the guard changed in v0.3.0) earns its place in the
|
||||
> architecture this commons specifies. **It does NOT wire the guard in** — the
|
||||
> executable wiring lands in the two reference implementations; this document is
|
||||
> the framework-neutral contract they build to, same as [method-spec.md](../../method-spec.md)
|
||||
> and [ingest-spec.md](../../ingest-spec.md).
|
||||
>
|
||||
> Source brief: `llm-ingestion-pipeline-security/docs/ADOPTION-BRIEF.md` (§7 checklist
|
||||
> is the template used below). Guard repo: `git.fromaitochitta.com/open/llm-ingestion-pipeline-security`.
|
||||
|
||||
## 1. What this commons is (relevant framing)
|
||||
|
||||
`portfolio-optimiser-commons` is the **framework-neutral shared core** consumed as a
|
||||
`shared/` git subtree by both reference implementations (MAF and Claude Agents SDK).
|
||||
It contains **normative specs + fixtures, no executable pipeline.** Two specs matter here:
|
||||
|
||||
- **[ingest-spec.md](../../ingest-spec.md)** — a *deterministic, zero-model-call* step that
|
||||
materializes real data sources into OKF bundles BEFORE the loop. Source types: `file`
|
||||
(local CSV catalogue), `sql` (read-only SELECT), and `http` (an **OPTIONAL extension
|
||||
point** — remote endpoint / MCP connector, §4).
|
||||
- **[method-spec.md](../../method-spec.md)** — the 8-step agentic loop that *reads* the OKF
|
||||
bundle, enriches via a proposer/checker model, gates numbers with a mandatory
|
||||
deterministic validator, takes human expert verdicts, and **promotes approved verdicts
|
||||
back into the bundle wiki** (the promotion gate, §6 / Step 8).
|
||||
|
||||
The bundle *is* an OKF second-brain a downstream run later reads as trusted context — the
|
||||
exact shape the guard's brief targets.
|
||||
|
||||
## 2. §7 checklist, scored against this architecture
|
||||
|
||||
| §7 condition | Verdict for the commons-specified system |
|
||||
|---|---|
|
||||
| Persist LLM-enriched / externally-received content into a store a *downstream* agent reads as trusted | **Partial.** The **promotion gate** (method §6) lifts model-*proposed*, expert-*approved* verdicts into the OKF wiki; the next run's seeding fold reads them as trusted. Ingest materializes *raw source extracts* — **no LLM step at ingest time**. |
|
||||
| **≥1 ingest path takes UNTRUSTED content** *(the decisive box)* | **Not on any path implemented today.** The two conformance-required sources — `file` and `sql` — are **first-party by origin** (the operator's own project files / database). The untrusted boundary is the **`http`/MCP source type** (ingest §4, an explicit not-yet-built extension point) and any future **received-external-OKF-bundle** merge (not yet specified). |
|
||||
| An LLM step sits between the untrusted source and the store | Ingest: **no** (zero model calls, ingest §1). Loop: yes, but over *first-party* bundle context. |
|
||||
| You want fail-secure (halt before persist) | **Yes, already the design ethos** — ingest is fail-fast/fail-closed throughout (§3 verdict-layer reservation, §4 manifest validation, §8 network opt-in). |
|
||||
|
||||
**Verdict: the decisive box is currently NO** — every live/required ingest path is
|
||||
first-party. But the architecture has *designed, foreseeable* untrusted extension points
|
||||
(`http`/MCP; received bundles). Per brief §7 ("note the guard as a dependency to add when —
|
||||
not if — you open an external/inbox/received-bundle path"), the honest status is
|
||||
**`planned`**, not `not-applicable`.
|
||||
|
||||
## 3. Untrusted boundaries (guard wires here) vs first-party paths (it does not)
|
||||
|
||||
### 3.1 Untrusted boundary A — the `http`/MCP connector (ingest §4 extension point)
|
||||
|
||||
When a connector fetches remote/vendor/web content, the extracted body is untrusted, and
|
||||
ingest §5 persists it **verbatim inside a fenced code block** into a concept file the loop
|
||||
later reads as trusted context. That verbatim persist of remote content into an agent-read
|
||||
bundle is precisely the write-time boundary the guard defends.
|
||||
|
||||
**Minimal wiring (at ingest materialization, before writing `ingest-{id}.md`):** ingest
|
||||
makes *no model call*, so only the **scan-before-persist half** of the contract applies
|
||||
(brief §3 steps 1, 6, 7 — the fence-a-model-call half is N/A here):
|
||||
|
||||
```python
|
||||
from llm_ingestion_guard import sanitize, scan_output, Disposition # primitives are exported per-piece
|
||||
|
||||
raw = connector.fetch(extraction) # untrusted remote body
|
||||
cleaned = sanitize(raw) # strip carrier classes (zero-width/BIDI/tag/comment/data:)
|
||||
decision = scan_output(cleaned) # lexicon + entropy + active-content, high-untrust
|
||||
if decision.disposition is Disposition.FAIL_SECURE:
|
||||
alert(gate_code=decision.reasons, run_id=run_id) # minimal payload, no content (brief §2/§3.8)
|
||||
raise SystemExit # halt — never materialize this concept
|
||||
# else: write the concept file exactly as ingest §5 specifies today
|
||||
```
|
||||
|
||||
### 3.2 Untrusted boundary B — a received external OKF bundle (`okf.import_bundle`)
|
||||
|
||||
Distinct from the first-party `commons` subtree sync: if either implementation ever adds a
|
||||
"merge a *foreign* OKF bundle" path, run the guard's OKF adapter at that persist gate.
|
||||
|
||||
```python
|
||||
from llm_ingestion_guard.okf import import_bundle, Origin, Channel
|
||||
|
||||
result = import_bundle(bundle, origin=Origin.EXTERNAL, channel=Channel.AUTOMATIC)
|
||||
for c in result.concepts:
|
||||
if c.error: # FAIL_SECURE per concept: bad path, unsafe frontmatter, non-https resource
|
||||
skip(c.path) # do not merge this concept
|
||||
# then materialize the surviving concepts
|
||||
```
|
||||
|
||||
This maps directly onto ingest-spec gates the commons *already* mandates — path
|
||||
boundary-check (§4), **verdict-layer reservation (§3 — a received bundle MUST NOT inject
|
||||
`type: verdict` files)**, and the index cross-link graph (§6). The adapter implements a
|
||||
superset (frontmatter parse-safety, `resource` URL allowlist, dangling-link detection). Pick
|
||||
`allow_reserved=True` for a whole received bundle (scan `index.md`/`log.md` bodies),
|
||||
`allow_reserved=False` if the path ever materialises *individual* uploads.
|
||||
|
||||
### 3.3 First-party path — the promotion gate (method §6). **Do NOT wire the guard here.**
|
||||
|
||||
The promotion gate lifts model-proposed, expert-approved verdicts into the wiki. Its content
|
||||
originates from the **first-party bundle context**, passes the **deterministic validator**,
|
||||
and crosses only on **human/persona approval** (method §6 fail-closed). Per brief §7,
|
||||
trusted-author approvals are **out of the guard's threat model by design**. Wiring the guard
|
||||
here would trip on legitimate content and add no security the human + validator do not
|
||||
already provide. *Recorded explicitly so a future session does not over-wire.*
|
||||
|
||||
### 3.4 Sharp edge — origin vs channel for `file`/`sql`
|
||||
|
||||
`file`/`sql` are first-party *channels*, but their *content origin* can be external (a
|
||||
vendor's CSV, a subcontractor's report the operator ingests). Brief §7: **"trust follows the
|
||||
data's origin, not the insertion channel."** A natural — but **not-yet-decided** — extension
|
||||
is a per-source `origin: first-party | external` field in the ingest manifest; a source
|
||||
declared external-origin would route its extracted text through the §3.1 scan-before-persist
|
||||
gate. Flagged as an open design question, **not prescribed here** (avoid scope creep); it
|
||||
keeps the decisive-box judgment honest.
|
||||
|
||||
## 4. When — roadmap placement
|
||||
|
||||
- **T0 (now):** first-party only (`file` + `sql`, curated bundles, first-party `commons`
|
||||
subtree). Guard = **documented dependency, not wired.** Marker status `planned`.
|
||||
- **Trigger A — `http`/MCP source type implemented** (in either reference impl): wire §3.1
|
||||
at ingest materialization **before that path handles any real remote content**, and author
|
||||
the normative gate as a new ingest-spec section (below) so both stacks wire it identically.
|
||||
- **Trigger B — a received-external-OKF-bundle merge path is added:** wire §3.2
|
||||
(`okf.import_bundle`) at that persist gate.
|
||||
- **Trigger C (weaker) — a manifest per-source `origin: external` field is added:** route
|
||||
external-origin `file`/`sql` extracts through the same §3.1 gate.
|
||||
|
||||
**Commons' specific role.** Because this is the normative shared core, the *contract* for the
|
||||
untrusted-source gate ("untrusted extracted text MUST be sanitized + scanned before persist;
|
||||
FAIL_SECURE halts materialization, never a silent commit") should be authored **here** — a
|
||||
future ingest-spec section, added when Trigger A/B nears — so MAF and the Claude-SDK
|
||||
implementation wire the identical guard. The executable wiring lives in each implementation
|
||||
repo; the *rule* lives in commons, mirroring how method-spec/ingest-spec already work.
|
||||
|
||||
**Coordination with consuming implementation repos (recorded 2026-07-16).** The Claude
|
||||
Agents SDK implementation (`claude-code-llm-wiki`) is running this same adoption task in
|
||||
parallel and has drawn the reciprocal boundary: its `shared/` is a **pull-only subtree of
|
||||
commons**, so guard wiring that touches `shared/` or the **ingest-spec** is owned by *this*
|
||||
commons session, not there — its plan covers only its **repo-local modules** (`ingest.py`,
|
||||
`verdicts.py`, `okf.py`). Division of labor, both directions:
|
||||
|
||||
- **Commons (here)** authors the normative untrusted-source *gate contract* (a future
|
||||
ingest-spec section) and MUST NOT edit implementation-repo modules.
|
||||
- **Each implementation repo** wires the guard in its own runtime modules and pulls the spec
|
||||
via subtree; it MUST NOT edit the shared ingest-spec in place (edits land in commons first,
|
||||
then `git subtree pull`, per README).
|
||||
|
||||
## 5. Honest limitations to carry into any future wiring (brief §8)
|
||||
|
||||
- **Semantic / factual poisoning is invisible** to the deterministic core — highest impact
|
||||
for a wiki. A plausible-but-wrong extracted value (wrong metric, wrong cost) passes clean.
|
||||
This system already has a strong mitigation: the **deterministic validator** (method §4)
|
||||
and **human expert gate** anchor the *numbers* the wiki feeds — the guard is defense in
|
||||
depth over the *text carrier*, not the semantic truth.
|
||||
- **Dormant / broken-link injection** — the guard's `link_graph` surfaces the dangling edge;
|
||||
blocking is a disposition call. Ingest's index-link contract (§6) is the natural place.
|
||||
- **Text-only, extracted-text-only** — if ingest ever pulls binary/file sources, extract
|
||||
text first, then scan with high-untrust provenance (brief §6).
|
||||
|
||||
## 6. Verification (testable criteria)
|
||||
|
||||
Run from the repo root:
|
||||
|
||||
1. Plan exists: `test -f docs/plan/2026-07-16-llm-ingestion-guard-adoption.md`
|
||||
2. Marker present, status first token: `grep -Eq '^llm-ingestion-guard: (planned|integrated|not-applicable) — ' STATE.md`
|
||||
3. STATE.md is LOCAL-ONLY: `git check-ignore -q STATE.md` (exit 0 = ignored, never committed)
|
||||
4. Both persist gates named, promotion gate excluded:
|
||||
`grep -q 'import_bundle' docs/plan/2026-07-16-llm-ingestion-guard-adoption.md` and
|
||||
`grep -q 'Do NOT wire the guard here' docs/plan/2026-07-16-llm-ingestion-guard-adoption.md`
|
||||
5. Scope guard — guard NOT wired yet (this repo has no runtime code):
|
||||
`! grep -rq 'llm_ingestion_guard' --include='*.py' .` (no Python files exist; the only
|
||||
`llm_ingestion_guard` references are the illustrative snippets in this plan)
|
||||
|
||||
## 7. Explicitly out of scope for this task
|
||||
|
||||
Implementing the guard, adding it as a dependency, editing ingest-spec.md/method-spec.md, or
|
||||
touching either implementation repo. This task **plans and records only.**
|
||||
111
shared/docs/plan/2026-07-20-f1-freetext-connector-direction.md
Normal file
111
shared/docs/plan/2026-07-20-f1-freetext-connector-direction.md
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
# F1 — fritekst-kilder i ingest-spec: retningsvurdering
|
||||
|
||||
**Status:** vurdering, ikke vedtak. Spec-en er IKKE endret. Beslutning tas av operatøren.
|
||||
**Foranledning:** `llm-ingestion-okf` v0.3.1 har ingen fritekst-connector; `file` er en streng
|
||||
CSV-leser og §5-body rendres som markdown-tabell med newline-kollaps. To uavhengige repo
|
||||
(`claude-playlist-corpus`, `claude-code-llm-wiki`) blokkeres. Spørsmålet stilt til commons:
|
||||
løses dette i `ingest-spec.md` (dør A) eller i bibliotekets dør B (innboks, ikke spec-styrt)?
|
||||
|
||||
---
|
||||
|
||||
## 1. Retning: løses i spec-en, i dør A
|
||||
|
||||
Fire grunner, i synkende vekt:
|
||||
|
||||
1. **Arkitekturregelen krever det.** §1: *«data reaches the model ONLY via OKF bundles»*, og
|
||||
bundlen materialiseres deterministisk FØR loopen. Fritekst er data. Ingenting i §1–§3
|
||||
skiller tabulært fra ustrukturert — utelukkelsen er et artefakt av at v1s `file`-connector
|
||||
ble en CSV-leser, ikke en prinsipiell grense. Skyves den vanligste kunnskapsbase-kilden ut
|
||||
til dør B, faller den samtidig utenfor provenance-stemplingen (§7), verdict-reservasjonen
|
||||
(§3), golden-regimet (§11) og determinismen (§10) — spec-ens dekning uthules der den betyr
|
||||
mest.
|
||||
2. **Sitatkravet peker samme vei.** Method-spec §9 krever *«file + exact text span +
|
||||
snippet»*. En span inn i en tabell-rendret, newline-kollapset body er i praksis ubrukelig;
|
||||
en verbatim dokument-body er det naturlige span-målet. Fritekst gjennom dør A gjør
|
||||
sitatkjeden bedre, ikke svakere.
|
||||
3. **Determinismen er lettere, ikke vanskeligere.** Verbatim bytes har ingen typekoercion,
|
||||
ingen tallformatering, ingen NULL-semantikk — hele §5s tabell-fallgruvefelt forsvinner.
|
||||
Det finnes ikke noe determinisme-argument for å holde fritekst ute.
|
||||
4. **Spec-en har allerede render-modusen — feilplassert.** §5 sier for `http`: *«the response
|
||||
body verbatim inside a fenced code block»*. Verbatim-render finnes altså, men er bundet til
|
||||
**transport** (`http`) i stedet for til **innholdsform**. Det er den faktiske defekten: §5
|
||||
konflaterer kildetype og body-form. F1 er symptomet.
|
||||
|
||||
**Grensen mot dør B holder likevel — men den går ikke ved innholdsform.** Den går ved
|
||||
*adresserbarhet + re-eksekverbarhet*: en kilde med manifest-deklarert, repeterbar `query` mot
|
||||
en adresserbar kilde hører i dør A (en transkripsjonsfil under `root` kvalifiserer). Materiale
|
||||
uten re-eksekverbar kilde-query — limt inn tekst, en e-post, et engangsdropp — hører i dør B.
|
||||
Bruker vi innholdsform som grense i stedet, havner halve kunnskapsbasen utenfor spec-en av en
|
||||
grunn som ikke er prinsipiell.
|
||||
|
||||
### Foreslått form (retning, ikke spec-tekst)
|
||||
|
||||
Ikke en ny kildetype `document` — den ville duplisere `file`s `root`- og
|
||||
grensesjekk-logikk. I stedet: **skill body-form fra kildetype.** Et nytt påkrevd
|
||||
extraction-felt (arbeidsnavn `render`) med `table` | `verbatim`. `file`+`table` = dagens
|
||||
CSV-leser; `file`+`verbatim` = fritekst-dokumentet som mangler; `http` beskrives retroaktivt
|
||||
som `verbatim` og blir dermed konsistent i stedet for et unntak. Ett felt, én rad i §12s
|
||||
kryssjekk-tabell, ingen ny kildetype.
|
||||
|
||||
## 2. Hva §5 må si om verbatim body-render
|
||||
|
||||
Seks punkter. De fire første er ikke valgfrie — dagens `http`-render mangler dem alle, så
|
||||
dette lukker et latent hull samtidig som det løser F1.
|
||||
|
||||
- **Bytes og dekoding.** Innholdet leses som bytes og dekodes som UTF-8, strengt. Dekodefeil
|
||||
er en ERROR — aldri erstatningstegn, aldri lossy koercion. Samme disiplin som §5s *«any
|
||||
other value type MUST fail (never silent coercion)»*.
|
||||
- **Linjeskift — her er «verbatim» nødt til å vike.** §5 krever LF-only og nøyaktig én
|
||||
avsluttende newline. CRLF/CR MÅ derfor normaliseres til LF, og etterfølgende blank plass ved
|
||||
EOF kollapses til nøyaktig én LF. Uten denne regelen motsier «verbatim» og «LF-only»
|
||||
hverandre, og golden-ekstraksjoner blir plattformavhengige. Spec-en MÅ si rett ut at
|
||||
renderen dermed er lossy mot kildens eksakte bytes — og at en eventuell kildehash regnes
|
||||
over **kildens** bytes, ikke de rendrede.
|
||||
- **Fencing — obligatorisk, med deterministisk bredde.** Fence-lengde =
|
||||
`max(3, lengste backtick-run i innholdet + 1)` (CommonMark-standard, deterministisk).
|
||||
- **Fencing er sikkerhetsbærende, ikke kosmetikk.** Ufenset markdown-passthrough er IKKE v1.
|
||||
Grunnen er konkret: method-spec §3 Step 1 navigerer ved å følge body-lenker
|
||||
(`](target.md)`), så et ufenset ingested dokument kan injisere navigasjonskanter utenom
|
||||
`index.md` og forsøke bundle-escape-stier. I tillegg rendres read-context som
|
||||
`## {type}: {title}`-seksjoner — et dokument med egne `##`-overskrifter visker ut
|
||||
seksjonsgrensene, og en ledende `---` kan leses som et nytt frontmatter-blokk. Fenset
|
||||
innhold har ingen av delene. (Ufenset passthrough = utvidelsespunkt.)
|
||||
- **`max_rows` for dokumenter.** Feltet er radorientert og meningsløst for fritekst. Enkleste
|
||||
konsistente lesning: for `verbatim` teller `max_rows` LF-delimiterte linjer, og overskridelse
|
||||
er ERROR (§8, aldri stille trunkering). **Åpen underbeslutning:** en 40 MB transkripsjon
|
||||
passerer en linjegrense lenge før den blir håndterbar — trengs et valgfritt `max_bytes` i
|
||||
tillegg? Anbefaling: ja, valgfritt, men dette er verdt et eksplisitt operatørvalg.
|
||||
- **`source_query` og provenance ellers:** uendret — relativ sti, whitespace-kollapset (§5/§7).
|
||||
|
||||
### `ingested_at`-determinismen bevares — med én felle å lukke eksplisitt
|
||||
|
||||
Verbatim-render leser ingen klokke. `ingested_at` forblir et eksplisitt påkrevd argument,
|
||||
stemplet ordrett; §5s regel er urørt. Fellen er en fristende «forbedring»: å stemple kildefilens
|
||||
mtime eller størrelse som ekstra provenance. Det MÅ forbys — git bevarer ikke mtime, så et
|
||||
golden-fixture ville gi ulikt resultat ved frisk utsjekking, og §11s byte-for-byte-krav brytes.
|
||||
Verdt en eksplisitt MUST NOT.
|
||||
|
||||
### §11 — nye seams
|
||||
|
||||
- Ny golden-case, konvensjon `examples/ingest-golden-verbatim/`. Fixturet MÅ inneholde et
|
||||
dokument med CRLF, en backtick-run ≥ 3, en ledende `---`, og ikke-ASCII — så escape-reglene
|
||||
fryses av golden i stedet for å leve i prosa.
|
||||
- Ny load-bearing rad: testen MÅ feile når fence-bredden slutter å vokse med innholdets
|
||||
lengste backtick-run (dvs. når ingested innhold kan bryte ut av fencen).
|
||||
|
||||
## 3. Sikkerhetsobservasjon (til operatøren, ikke en spec-endring)
|
||||
|
||||
Dette er første gang dør A materialiserer innhold som er **untrusted av opphav** selv om
|
||||
transporten er lokal og first-party: `root` eies av operatøren, men en transkripsjon eller
|
||||
artikkel under `root` er tredjeparts tekst. Guard-adopsjonsplanen
|
||||
([2026-07-16](2026-07-16-llm-ingestion-guard-adoption.md) §4) definerte Trigger A som «når
|
||||
`http`/MCP-kildetypen bygges». En fritekst-connector treffer samme untrusted-grense uten å gå
|
||||
via `http` — så Trigger A bør trolig omformuleres fra *transport* til *opphav*, og kan dermed
|
||||
inntreffe tidligere enn planen antar. Flagget her, ikke besluttet.
|
||||
|
||||
## 4. Hva dette IKKE avgjør
|
||||
|
||||
- Ingen spec-tekst er skrevet. §4, §5, §11 og §12 er urørt.
|
||||
- `render`-feltnavnet er et arbeidsnavn.
|
||||
- `max_bytes`-spørsmålet står åpent (§2).
|
||||
- Dør B (innboks) berøres ikke: den beholder materiale uten re-eksekverbar kilde-query.
|
||||
|
|
@ -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.
|
||||
177
shared/docs/plan/2026-07-23-d2-register-form-fix.md
Normal file
177
shared/docs/plan/2026-07-23-d2-register-form-fix.md
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
# D2 — register-form-fiks: to-utgangs-modell som løser ÅS#5
|
||||
|
||||
**Status:** forslag + kontraktutkast. Ingen global konvensjon er endret. Ratifiseres av operatøren.
|
||||
**Foranledning:** D2 (koordineringsregister — committet per-repo-bærer + generert pekerindeks) er
|
||||
blokkert av **ÅS#5**: en committet, fjern-hentbar bærer lekker intern koordineringsmetadata
|
||||
(repo-navn, statuser, arkitekturprosa) til offentlige speil. Commons er selve mønstereksemplet —
|
||||
åpen publisering **+** subtree-konsumert → `STATE.md` er allerede LOCAL-ONLY av nettopp denne
|
||||
grunn. Denne doken forfatter register-form-**kontrakten** HER (jf. driftsmodell: kontrakten
|
||||
forfattes i commons, wiringen i catalog/impl), til ratifisering inn i koordineringskonvensjonen.
|
||||
|
||||
**Ratifiserte valg (operatør, 2026-07-23):** to-utgangs-modell; leveranse = denne proposal-doken
|
||||
(ingen unilateral global edit).
|
||||
|
||||
---
|
||||
|
||||
## 1. Rotårsak: D2 konflaterer durabilitet med offentlighet
|
||||
|
||||
Roll-up-markøren har i dag én form — en linje i hvert repos `STATE.md`:
|
||||
|
||||
```
|
||||
<topic>: <status> — <fri prosa; kan navngi repo, arkitektur, blokkere>
|
||||
```
|
||||
|
||||
Første token etter `:` er maskinlesbar status; resten er menneskeprosa. `STATE.md` er LOCAL-ONLY
|
||||
(gitignored), og «roll-up-en» er en tverrsnittsvisning satt sammen ved å **grep-e
|
||||
`~/repos/*/STATE.md`**. Den assemblen er **manuell/ad-hoc** — det finnes ikke noe automatisert
|
||||
assembler-script (verifisert: `session-start.sh` injiserer kun *nærmeste enkelt*-STATE, ikke noe
|
||||
tverrsnitt). Det finnes altså ingen automatisert konsument å regressere.
|
||||
|
||||
D2 ville gjøre markøren **committet** slik at den overlever fresh clone / subtree-konsum *uten* en
|
||||
co-lokalisert LOCAL-ONLY STATE. Men «committet» drar **to uavhengige egenskaper** sammen:
|
||||
|
||||
- **Durabilitet** — reiser markøren med repoet (clone/subtree) uten STATE?
|
||||
- **Offentlighet** — tåler innholdet å bli speilet til en offentlig flate?
|
||||
|
||||
D2 valgte «committet» for durabilitet og fikk offentlighet på kjøpet. **Fiksen er å koble dem
|
||||
fra:** en markør kan være durabel uten å være offentlig-eksponert, og offentlig-eksponert innhold
|
||||
kan reduseres til det som trygt tåler speiling.
|
||||
|
||||
## 2. Fiksen: to utganger, aldri konflatert igjen
|
||||
|
||||
Registeret har **to distinkte utganger** med ulik målgruppe. De blandes aldri.
|
||||
|
||||
### Utgang A — rik lokal roll-up (LOCAL-ONLY)
|
||||
|
||||
- **Kilde:** `STATE.md`-markørlinjene (full `topic: status — prosa`), grep-et over
|
||||
`~/repos/*/STATE.md`.
|
||||
- **Målgruppe:** operatøren, lokalt. **Publiseres aldri.**
|
||||
- **Endring: ingen.** Dette er dagens mekanisme. Den forblir komplett og LOCAL-ONLY.
|
||||
|
||||
### Utgang B — offentlig status-token-indeks (VALGFRI, per repo)
|
||||
|
||||
- **Kilde:** en per-repo **minimal committet bærer** — status-token(s) *alene*, ingen prosa, ingen
|
||||
repo-intern arkitekturdetalj.
|
||||
- **Målgruppe:** enhver som kloner/konsumerer repoet (inkl. offentlige speil). Trygg fordi den kun
|
||||
bærer `topic: status`.
|
||||
- **Valgfri per repo:** et repo i offentlig-speil-klassen (commons) KAN velge å publisere ingenting
|
||||
→ det opptrer da ikke i utgang B. Det er «tverrsnitt-eksklusjon by design», ikke et tap.
|
||||
|
||||
Utgang B er en **projeksjon** av utgang A, bevisst strippet til den offentlig-trygge delmengden
|
||||
(status-token), og **opt-in per repo**. Rik prosa forlater aldri utgang A.
|
||||
|
||||
## 3. Bærer-grammatikk (normativ)
|
||||
|
||||
### 3.1 Utgang A-kilde — STATE.md-markøren (uendret)
|
||||
|
||||
Som i dag; LOCAL-ONLY. Ingen endring, tatt med for kontrast.
|
||||
|
||||
### 3.2 Utgang B-kilde — minimal committet bærer
|
||||
|
||||
- **Fil:** en committet fil i repo-rot. Navn (forslag): `.rollup-carrier`.
|
||||
- **Form:** én linje per tema, **status-token alene** — ingen em-dash, ingen prosa:
|
||||
|
||||
```
|
||||
llm-ingestion-guard: planned
|
||||
llm-ingestion-okf: not-applicable
|
||||
```
|
||||
|
||||
- **Vokabular:** status-token trekkes fra D3-status-vokabularet (`planned` / `in-progress` /
|
||||
`blocked` / `done` / `not-applicable` / `partial` / `deferred` / `active` …). D3 er eget spor;
|
||||
denne doken *refererer* vokabularet, definerer det ikke.
|
||||
- **Offentlig-trygghet er per repo, ikke absolutt:** selv `topic: status` avslører at repoet
|
||||
deltar i en intern koordinering. `.rollup-carrier` brukes derfor KUN når topic+status er
|
||||
ikke-sensitivt for det repoet. Er selv det sensitivt → bruk modus «fraværende» (§4.1).
|
||||
|
||||
## 4. Tre-modus builder-toleranse (avgjort ved register-build)
|
||||
|
||||
Register-builderen prober hvert repo og **tolererer fravær** — ingen global regel tvinger commit.
|
||||
Hver modus svarer til en repo-klasse:
|
||||
|
||||
### 4.1 Fraværende bærer
|
||||
Repoet committer ingenting. **Utgang B utelater det** (ingen feil); **utgang A** (lokal STATE-grep)
|
||||
dekker det fullt. → Offentlig-speil-klassen (**commons**).
|
||||
|
||||
### 4.2 Minimal committet bærer
|
||||
Repoet committer `.rollup-carrier` (`topic: status`, §3.2). Reiser med clone/subtree/offentlig
|
||||
speil. → Repo der topic+status er offentlig-trygt OG durabel offentlig-side-reise ønskes.
|
||||
|
||||
### 4.3 Privat sidekanal
|
||||
Bæreren bor på en privat-kun flate — en `*.local`-gitignored fil, coord-området
|
||||
(`~/.claude/coord/`, allerede lokalt/privat), eller en privat-remote-kun sti. Builderen leser den
|
||||
ved assembly av den **private/lokale** indeksen (utgang A-siden). → Privat repo (intet offentlig
|
||||
speil), subtree-konsumert, som vil ha markør-reise *innen* det private økosystemet uten offentlig
|
||||
eksponering (f.eks. `claude-code-llm-wiki`: «fullt privat, intet open/-speil» i dag).
|
||||
|
||||
## 5. Presedens når et repo har flere kilder
|
||||
|
||||
- **Utgang A** er autoritativt fra `STATE.md`-markøren (kanonisk state-of-play per
|
||||
kontinuitets-systemet).
|
||||
- **Utgang B** er autoritativt fra `.rollup-carrier`.
|
||||
- **Drift-invariant:** når STATE-markørens status ≠ committet bærers status, **vinner STATE**, og
|
||||
builderen SKAL varsle (den offentlige projeksjonen har drevet fra den lokale sannheten). Prosa
|
||||
promoteres aldri inn i utgang B; intet repo tvinges til å committe.
|
||||
|
||||
## 6. Commons' egen posisjon
|
||||
|
||||
Commons velger **modus «fraværende»** (§4.1): ingen `.rollup-carrier`, markøren blir i LOCAL-ONLY
|
||||
`STATE.md`. Begrunnelse er commons' definerende egenskap — åpen publisering + subtree-konsum — den
|
||||
samme som allerede holder `STATE.md` gitignored. Commons opptrer i utgang A (lokal grep), aldri i
|
||||
utgang B. **Interim-linjen i STATE («hold roll-up-markørene LOCAL-ONLY, aksepter
|
||||
tverrsnitt-eksklusjon») blir dermed den permanente designen for denne klassen**, ikke en midlertidig
|
||||
innrømmelse.
|
||||
|
||||
## 7. Plassering av grammatikken (anbefaling — avgjøres ved ratifisering)
|
||||
|
||||
STATE peker grammatikken til «koordineringskonvensjonen». Men coord-README-en er eksplisitt
|
||||
*postkasse-transport* (Regel 1: «Postkasse, ikke state»; Regel 2: «Ingen varige beslutninger bor
|
||||
her»). Roll-up-registeret er **state-roll-up, ikke transport** — å veve det inn i postkasse-reglene
|
||||
grumser Regel 1.
|
||||
|
||||
**Anbefaling:** plasser det som en **avgrenset register-del i coord-området**, eksplisitt adskilt
|
||||
fra postkasse-transporten — f.eks. `~/.claude/coord/register.md`, eller en klart merket seksjon
|
||||
«Register — state-roll-up, distinkt fra postkasse-transporten». Det honorer STATEs retning
|
||||
(koordineringsflaten er den globale hjemmen for tverrsnitt-mekanikk) samtidig som coord Regel 1/2
|
||||
holdes rene. Alternativet — å legge det ved STATE-konvensjonen i global `CLAUDE.md` — ble vurdert:
|
||||
forkastet fordi registeret er en *tverr-repo* mekanisme, ikke en enkelt-repos STATE-form.
|
||||
|
||||
## 8. Avhengigheter og grenser
|
||||
|
||||
- **D3-status-vokabular** (eget spor) leverer status-token-settet. Denne kontrakten refererer det;
|
||||
definerer det ikke.
|
||||
- **catalog** eier builderens *wiring* (det faktiske register-build-scriptet + adversariske akser).
|
||||
Denne doken er den framework-nøytrale kontrakten — commons rører ikke catalog-moduler.
|
||||
- **Ingen global konvensjon endres her.** Ratifisering (den globale register-seksjonen +
|
||||
catalog-wiring) er neste steg, etter operatør-OK.
|
||||
|
||||
## 9. Verifisering
|
||||
|
||||
Testbare kriterier som beviser at fiksen er korrekt og ikke-regressiv:
|
||||
|
||||
- **V1 (ingen regresjon i utgang A):** `for f in ~/repos/*/STATE.md; do grep -nE '^[a-z][a-z0-9-]+:
|
||||
(planned|not-applicable|blocked|in-progress|done|partial|deferred|active)' "$f"; done` gir fortsatt
|
||||
begge temaene på tvers av repo (uendret mot i dag).
|
||||
- **V2 (commons fraværende fra utgang B):** `git ls-files | grep -i 'rollup-carrier'` i commons →
|
||||
tomt; commons' markør vises fortsatt i V1-grepet. Bekrefter tverrsnitt-eksklusjon by design.
|
||||
- **V3 (sidekanal er gitignored):** `git check-ignore .rollup-carrier.local` → matcher (`*.local.md`
|
||||
dekker ikke bare `.local` — bekreft mønsteret ved ratifisering; utvid `.gitignore` om nødvendig).
|
||||
- **V4 (offentlig-trygghet):** en `.rollup-carrier` inneholder ingen prosa/em-dash:
|
||||
`grep -n '—' .rollup-carrier` → tomt. Regex-gate builderen kan håndheve.
|
||||
- **V5 (drift-varsel):** når STATE-markørens status ≠ bærers status, varsler builderen (§5). Spec-nivå-
|
||||
assertion; catalog-wiring implementerer.
|
||||
|
||||
**Nøkkelantakelser — eksplisitt testet:**
|
||||
|
||||
- *«Ingen automatisert roll-up-konsument å knuse.»* Testet: `grep -rl 'repos/\*/STATE'
|
||||
~/.claude/{scripts,hooks}` → kun `session-start.sh`, som injiserer nærmeste *enkelt*-STATE, ikke
|
||||
tverrsnitt. **Verifisert 2026-07-23.**
|
||||
- *«Commons' offentlig-lekkasje er prospektiv, ikke live.»* Testet: `git remote -v` → kun privat
|
||||
Forgejo (`git.fromaitochitta.com`), intet `open/`-speil. **Verifisert 2026-07-23.** Fiksen er
|
||||
altså forebyggende for klassen, ikke en akutt tetting.
|
||||
|
||||
## 10. Ratifiseringssti
|
||||
|
||||
1. Operatør-OK på denne kontrakten.
|
||||
2. `coord-send` til catalog (builder-wiring) + broadcast til deltakende repo (bærer-modus-valg per repo).
|
||||
3. Ratifiser inn i koordineringskonvensjonen (register-del, §7).
|
||||
4. catalog wirer builderen med de tre modusene + V4/V5-gatene.
|
||||
140
shared/docs/plan/2026-07-23-d3-status-vocabulary.md
Normal file
140
shared/docs/plan/2026-07-23-d3-status-vocabulary.md
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
# D3 — status-vokabular: kanonisk token-sett for roll-up-registeret
|
||||
|
||||
**Status:** RATIFISERT 2026-07-24 — landet i `~/.claude/coord/register.md` (kanonisk 7-token-sett +
|
||||
de to reglene inline; `active` og det åpne `…` fjernet). Denne doken er kontrakt-kilden.
|
||||
**Foranledning:** D2-register-form-fiksen (`docs/plan/2026-07-23-d2-register-form-fix.md`, `c66ccc3`)
|
||||
landet register-*formen* og ble ratifisert inn i `~/.claude/coord/register.md` (2026-07-23). Både
|
||||
kontrakten og den globale register-fila **refererer** et status-token-vokabular men **definerer det
|
||||
ikke** — begge peker eksplisitt til «D3-sporet» (register.md linje 42–44, 73; D2 §3.2). Denne doken
|
||||
forfatter det vokabular-**kontrakten** HER (jf. driftsmodell: kontrakten forfattes i commons,
|
||||
wiringen i catalog), til ratifisering inn i register-fila.
|
||||
|
||||
**Ratifiserte valg (operatør, 2026-07-23):** kanonisk 7-token-sett (`active` foldet inn i
|
||||
`in-progress`); to normative regler (`partial` transitorisk; `deferred` ≠ `blocked`); leveranse =
|
||||
denne proposal-doken (ingen unilateral global edit), speiler D2-mønsteret.
|
||||
|
||||
---
|
||||
|
||||
## 1. Gapet: vokabularet er referert, ikke definert
|
||||
|
||||
Roll-up-markøren har formen `topic: status — prosa` (utgang A / STATE.md) og `topic: status`
|
||||
(utgang B / `.rollup-carrier`). Første token etter `:` er den maskinlesbare statusen. D2 fastslo at
|
||||
token-settet trekkes fra «D3-status-vokabularet» men lot definisjonen stå åpen — register.md lister
|
||||
et *kandidatsett* med et etterfølgende `…` (åpen-endet), ikke en normativ definisjon.
|
||||
|
||||
**Ground truth (verifisert 2026-07-23):** kun to tokens er i faktisk bruk på tvers av
|
||||
`~/repos/*/STATE.md` — `planned` (5×) og `not-applicable` (1×). Resten av settet er prospektivt.
|
||||
Dette er altså en definisjon *før* bred bruk: D3 setter den kanoniske betydningen slik at fremtidige
|
||||
markører er konsistente, ikke en migrasjon av eksisterende data.
|
||||
|
||||
## 2. Kanonisk token-sett (normativt)
|
||||
|
||||
Syv tokens. `active` (i register.md-kandidatlista) droppes — det overlapper `in-progress` uten skarp
|
||||
semantisk forskjell; en ekstra synonym svekker den maskinlesbare disiplinen.
|
||||
|
||||
| Token | Semantikk | Terminal? |
|
||||
|-------|-----------|-----------|
|
||||
| `planned` | Avgrenset scope, ikke startet. | Nei |
|
||||
| `in-progress` | Aktivt arbeid, én eier, forventet å nå `done`. | Nei |
|
||||
| `partial` | **Transitorisk.** Noen konstituerende deler ferdig, andre utestående — potensielt under ulike eiere. MÅ bære eier + neste-steg i utgang-A-prosaen (§3.1). Aldri en terminal hviletilstand. | Nei |
|
||||
| `blocked` | Kan ikke fortsette — venter på en **ekstern** avhengighet. Navngir blokkereren i prosaen. | Nei |
|
||||
| `deferred` | **Bevisst** utsatt — et valg om å ikke gjøre nå, ikke venting på noe. Distinkt fra `blocked` (§3.2). | Nei |
|
||||
| `done` | Fullført, ingen utestående arbeid. | Ja |
|
||||
| `not-applicable` | Temaet gjelder ikke dette repoet (f.eks. spec-eier uten kjørbar pipeline). | Ja |
|
||||
|
||||
**Terminal-klassifiseringen er informativ, ikke håndhevet:** den skiller hviletilstander (`done`,
|
||||
`not-applicable`) fra tilstander som forventer videre bevegelse. Den muliggjør en fremtidig
|
||||
staleness-heuristikk (en ikke-terminal markør som ikke har endret seg på lenge fortjener tilsyn),
|
||||
men denne doken *innfører ingen slik gate* — det finnes ingen pålitelig tidsstempel-mekanisme i
|
||||
markør-formen, og å oppfinne en her ville være spekulativt scope.
|
||||
|
||||
## 3. To normative regler
|
||||
|
||||
### 3.1 `partial` er transitorisk — krever eier + neste-steg
|
||||
|
||||
`partial` beskriver arbeid delt i konstituerende deler med ulik framdrift, ofte ulike eiere. Uten en
|
||||
eksplisitt eier + neste-steg blir det en åpen «halvferdig»-tilstand uten vei ut. Regelen:
|
||||
|
||||
> En `partial`-markør hvis utgang-A-prosa mangler både en eier og et neste-steg er **malformert**.
|
||||
|
||||
Dette er en disiplin på **utgang A** (STATE-prosaen), ikke utgang B. Utgang B bærer kun token-et
|
||||
(`topic: partial`) og er offentlig-trygg per D2 §3.2 — eier/neste-steg-prosaen forlater aldri utgang
|
||||
A. Regelen kolliderer derfor ikke med D2s offentlig-trygghet (V4): den håndheves der prosaen finnes.
|
||||
|
||||
**Grounding:** «form→catalog, plumbing→okf, tillit→guard, spec→commons» (fra
|
||||
`llm-ingestion-okf`-markøren) er nettopp den fler-eier-delingen `partial` fanger — et tema der én
|
||||
eiers del kan være ferdig mens en annens er utestående.
|
||||
|
||||
### 3.2 `deferred` ≠ `blocked` — frivillig vs. ufrivillig
|
||||
|
||||
- `blocked`: arbeidet *kan ikke* fortsette; en ekstern avhengighet står i veien. Ufrivillig.
|
||||
- `deferred`: arbeidet *kunne* fortsette, men er bevisst nedprioritert. Frivillig.
|
||||
|
||||
Skillet bærer beslutnings-informasjon: `blocked` inviterer «løs blokkereren»; `deferred` sier
|
||||
«dette ligger med vilje, ikke rør». Å konflatere dem skjuler hvorvidt noen venter på noe.
|
||||
|
||||
**Grounding (begge fra dagens økosystem):**
|
||||
- **`deferred`:** commons' «broadcast (bærer-modus per repo) **bevisst utsatt** — builder tolererer
|
||||
fravær → ingen andre repo blokkeres» (STATE). Ingenting venter; valgt å ligge. Tekstbok `deferred`.
|
||||
- **`blocked`:** catalogs «ÅS#5-gatede STEG 0» var `blocked` på D2-ratifiseringen inntil commons
|
||||
varslet via coord (som *låste opp* steget). Ekstern avhengighet, ufrivillig — tekstbok `blocked`.
|
||||
|
||||
## 4. Konsekvens for register.md ved ratifisering
|
||||
|
||||
Ratifisering erstatter register.md-kandidatlista (linje 42–44) med det kanoniske settet:
|
||||
|
||||
- Fjern `active` (foldet inn i `in-progress`).
|
||||
- Fjern det åpen-endede `…` — settet er lukket og normativt.
|
||||
- Legg til de to reglene (§3.1, §3.2) eller en peker til denne doken.
|
||||
|
||||
Ingen eksisterende markør må endres: `planned` og `not-applicable` (de eneste i bruk) er begge i det
|
||||
kanoniske settet.
|
||||
|
||||
## 5. Avhengigheter og grenser
|
||||
|
||||
- **D2** (`c66ccc3`, register.md) leverer *formen* som konsumerer dette vokabularet. D3 fyller
|
||||
vokabular-referansen D2 lot stå åpen. D3 endrer **ikke** to-utgangs-modellen, bærer-grammatikken
|
||||
eller builder-modusene.
|
||||
- **catalog** eier builderens *wiring*. En builder KAN håndheve §3.1-gaten (regex på `partial`-linjer
|
||||
i utgang-A-kilden) og bør avvise ukjente tokens utenfor det kanoniske settet — men det er
|
||||
catalog-wiring, ikke denne kontrakten. Commons rører ikke catalog-moduler.
|
||||
- **Ingen global konvensjon endres her.** Ratifisering (register.md-oppdateringen) er neste steg,
|
||||
etter operatør-OK.
|
||||
|
||||
## 6. Verifisering
|
||||
|
||||
Testbare kriterier som beviser at kontrakten er korrekt og ikke-regressiv:
|
||||
|
||||
- **V1 (ingen regresjon):** de to tokens i faktisk bruk er begge i det kanoniske settet.
|
||||
`grep -rhoE '^[a-z][a-z0-9-]+: (planned|not-applicable)' ~/repos/*/STATE.md` → treff; ingen
|
||||
eksisterende markør faller utenfor settet.
|
||||
- **V2 (`active` er ikke i bruk — trygt å droppe):**
|
||||
`grep -rhE '^[a-z][a-z0-9-]+: active\b' ~/repos/*/STATE.md` → tomt. Bekrefter at å folde `active`
|
||||
inn i `in-progress` ikke bryter noen levende markør. **Verifisert 2026-07-23: tomt.**
|
||||
- **V3 (register.md refererer, definerer ikke — gapet er reelt):**
|
||||
`grep -n 'D3' ~/.claude/coord/register.md` → linjene som utsetter til D3-sporet (i dag 42–44, 73).
|
||||
Etter ratifisering skal disse erstattes av den normative definisjonen.
|
||||
- **V4 (`partial`-gaten er utgang-A-lokal, kolliderer ikke med D2 V4):** en `partial`-linje bærer
|
||||
eier + neste-steg kun i prosa (etter em-dash); utgang B (`.rollup-carrier`) forblir prosa-fri per
|
||||
D2 §3.2. Gaten `partial ⇒ prosa har eier+neste-steg` og D2s gate `.rollup-carrier har ingen prosa`
|
||||
gjelder to ulike utganger og kan begge holde samtidig.
|
||||
- **V5 (`deferred`/`blocked`-skillet er observerbart):** dagens `deferred`-case (commons' bevisst
|
||||
utsatte broadcast) og `blocked`-case (catalogs ÅS#5-gatede STEG 0, nå løst) er distinkte i
|
||||
økosystemet (§3.2 grounding) — skillet koder reell beslutnings-informasjon, ikke en synonym.
|
||||
|
||||
**Nøkkelantakelser — eksplisitt testet:**
|
||||
|
||||
- *«Vokabularet er referert men udefinert i register.md.»* Testet: `grep -n 'D3' register.md` →
|
||||
linje 42–44 («D3 er eget spor; her *refereres* vokabularet, defineres ikke») + linje 73
|
||||
(«Status-vokabularet: D3-sporet»). **Verifisert 2026-07-23.**
|
||||
- *«Kun `planned` + `not-applicable` er i bruk; resten er prospektivt.»* Testet:
|
||||
`grep -rhoE ... ~/repos/*/STATE.md | sort | uniq -c` → `planned` 5×, `not-applicable` 1×, ingen
|
||||
andre. **Verifisert 2026-07-23.** D3 er en definisjon før bred bruk, ikke en migrasjon.
|
||||
|
||||
## 7. Ratifiseringssti
|
||||
|
||||
1. ~~Operatør-OK på dette vokabularet + de to reglene.~~ **GJORT** (2026-07-23).
|
||||
2. ~~Ratifiser inn i `~/.claude/coord/register.md`: erstatt kandidatlista med det kanoniske settet +
|
||||
reglene; fjern `active` og `…`.~~ **GJORT** (2026-07-24): ny «Status-vokabular»-seksjon (tabell +
|
||||
regel 1/2) + kontrakt-kilde-peker; vokabular-bulleten og Eierskap-pekeren oppdatert.
|
||||
3. catalog kan (valgfritt, deres wiring) håndheve §3.1-gaten + avvise ukjente tokens i builderen. — GJENSTÅR (catalog-spor).
|
||||
530
shared/docs/plan/2026-07-25-amendment-underlag.md
Normal file
530
shared/docs/plan/2026-07-25-amendment-underlag.md
Normal file
|
|
@ -0,0 +1,530 @@
|
|||
# Amendment-underlag — hva frossen tekst sier i dag, per køpunkt
|
||||
|
||||
**Status:** underlag. Spec-en er IKKE endret, og dette dokumentet foreslår INGEN ny tekst.
|
||||
Ny tekst er pakken operatøren ratifiserer, og den eies oppstrøms (`portfolio-optimiser`,
|
||||
sammen med `portfolio-optimiser-claude`s tekstforslag). Dette dokumentet svarer bare på
|
||||
spørsmålet commons kan svare på uten å foregripe ratifiseringen: **hvilke seksjoner røres,
|
||||
hva står der i dag (fil + linje), og hva koster endringen mot fasit-bundelen.**
|
||||
|
||||
**Foranledning:** `portfolio-optimiser` klarerte 2026-07-25 (05:09Z, punkt 3) generelt
|
||||
underlag som trygt arbeid som ikke invalideres av at pakken deres holdes: *«preparing GENERAL
|
||||
underlag (which spec sections those five stories touch, what the frozen text says today) is
|
||||
safe work that will not be invalidated»*. Dette er det arbeidet.
|
||||
|
||||
**Grunnlag:** commons HEAD `a67a243`. Normativ spec-tekst er uendret siden `bfa5a9b`
|
||||
(ingest-spec, D1-stempelmodellen) / `9801d35` (method-spec §3 Steg 1, Q3-navigasjonen) /
|
||||
`b641741` (nav-golden-fixtures) — alle 2026-07-21. Hver sitat-blokk under er hentet fra HEAD
|
||||
og linjeankeret verifisert, ikke gjengitt fra hukommelse.
|
||||
|
||||
> **Leseanvisning for konsumenter (tilføyd 2026-07-26).** Alle linjeankere i dette dokumentet
|
||||
> gjelder **commons HEAD**. Konsumentene er pinnet på eldre `shared/`-subtrær, og et
|
||||
> linjenummer overlever ikke en pull-grense: `portfolio-optimiser-claude` står på `7aa53fc`,
|
||||
> der `method-spec.md` er 441 linjer og §12 begynner på `:413` — mot 464 linjer og `:436` her.
|
||||
> **Sitér seksjon + ordrett tekst; linjenummeret er en bekvemmelighet, aldri ankeret.** Et
|
||||
> linjenummer sitert over en pull-grense er et *premiss*, ikke et faktum (metodenote fra
|
||||
> `portfolio-optimiser-claude`, 2026-07-26 — de fant den på dette dokumentets egne sitater).
|
||||
|
||||
---
|
||||
|
||||
## 0. Køen, avstemt
|
||||
|
||||
To korreksjoner fra `portfolio-optimiser` (16:11Z) er **godtatt**, begge verifisert:
|
||||
|
||||
1. **Tittel-whitespace er ikke et eget punkt** — det ER amendment-innholdet i S3.5. Vår
|
||||
telling dobbeltførte det. (Deres kø-linje `(a) commons-amendment tittel-whitespace → S3.5`
|
||||
ble lest som «punkt ved siden av», ikke «innholdet i».)
|
||||
2. **B1 er ute av pakken** — commons-eid, ingen adferds-interesse oppstrøms. Bæres separat
|
||||
(§8 her).
|
||||
|
||||
Én korreksjon går **tilbake** oppstrøms, og den er grunnen til at underlaget er verdt å
|
||||
skrive før pakken lukkes:
|
||||
|
||||
3. **De to oppstrøms-enumerasjonene er ikke den samme mengden.** `portfolio-optimiser`
|
||||
teller fem *stories* (S2.7 / S2.2–2.4 / S3.2 / S3.5 / S4.0); `portfolio-optimiser-claude`
|
||||
teller fem *D-A-punkter*. Bare tre par binder (S2.7 = D-A#1, S3.2 = D-A#4, S4.0 = D-A#2).
|
||||
De to gjenstående D-A-punktene har **ingen story-etikett** oppstrøms:
|
||||
- **D-A#3 (ledende `/`)** — trenger ingen amendment i det hele tatt. Frossen tekst i
|
||||
commons HEAD sier allerede det punktet ber om (§3 under). **Årsaken er repo-avhengig**
|
||||
(begge konsumenter svarte, med hver sin — begge er sanne): drift over pull-grensen hos
|
||||
`portfolio-optimiser-claude`, målt konformitetsgap i `okf.py` hos `portfolio-optimiser`.
|
||||
Anbefalingen er den samme uansett hvilken som gjelder hvor.
|
||||
- **D-A#5 (hovedbok-projeksjoner)** — er reell og har ingen tekst å endre (§6 under), men
|
||||
mangler story-etikett. Ratifiserer operatøren «pakken» slik `portfolio-optimiser`
|
||||
beskriver den, faller D-A#5 utenfor.
|
||||
|
||||
Netto, fra commons' side: pakken er deres seks **pluss** hovedbok-kontrakten = **sju
|
||||
ratifiserbare punkter**, med D-A#3 strøket som amendment. B1 utenfor, som avtalt.
|
||||
Tallet er ikke poenget — membership er.
|
||||
|
||||
**S2.2–2.4 (D-B)** var det ene punktet commons ikke kunne ankre. **Substansen kom 2026-07-26**
|
||||
og er ført i §7.1–7.2: D-B er en *scope*-beslutning, ikke spec-tekst. **To av de tre gatede
|
||||
storiene viste seg UGATED** — S2.2 fordi §4s `http`-punkt normativt plasserer MCP-konnektorer i
|
||||
`http`-familien (MUST på samme kontrakter), S2.4 fordi berikelsen holder seg innenfor §8s tre
|
||||
felt. **Kun S2.3 (`doc`) krever amendment.** Begge frigjøringene kom av ankring i frossen tekst,
|
||||
ingen av dem av en endring.
|
||||
|
||||
---
|
||||
|
||||
## 1. S2.7 / D-A#1 — nominal feasibility skal GATE, + IR-invariant `low ≤ unit_cost ≤ high`
|
||||
|
||||
**Seksjoner:** `method-spec.md` §3 Steg 4 (`:150-166`), §7.1 (`:335-346`), §7.2 (`:348-365`),
|
||||
§12 (`:458`).
|
||||
|
||||
**Hva frossen tekst sier i dag.** Den nominale grensen *beregnes*, og bare `p90` *blokkerer*:
|
||||
|
||||
> **`method-spec.md:157-160`** (§3 Steg 4, punkt 2)
|
||||
> 2. **Feasibility bound:** the maximum feasible saving is capped at a policy fraction
|
||||
> (0.30) of the affected items' total cost. *(reference: computed with an LP solve whose
|
||||
> closed form here is `0.30 × Σ quantity·unit_cost`; a missing solver MUST escalate, never
|
||||
> silently fall back.)*
|
||||
|
||||
> **`method-spec.md:163-166`** (§3 Steg 4, punkt 4)
|
||||
> 4. **Structural block:** a claim above the optimistic feasible bound (`p90`) yields a
|
||||
> **rejection that is a distinct type from a validated proposal** …
|
||||
|
||||
Punkt 2 sier `capped at`, men gir ingen avvisningsregel; punkt 4 er den ENESTE blokkeringen
|
||||
og bruker `p90`. Påstanden «beregnes, men bare p90 blokkerer» holder altså mot teksten.
|
||||
|
||||
§7.2 sier det samme eksplisitt om hva assertionen betyr:
|
||||
|
||||
> **`method-spec.md:356-358`** (§7.2)
|
||||
> … The meaningful assertion is `validates` = true (claimed ≤ `p90`); the frozen numbers are
|
||||
> the regression net.
|
||||
|
||||
`nominal_feasible` er et frosset felt (`:352-356`, kryssjekk `:458`) — det er allerede
|
||||
normativt *rapportert*, bare ikke *gatende*.
|
||||
|
||||
**IR-invarianten finnes ikke.** §7.1 har én construction-time-invariant, og den handler om
|
||||
noe annet:
|
||||
|
||||
> **`method-spec.md:343-344`** (§7.1)
|
||||
> - Construction-time invariant: the claimed saving MUST NOT exceed the affected items' own
|
||||
> total (`Σ quantity·unit_cost`); violation is a schema error, not a validator rejection.
|
||||
|
||||
`assumptions` er definert som band per kostkode (`:341-342`), men ingenting binder
|
||||
`unit_cost` til å ligge INNI sitt eget band. Hullet er reelt.
|
||||
|
||||
**Målt mot fasit-bundelen (`examples/bygg-energi-mikro/`) — begge endringene er byte-nøytrale:**
|
||||
|
||||
| Sjekk | Verdi i fasit | Konsekvens |
|
||||
|---|---|---|
|
||||
| `claimed_saving_nok` vs. `nominal_feasible` | `30000` vs. `90000.0` | claimed ≤ nominal → en nominal gate endrer ikke `validates` (fortsatt `true`) |
|
||||
| `unit_cost` vs. eget band | `1.0` i `[0.70, 1.40]` | invarianten er allerede oppfylt → fixture trenger ingen endring |
|
||||
|
||||
Kilder: `examples/bygg-energi-mikro/golden.json` (validator-blokken),
|
||||
`examples/bygg-energi-mikro/validator-input.json` (`affected_items[0]`, `assumptions`).
|
||||
|
||||
**Klassifisering:** ekte hull i frossen tekst (begge deler). Gratis mot goldenbytene.
|
||||
**Operatør-spørsmålet:** skal nominal-grensen gi samme *type* avvisning som p90-blokket
|
||||
(`:163-166` sier «distinct type … carrying the claimed and feasible figures … and no
|
||||
percentiles») eller en egen? Teksten har i dag én avvisningsform, og den er definert av p90.
|
||||
|
||||
---
|
||||
|
||||
## 2. S3.2 / D-A#4 — seedet dom skal nøkles på SINE EGNE features
|
||||
|
||||
**Seksjoner:** `method-spec.md` §3 Steg 1, experience fold (`:105-123`).
|
||||
|
||||
> **`method-spec.md:108-114`**
|
||||
> - The retrieval query key is the bundle's candidate features, read from the IR projection
|
||||
> (§7.1) — available *before* any proposal exists.
|
||||
> - Seeding: every `type: verdict` file in the bundle becomes a store entry keyed on those
|
||||
> candidate features, with `decision` from frontmatter (default `approved`) …
|
||||
|
||||
`those candidate features` refererer tilbake til forrige kulepunkt — bundelens ENE
|
||||
IR-projeksjon. Frossen tekst sier altså eksplisitt det punktet vil bort fra: hver seedet dom
|
||||
arver bundelens projeksjonsnøkkel, ikke sin egen. Hullet er reelt og teksten er entydig.
|
||||
|
||||
**Berøringsflate videre:** nøkkelen er det rangeringen (`:115-120`) og id-mintingen
|
||||
(§4.2 `:254-260`) hviler på. `affected_codes` / `measure_type` / `claimed_saving_nok` er
|
||||
feltene en «egen-features»-nøkling må komme fra, og §4.2 `:250-252` sier at `description`
|
||||
bevisst er utenfor både likhet og minting. En amendment her må si hvor en seed-fils egne
|
||||
features LESES fra (frontmatter? egen projeksjon?) — det er den åpne enden, ikke prinsippet.
|
||||
|
||||
**Målt:** fasit-bundelen har én seed (`verdict-led-fro.md`) og én projeksjon, så
|
||||
`golden.json` kan ikke skille gammel og ny nøkling. Endringen er byte-nøytral her, og
|
||||
fasiten er derfor ikke et vern mot regresjon på dette punktet.
|
||||
|
||||
**Klassifisering:** ekte hull. **Operatør-spørsmålet:** kilden for en seeds egne features.
|
||||
|
||||
---
|
||||
|
||||
## 3. D-A#3 — ledende `/` : INGEN amendment nødvendig
|
||||
|
||||
**Dette punktet er allerede normativt i commons HEAD** (landet `9801d35`, 2026-07-21):
|
||||
|
||||
> **`method-spec.md:67-71`** (§3 Steg 1)
|
||||
> - Navigation starts at `index.md` and follows its intra-bundle markdown cross-links
|
||||
> (`](target.md)`). A target is resolved **relative to the bundle** and boundary-checked
|
||||
> fail-closed (below): a leading `/` denotes the **bundle root** (NEVER a filesystem-absolute
|
||||
> path), any other form is relative to the linking file's own directory — so a target MAY
|
||||
> address a nested directory (`sub/index.md`, `/a/b.md`).
|
||||
|
||||
Og §11 har allerede rød-betingelsen som feiler hvis en implementasjon hopper over den:
|
||||
|
||||
> **`method-spec.md:425`** (§11, «Navigation boundary»)
|
||||
> … an escaping cross-link (`..`, a filesystem-absolute path, or a bundle-root `/` read as
|
||||
> filesystem-absolute) is followed, a malformed target … is raised instead of skipped, or a
|
||||
> legitimate nested in-bundle link is skipped
|
||||
|
||||
Teksten sier også selv at den ERSTATTET den gamle skip-heuristikken (`:73-74`).
|
||||
|
||||
**Klassifisering:** **drift over pull-grensen** — verken spec-hull eller konformitetsavvik.
|
||||
Ligger et forslag om å «endre» §3 Steg 1 her, endrer det tekst som allerede sier det forslaget
|
||||
vil oppnå — og risikoen er at ratifiseringen omformulerer en fungerende regel. **Meldt
|
||||
oppstrøms.**
|
||||
|
||||
> **Årsaks-korreksjon (2026-07-26).** Første utgave av dette avsnittet klassifiserte D-A#3 som
|
||||
> «konformitetsgap i implementasjonen» og skrev at forslaget beskrev implementasjonens
|
||||
> oppførsel framfor specens. **Det var feil årsak.** `portfolio-optimiser-claude` målte det mot
|
||||
> eget tre og svarte at deres `shared/method-spec.md` greper 0 for «bundle root» og
|
||||
> «filesystem-absolute» — fordi teksten landet i `9801d35`, og:
|
||||
>
|
||||
> ```
|
||||
> git merge-base --is-ancestor 9801d35 7aa53fc -> false (reprodusert her)
|
||||
> ```
|
||||
>
|
||||
> Deres split er `7aa53fc`; driften er 15 commits per 2026-07-26. De står i «ikke pullet»
|
||||
> **bevisst** — avtalen er samme commons-commit i begge repo før noen bygger. D-A#3 var altså
|
||||
> korrekt formulert mot den frosne teksten de har lov til å se; regelen landet oppstrøms
|
||||
> etterpå. **Anbefalingen er uendret** (punktet ut av pakken, siden regelen er normativ når
|
||||
> pakken lander), men den følger av drift, ikke av et avvik hos dem.
|
||||
>
|
||||
> Lærdommen generaliserer og er derfor løftet til leseanvisningen øverst: **samme felle venter
|
||||
> på ethvert punkt målt mot en bevegelig frossen tekst mens konsumentene er pinnet.**
|
||||
|
||||
> **Andre korreksjon, samme dag — årsaken er REPO-AVHENGIG, og begge svar er sanne.**
|
||||
> `portfolio-optimiser` svarte kort etter og klassifiserte D-A#3 som **«et KONFORMITETSGAP i
|
||||
> implementasjonen vår — ikke et spec-hull»**, med en konkret årsak: *«okf.py hopper over enhver
|
||||
> lenke med `/`»*. Det er den motsatte klassifiseringen av den `portfolio-optimiser-claude` ga.
|
||||
>
|
||||
> **Ingen av dem tar feil.** De beskriver to forskjellige repo:
|
||||
>
|
||||
> | Repo | Har `9801d35`? | Årsak der |
|
||||
> |---|---|---|
|
||||
> | `portfolio-optimiser-claude` | nei (pinnet `7aa53fc`) | **drift** — regelen finnes ikke i teksten de har |
|
||||
> | `portfolio-optimiser` | nei (pinnet `7aa53fc`) | **konformitetsgap** — de har målt eget avvik i `okf.py` og har det på operatørkøen som implementasjonssak |
|
||||
>
|
||||
> Begge konsumenter står på samme commons-commit (`7aa53fc`, bekreftet av begge uavhengig), så
|
||||
> pullen er én koordinert handling, ikke en opprydding etter usynk.
|
||||
>
|
||||
> **Dette avsnittets egen historie er poenget:** første utgave ga én universell årsak, korreksjonen
|
||||
> ga en annen universell årsak, og begge var for brede. «Årsaken til D-A#3» er ikke én ting når
|
||||
> konsumentene er pinnet og har ulik implementasjonstilstand. Anbefalingen har vært uendret hele
|
||||
> veien — punktet ut av pakken — og den avhenger ikke av hvilken årsak som gjelder hvor.
|
||||
|
||||
---
|
||||
|
||||
## 4. S4.0 / D-A#2 — kostbaseline-forankring (`cost-baseline.json`)
|
||||
|
||||
**Seksjoner:** `method-spec.md` §7 (`:330-333`), §7.1 (`:335-346`), §12 (`:441-464`).
|
||||
|
||||
**Frossen tekst har ingen baseline-forankring** — `grep -c 'cost-baseline'` over begge
|
||||
spec-er: **0 treff**. Det som finnes, og som en ubetinget baseline kolliderer med:
|
||||
|
||||
> **`method-spec.md:332-333`** (§7)
|
||||
> The shared example bundle ships two JSON files that are **the only ground truth** ("fasit")
|
||||
> for cross-implementation equivalence. Implementations MUST consume them unchanged.
|
||||
|
||||
**Målt:** `examples/bygg-energi-mikro/` inneholder nøyaktig to JSON-filer
|
||||
(`validator-input.json`, `golden.json`) og seks markdown-filer. Ingen baseline. Et ubetinget
|
||||
krav gjør en tredje fil til ground truth og gjør setningen over usann samtidig — pluss at
|
||||
fasit-bundelen må utvides, hvilket er en fixture-endring, ikke bare en tekstendring.
|
||||
`portfolio-optimiser-claude`s egen formulering («åpent om den skal være obligatorisk … et
|
||||
ubetinget krav endrer golden-bytene») treffer riktig, og §7:332-333 er ankeret som gjør det
|
||||
konkret.
|
||||
|
||||
**Presedens for hvordan en required input formuleres**, hvis den blir obligatorisk:
|
||||
|
||||
> **`method-spec.md:345-346`** (§7.1)
|
||||
> - Loading the IR projection from a bundle is FAIL-FAST: a missing file raises (required
|
||||
> input — contrast the tolerant inbox, §5).
|
||||
|
||||
**Klassifisering:** ekte hull, men det eneste punktet i køen som (hvis ubetinget) **endrer
|
||||
fasit-bundelen** og ikke bare prosa. **Operatør-spørsmålet:** obligatorisk for alle kjøringer
|
||||
(→ fixture-endring + §7-setningen må omskrives), eller opsjonell med fail-fast-semantikk kun
|
||||
når den finnes (→ ren prosa-utvidelse, fasiten uendret)?
|
||||
|
||||
---
|
||||
|
||||
## 5. S3.5 / D-F — tittel-whitespace
|
||||
|
||||
**Seksjoner:** `ingest-spec.md` §4 (`:125`), §5 (`:149-161`), §6 (`:194-197`), §11 (`:278`),
|
||||
§12 (`:301`).
|
||||
|
||||
> **`ingest-spec.md:125`** (§4, extraction-tabellen)
|
||||
> | `title` | Human-readable title; … Single-line, and MUST NOT contain `[` or `]` … Validated
|
||||
> fail-fast at manifest load — rendered verbatim thereafter (§5, §6). |
|
||||
|
||||
> **`ingest-spec.md:158-161`** (§5, frontmatter-kulepunktet)
|
||||
> … All values MUST be single-line; `title` is emitted **verbatim** (it is `[`/`]`-free by §4,
|
||||
> so verbatim rendering is safe — the invariant is met by validation, not repair); the
|
||||
> materializer MUST collapse whitespace runs (including newlines) in `source_query` to single
|
||||
> spaces.
|
||||
|
||||
Formen på hullet, presist: **collapse er scoped til `source_query` alene**, og `title` er
|
||||
uttrykkelig `verbatim` med invarianten «met by validation, not repair». En tittel med interne
|
||||
whitespace-runs er derfor ikke normalisert noe sted — den er bare «single-line», og hva
|
||||
`single-line`-valideringen dekker (bare `\n`? `\r`? ledende/etterfølgende blanke?) står ikke.
|
||||
Amendment-punktet ligger i den sømmen, og valget er **validere hardere** vs. **reparere** —
|
||||
teksten har i dag valgt validering for `title` og reparasjon for `source_query`.
|
||||
|
||||
**⚠ Anker-avvik oppstrøms (viktig for denne teksten spesielt).** `portfolio-optimiser` ankret
|
||||
sitatet til `shared/ingest-spec.md:140`. Det linjenummeret er commons ved **`7aa53fc`**, ikke
|
||||
HEAD:
|
||||
|
||||
- ved `7aa53fc`, linje 140: `` `generated`. All values MUST be single-line; the materializer MUST collapse whitespace runs ``
|
||||
- ved HEAD (`bfa5a9b`): samme setning er splittet over `:158` og `:160-161`, og `title is
|
||||
emitted verbatim` + «validation, not repair» er **ny tekst** fra `bfa5a9b`.
|
||||
|
||||
`bfa5a9b` (+39/−9) skrev om nettopp dette kulepunktet OG `title`-raden i §4 (som gikk fra
|
||||
bare «Single-line.» til dagens `[`/`]`-forbud + fail-fast + verbatim). Deres `shared/`-kopi
|
||||
ser altså ut til å ligge ett commons-commit bak, fra FØR D1-stempelmodellen landet. Det
|
||||
betyr at S3.5 kan være formulert mot tekst som ikke lenger finnes i den formen — og det er
|
||||
samme risiko de selv navnga («pullen må være koordinert, samme commons-commit»). **Meldt
|
||||
oppstrøms.**
|
||||
|
||||
**Klassifisering:** ekte, men smal søm. Ingen fasit-effekt her (våre fixtures har ingen
|
||||
ingest-manifester; goldenbundlene for ingest bor i implementasjonene, jf. §11 `:259-261`).
|
||||
|
||||
---
|
||||
|
||||
## 6. D-A#5 — projeksjoner over besparelses-hovedboken (INGEN eksisterende tekst)
|
||||
|
||||
**Målt:** `grep -ci 'ledger'` og `grep -ci 'hovedbok'` over `method-spec.md` +
|
||||
`ingest-spec.md` → **0 og 0**. Ingen monetær avrundingsregel finnes heller: alle 13
|
||||
forekomster av `round` (12 linjer: 9 i method-spec, 3 i ingest-spec) er andre ting
|
||||
(`round-capped` debatt `:141`/`:386`, `max_rounds` `:378`/`:385`, `shortest round-trip
|
||||
decimal` `ingest-spec.md:165`, `round-trip` `ingest-spec.md:85`, `around` `:323`).
|
||||
|
||||
Nærmeste eksisterende flater, som en ny kontrakt må forholde seg til uten å kollidere:
|
||||
|
||||
- **`method-spec.md:367-372`** (§7.2 `learning_surface`) — de monetære feltene som ER frosset
|
||||
(`modelled_saving_nok`, `expected_actual_saving_nok`, intern konsistens påkrevd).
|
||||
- **`method-spec.md:199-202`** (§3 Steg 6) — råresultater er output-laget, «plain JSON», og
|
||||
bruker bevisst IKKE wiki-formatet.
|
||||
- **`method-spec.md:250-252`** (§4.2) — `claimed_saving_nok` som tall i verdict-kontrakten,
|
||||
og `description` bevisst utenfor likhet/minting.
|
||||
- **`method-spec.md:436-464`** (§12) — kryssjekk-tabellen er *fullstendighets-håndhevet* av
|
||||
spec-integritetstesten (§11 `:434`), så en ny kontrakt med nye felter MÅ inn her, ellers
|
||||
feiler den testen. Dette er den mekaniske konsekvensen ingen av oppstrøms-meldingene nevner.
|
||||
|
||||
**Klassifisering:** ny seksjon, ikke en amendment. Dette er køens største punkt (den eneste
|
||||
som utvider spec-ens virkeområde) og samtidig den uten story-etikett oppstrøms — mest utsatt
|
||||
for å falle mellom de to enumerasjonene. **Meldt oppstrøms.**
|
||||
|
||||
**Note, videreformidlet ikke verifisert her:** `portfolio-optimiser` melder (16:11Z, pkt. 4)
|
||||
at forslagets A5-regel 1 («Monetary figures MUST NOT be rounded in the projection») treffer
|
||||
en persisterings-kant hos dem (NOK→øre-kvantisering) og bør skille projeksjons-aritmetikk fra
|
||||
enhets-kvantisering. Commons har ikke lest deres kode og fører det som deres måling, ikke som
|
||||
vårt faktum.
|
||||
|
||||
---
|
||||
|
||||
## 7. S2.2–2.4 / D-B — substansen er ikke ankret hos commons
|
||||
|
||||
Punktet er kjent bare som etikett. Vi leste det ut av `portfolio-optimiser`s STATE
|
||||
(GATES-blokk) 2026-07-25 04:05Z og har aldri fått innholdet. `grep -rn 'D-B'` over dette
|
||||
repoet gir **0 treff** — heller ikke i vår egen STATE, som bare bærer story-etiketten
|
||||
`S2.2-2.4`. Koblingen `S2.2–2.4 (D-B)` finnes utelukkende i coord-arkivet
|
||||
(`20260725T040535Z`, vår egen melding), ikke i noen commons-tekst.
|
||||
|
||||
Commons kan derfor ikke si hvilken seksjon det rører. **Ingen antakelse føres.** Dette er den
|
||||
ene raden i underlaget som er blank av en grunn — og den blir stående blank til substansen
|
||||
kommer. **Etterspurt oppstrøms.**
|
||||
|
||||
### 7.1 Substansen ER LEVERT (2026-07-26) — raden er ikke lenger blank
|
||||
|
||||
`portfolio-optimiser` leverte D-B-substansen på direkte forespørsel. Kilde:
|
||||
`docs/plan/2026-07-10-sesjonsplan-fase2-6.md` **i deres repo, ikke commons** — gjengitt her som
|
||||
mottatt, ikke verifisert av oss (vi har ikke lesetilgang til premisset, og fører det som
|
||||
*rapportert*, ikke *målt*).
|
||||
|
||||
**D-B er en SCOPE-beslutning, ikke en spec-tekst.** Det er den avklaringen som gjør raden
|
||||
meningsfull: vi lette etter hvilken seksjon den rører, og svaret er at *den selv* ikke rører
|
||||
noen — den avgjør om tre andre stories gjør det. Fire delbeslutninger (`:56-62`): (1) amender
|
||||
ingest-målbildet bevisst vs. nedskopér roadmap-B til mock-herding *(deres anbefaling: nedskopér
|
||||
nå)*; (2) første live-kilde *(anbefaling: SQL — `read_sql` er mest herdet, read-only by
|
||||
construction)*; (3) dokument-konnektor-avgrensning *(PDF via `pypdf` nå, DOCX utsatt)*;
|
||||
(4) MCP-kildefamiliens nettverks-/subprosess-grant-form.
|
||||
|
||||
**Konsekvensen for commons — hvilke av de tre gatede storiene som faktisk krever amendment:**
|
||||
|
||||
| Story | Krever ingest-spec-amendment? | Hva |
|
||||
|---|---|---|
|
||||
| **S2.2** MCP-konnektor | **NEI — UGATED** (se §7.2) | §4s `http`-punkt (HEAD `:113-114` = pin `:101-102`) sier normativt at en MCP-konnektor **er en utvidelse av `http`-familien** med MUST på samme kontrakter. Ingen ny familie, ingen amendment. Bygges mot `http` + §8-grant |
|
||||
| **S2.3** Dokument-konnektor | **JA** — ny kildefamilie | `{type: "doc"}`. PDF→OKF-konseptfil med provenance, deterministisk mot committede fixtures |
|
||||
| **S2.4** Konnektor-herding | **NEI — UGATED** (bekreftet av po) | Timeout-parameter, feilkategorisering, beriket §8-logg (kilde/tidspunkt/radantall — aldri innhold). Retry KUN hvis D-B sier ja (default nei, av hensyn til determinisme). Inkrementell re-ingest: **ikke**, med mindre D-B amender §8 |
|
||||
|
||||
**Commons' observasjon, ikke et forslag:** §4 har i dag tre kildetyper (`file`, `sql`, `http`),
|
||||
og §1 (`:27-31`) binder konformans til `file` + `sql` som påkrevde med `http` som eksplisitt
|
||||
OPTIONAL utvidelsespunkt. To nye familier reiser derfor et spørsmål ingen av meldingene har
|
||||
stilt: **blir `mcp` og `doc` påkrevde for konformans, eller valgfrie som `http`?** Svaret
|
||||
avgjør om `:27-31` må skrives om i samme amendment. Vi fører ingen antakelse om hvilket.
|
||||
|
||||
**⚠️ Og frossen tekst sier allerede noe om MCP som ingen av meldingene har nevnt.** Ordrett,
|
||||
`ingest-spec.md:29-31`:
|
||||
|
||||
> The `http` source type is an OPTIONAL extension point: implementing it (e.g. against a local
|
||||
> mock, **or via an MCP-based connector**) does not require any change to this spec, and NOT
|
||||
> implementing it does not break conformance.
|
||||
|
||||
Specen forutser altså eksplisitt at MCP kan være **transporten under `http`**, og sier at den
|
||||
veien **ikke krever noen spec-endring**. Det gjør S2.2s premiss — «krever ingest-spec-amendment
|
||||
i commons FØRST» — til noe som må avgjøres, ikke antas:
|
||||
|
||||
- Er `mcp` en **fjerde kildefamilie** (nytt `{type: "mcp"}`-skjema, amendment nødvendig), eller
|
||||
en **transport for den eksisterende `http`-familien** (allerede dekket, ingen amendment)?
|
||||
- Distinksjonen er ikke akademisk: den avgjør om S2.2 er blokkert på commons i det hele tatt.
|
||||
Under den andre lesningen er S2.2 **ugated** og kan bygges nå, mot `http` + opt-in-flagget (§8).
|
||||
|
||||
### 7.2 Spørsmålet er AVGJORT av frossen tekst — det er ikke to forsvarlige svar (korr. 2026-07-26)
|
||||
|
||||
Avsnittet over stilte (a)/(b) som et åpent valg. **Det var feil, og `portfolio-optimiser` fant
|
||||
hvorfor: det finnes et ANDRE anker vi ikke siterte, og det er normativt.**
|
||||
|
||||
> **`ingest-spec.md` §4, kildetypelisten, `type: "http"`-punktet** — sitert ORDRETT fordi
|
||||
> linjenumrene ikke overlever pull-grensen (se rammen under):
|
||||
> `type: "http"` — a remote endpoint (OPTIONAL extension point, §1). Field `base_url`: the
|
||||
> endpoint base; it MUST NOT embed credentials. Optional field `credential_ref`: the NAME of
|
||||
> a runtime-resolved secret reference. **An MCP-based connector is an extension of this family
|
||||
> and MUST honour the same extraction, materialization, and gate contracts.**
|
||||
|
||||
> **⚠️ Ref-binding for sitatet over (korr. 2026-07-27, meldt av `repos`).** Avsnittet siterte
|
||||
> tidligere «`:112-114`» uten ref. To feil i én: (1) numrene er HEAD-relative, og konsumentene
|
||||
> står på `7aa53fc`; (2) selv mot HEAD var de av med én linje. Målt:
|
||||
>
|
||||
> | Ref | MCP-setningen | Hele `http`-punktet |
|
||||
> |---|---|---|
|
||||
> | commons HEAD | `:113-114` | `:111-114` |
|
||||
> | `7aa53fc` (konsumentenes pin, verifisert identisk i BEGGE arbeidstrær) | `:101-102` | `:99-102` |
|
||||
>
|
||||
> **Failure-moden er grunnen til at dette er verdt en korreksjon:** en konsument som slår opp
|
||||
> `:112-114` i SIN fil finner ikke ingenting — den finner felt-tabellen for `extractions`
|
||||
> (`id`/`title`/`query`), troverdig og relatert spec-tekst uten et ord om MCP. Den mest
|
||||
> sannsynlige konklusjonen er da at ankeret ikke finnes og at kjennelsen hviler på løs grunn.
|
||||
> Et åpenbart tomt treff hadde vært tryggere. **Regel, samme klasse som `git log -S`-regelen
|
||||
> under: sitér SEKSJON + ORDRETT TEKST når mottakeren står på en annen ref — eller skriv
|
||||
> hvilken ref numrene gjelder.**
|
||||
|
||||
`:29-31` sier at MCP-veien ikke krever spec-endring. `http`-punktet sier **normativt hvor den
|
||||
hører hjemme** — som en utvidelse av `http`-familien, med **MUST** på de samme kontraktene.
|
||||
Frossen tekst har altså allerede valgt (b). En fjerde `{type: "mcp"}`-familie ville **motsagt**
|
||||
MCP-setningen, ikke utfylt den.
|
||||
|
||||
**Konsekvens: S2.2 er UGATED.** Premisset «krever ingest-spec-amendment i commons FØRST» er
|
||||
avkreftet av vår egen frosne tekst. S2.2 kan bygges mot `http`-familien + §8s opt-in-grant uten
|
||||
å vente på noe herfra.
|
||||
|
||||
**Og §1-spørsmålet i 7.1 faller bort for MCP:** MCP arver `http`s status, og `http` er eksplisitt
|
||||
OPTIONAL (`:27-31`). `:27-31` trenger derfor **ingen** omskriving for MCP. Spørsmålet gjenstår
|
||||
kun for **S2.3 (`doc`)**, som fortsatt er en genuint ny familie. `portfolio-optimiser`s
|
||||
anbefaling der: valgfri som `http` — en konform implementasjon bør ikke tvinges til en
|
||||
PDF-parser. Det er deres anbefaling til ratifisering, ikke en beslutning.
|
||||
|
||||
**(a)-argumentet lever videre, men som implementasjonssak, ikke spec-tekst.** At
|
||||
`base_url`/`credential_ref` passer dårlig på en kommando, og at env-var-med-kommando er en
|
||||
subprosess-grant, er ekte friksjon — men MCP-setningen sier MUST på *kontraktene*, ikke på
|
||||
*feltnavnene*, og §8 dekker «any other non-local transport». Friksjonen lever i hvordan en
|
||||
kommando uttrykkes innenfor `http`-familien. Støter implementasjonen faktisk på noe MCP-setningen
|
||||
forbyr, er det et målt funn og kommer tilbake hit.
|
||||
|
||||
**Vår egen feil, ført åpent fordi den er tredje instans samme dag:** vi skrev at `:29-31` var
|
||||
«frossen siden `bfa5a9b`». Målt med `git log -S`: **begge** MCP-ankrene landet i **`7aa53fc`** —
|
||||
nøyaktig commit-en konsumentene står på. `bfa5a9b` rørte bare D1-stempelmodellen. Vi tok repoets
|
||||
*siste* ingest-spec-commit og antok at filen daterte derfra. Og vi fant ikke `:113` fordi vårt
|
||||
`grep -n 'connection_ref' | head -3` stoppet på `:109` uten å lese omgivelsene — setningen i §1
|
||||
er dessuten linjebrutt (`:30-31`), så et grep på hele frasen ville også bommet.
|
||||
**Sikt grepet mot seksjonen, ikke mot ett token — og bekreft opphav med `git log -S`, aldri med
|
||||
«siste commit som rørte filen».**
|
||||
|
||||
Merk også at S2.4s «beriket ingest-logg» ligger mot §8, som allerede sier *«Source calls are
|
||||
logged (which source, when, row count)»* — en berikelse innenfor de tre feltene er neppe en
|
||||
tekstendring; en berikelse *utover* dem er det. Grensen går ved «aldri innhold», som deres egen
|
||||
formulering allerede respekterer.
|
||||
|
||||
---
|
||||
|
||||
## 8. B1 — nav-golden-klassen (utenfor pakken, commons-eid)
|
||||
|
||||
Ankret, etter å ha vært et etikett-punkt i tre uker. `llm-ingestion-okf` svarte
|
||||
`portfolio-optimiser` 2026-07-25 04:19Z at «B1 bundle class» ikke finnes hos dem, og at
|
||||
beskrivelsen matcher **D4 fra OKF-runden, ratifisert i trinn F 2026-07-21**: commons eier
|
||||
nav-golden-bundleklassen + forventet utfall og leverer den inn; catalog eier
|
||||
korpus-containeren, de adversarielle aksene og runner/gate.
|
||||
|
||||
**Commons-halvdelen er levert** (`b641741`, 2026-07-21):
|
||||
|
||||
| Case | Innhold |
|
||||
|---|---|
|
||||
| `examples/nav-golden-escape/` | `bundle/`, `expected-read-context.md`, `README.md`, `SHOULD-NOT-BE-READ.md` |
|
||||
| `examples/nav-golden-hierarchy/` | `bundle/` (nestet `a/b/`, `c/orphan.md`), `expected-read-context.md`, `README.md` |
|
||||
|
||||
**Det åpne spørsmålet er ett, og det er vårt:** `grep -c 'nav-golden'` i `method-spec.md`,
|
||||
`ingest-spec.md`, `CONCEPT.md`, `README.md` og `skills/expert-reviewer/SKILL.md` → **0 i alle
|
||||
fem**. Klassen er levert som fixture, men ingen normativ seksjon peker på den — i motsetning til
|
||||
`validator-input.json` / `golden.json` (navngitt i §7 `:330-346`) og
|
||||
`examples/ingest-golden-{source type}/` (navngitt som konvensjon i `ingest-spec.md:259-261`).
|
||||
§11-raden «Navigation boundary» (`:425`) beskriver rød-betingelsen, men nevner ikke fixturene
|
||||
som beviser den.
|
||||
|
||||
**Operatør-spørsmålet:** skal nav-golden-klassen få en normativ referanse (§7 ground truth
|
||||
og/eller §11-raden), eller forbli en informativ fixture? Dette er commons' eget punkt, ingen
|
||||
venter på oss, og det hører IKKE inn i oppstrøms-pakken.
|
||||
|
||||
**Utskrevet i sin helhet:** `docs/plan/2026-07-25-b1-nav-golden-normative-status.md` — fire
|
||||
opsjoner med målt kostnad. Spørsmålet viste seg ikke å være binært: specen har **tre**
|
||||
referanseformer i bruk (artefaktnavn som fasit, katalogkonvensjon, informativ lenke), og
|
||||
method-spec navngir aldri en repo-sti (`grep -c 'examples/' method-spec.md` → 0).
|
||||
|
||||
---
|
||||
|
||||
## 9. Sammendrag
|
||||
|
||||
| # | Punkt | Fil + seksjoner | Klassifisering | Fasit-effekt |
|
||||
|---|---|---|---|---|
|
||||
| 1 | S2.7 / D-A#1 nominal gate + IR-band | method §3.4 `:157-166`, §7.1 `:339-346`, §7.2 `:352-358` | ekte hull | ingen (målt) |
|
||||
| 2 | S3.2 / D-A#4 seed-nøkkel | method §3.1 `:108-114` | ekte hull | ingen (fasiten skiller ikke) |
|
||||
| 3 | D-A#3 ledende `/` | method §3.1 `:67-71`, §11 `:425` | **allerede normativt i HEAD — drift, ikke avvik** | — |
|
||||
| 4 | S4.0 / D-A#2 kostbaseline | method §7 `:332-333`, §7.1 `:345-346` | ekte hull | **endrer fasit hvis ubetinget** |
|
||||
| 5 | S3.5 / D-F tittel-whitespace | ingest §4 `:125`, §5 `:158-161`, §6 `:194-197`, §11 `:278` | ekte, smal søm | ingen |
|
||||
| 6 | D-A#5 hovedbok-projeksjoner | ingen tekst; naboer method §7.2 `:367-372`, §12 `:436-464` | **ny seksjon** | ingen direkte; §12 må utvides |
|
||||
| 7 | S2.2–2.4 / D-B | **ANKRET** (§7.1–7.2): kun **S2.3** (`doc`) krever amendment. **S2.2 + S2.4 er UGATED** — S2.2 av §4s `http`-punkt, S2.4 innenfor §8s tre felt | **scope-beslutning, ikke spec-tekst** | ingen |
|
||||
| 8 | B1 / D4 nav-golden | levert `b641741`; 0 spec-referanser | commons-eget, utenfor pakken | — |
|
||||
|
||||
Commons' rolle er uendret: vi forbereder underlaget, operatøren ratifiserer, og frossen tekst
|
||||
endres ikke uten den ratifiseringen.
|
||||
|
||||
---
|
||||
|
||||
## Verifiseringslogg
|
||||
|
||||
| Påstand | Sjekk | Resultat |
|
||||
|---|---|---|
|
||||
| Spec-tekst uendret siden 2026-07-21 | `git log -- method-spec.md ingest-spec.md` | `9801d35` / `bfa5a9b`, begge 07-21 |
|
||||
| Bare p90 blokkerer i dag | lest `method-spec.md:150-166` i sin helhet | punkt 2 «capped», punkt 4 eneste blokk |
|
||||
| Ingen `low ≤ unit_cost ≤ high`-invariant | lest §7.1 `:339-346` | én invariant, om claimed vs. total |
|
||||
| Nominal gate er byte-nøytral | `golden.json`: claimed `30000` ≤ nominal `90000.0` | `validates` uendret `true` |
|
||||
| IR-band-invarianten er alt oppfylt i fasit | `validator-input.json`: `1.0` ∈ `[0.70, 1.40]` | ingen fixture-endring |
|
||||
| Seeding nøkles på bundelens projeksjon | lest `:108-114` | «keyed on those candidate features» |
|
||||
| Ledende `/` alt normativt | `git log -S'denotes the **bundle root**'` | `9801d35`, 2026-07-21 |
|
||||
| §11 har alt rød-betingelsen | `method-spec.md:425` | «bundle-root `/` read as filesystem-absolute» |
|
||||
| Ingen hovedbok-/baseline-tekst | `grep -ci ledger\|hovedbok\|cost-baseline` | 0 / 0 / 0 |
|
||||
| Ingen monetær avrundingsregel | `grep -n round` begge spec-er, alle 18 treff lest | alle urelaterte |
|
||||
| Fasit-bundelen har to JSON-filer | `ls examples/bygg-energi-mikro/` | `validator-input.json`, `golden.json` |
|
||||
| Oppstrøms-anker `:140` er stale | `git show 7aa53fc:ingest-spec.md \| grep -n` | treff på `:140` ved `7aa53fc`, `:158/:160` ved HEAD |
|
||||
| `bfa5a9b` rørte nettopp den teksten | `git show bfa5a9b -- ingest-spec.md` | +39/−9; §5-kulepunkt + §4 `title`-rad omskrevet |
|
||||
| Multi-manifest er extension point | `ingest-spec.md:173-174` | «Version 1 assumes ONE manifest per bundle» |
|
||||
| B1 = D4, ikke okf-eid | `~/.claude/coord/portfolio-optimiser/archive/20260725T041948Z-*` | okf: «never owned here; it is D4» |
|
||||
| nav-golden levert | `git log -- examples/nav-golden-*` | `b641741`, 2026-07-21 |
|
||||
| nav-golden ikke normativt referert | `grep -c 'nav-golden'` i de fem normative filene | 0 i alle fem |
|
||||
| D-B ikke ankret hos commons (per 07-25) | `grep -rn 'D-B'` | 0 treff utenfor STATE |
|
||||
| D-B-substansen levert 07-26 | coord fra `portfolio-optimiser` | rapportert fra deres `sesjonsplan-fase2-6.md:56-62`, ikke målt av oss |
|
||||
| §1 gjør `http` OPTIONAL, `file`+`sql` påkrevd | `ingest-spec.md:27-31` | ordrett sitert i §7.1 |
|
||||
| …og nevner MCP som `http`-transport | `ingest-spec.md:29-31` | «or via an MCP-based connector … does not require any change to this spec» |
|
||||
| **ANDRE anker: MCP hører NORMATIVT til `http`** | `ingest-spec.md` §4, `http`-punktet (HEAD `:113-114` = pin `:101-102`) | «An MCP-based connector **is an extension of this family** and **MUST** honour the same … contracts» |
|
||||
| Begge MCP-ankere landet i `7aa53fc`, ikke `bfa5a9b` | `git log -S` på hver frase | `7aa53fc` for begge — altså i konsumentenes egen kopi |
|
||||
| `bfa5a9b` rørte kun stempelmodellen | `git show --stat bfa5a9b` | 39 innsettinger, D1-stempel |
|
||||
| §8 logger tre felter | `ingest-spec.md:230` | «which source, when, row count» |
|
||||
| `connection_ref` er env-var-NAVN | `ingest-spec.md:109`, `:117` | «the NAME of a runtime-resolved …», secret resolved at run time |
|
||||
| Begge konsumenter står på `7aa53fc` | coord fra begge, uavhengig | po: `git log --grep=git-subtree-split` → `ef1a2c5`; po-claude: egen måling |
|
||||
238
shared/docs/plan/2026-07-25-b1-nav-golden-normative-status.md
Normal file
238
shared/docs/plan/2026-07-25-b1-nav-golden-normative-status.md
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
# B1 / D4 — skal nav-golden-klassen ha en normativ referanse?
|
||||
|
||||
**Status: AVGJORT OG UTFØRT 2026-07-31 — operatøren valgte O1** (form C: informativ kataloglenke
|
||||
fra `README.md`). Utført i samme økt; se **§5.1**. Underlaget under står uendret som grunnlaget
|
||||
vedtaket ble tatt på.
|
||||
|
||||
Opprinnelig status: operatør-spørsmål. Dette dokumentet endrer **ingen** normativ tekst og foreslår
|
||||
ingen. Det svarer på det commons kan svare på uten å foregripe en ratifisering: hva som
|
||||
allerede er avgjort, hva tilstanden er i dag (fil + linje), hvilke referanseformer specen
|
||||
faktisk har, og hva hver av dem koster å ta i bruk her.
|
||||
|
||||
**Grunnlag:** commons HEAD `29ad8ca`. Normativ spec-tekst uendret siden `9801d35`
|
||||
(method-spec) / `bfa5a9b` (ingest-spec), fixturene siden `b641741` — alle 2026-07-21. Alle
|
||||
linjeankere under er lest fra HEAD, ikke gjengitt fra hukommelse.
|
||||
|
||||
**Avgrensning:** dette er commons' eget punkt. Det står **utenfor** amendment-pakken
|
||||
(`portfolio-optimiser` bekreftet 2026-07-25 at B1 ikke har adferds-interesse oppstrøms), og
|
||||
ingen venter på oss. Det kan avgjøres når som helst, også etter pakken.
|
||||
|
||||
---
|
||||
|
||||
## 1. Det som allerede er avgjort — ikke oppe til vurdering
|
||||
|
||||
D4 fra OKF-runden, ratifisert i trinn F 2026-07-21, deler arbeidet i to:
|
||||
|
||||
- **commons eier** nav-golden-bundleklassen og det forventede utfallet, og leverer den inn.
|
||||
- **catalog eier** korpus-containeren, de adversarielle aksene og runner/gate.
|
||||
|
||||
`llm-ingestion-okf` bekreftet 2026-07-25 04:19Z at klassen aldri var deres, og at beskrivelsen
|
||||
matcher D4. Commons-halvdelen er **levert** i `b641741`:
|
||||
|
||||
| Case | Innhold | Rolle |
|
||||
|---|---|---|
|
||||
| `examples/nav-golden-hierarchy/` | `bundle/` (nestet `a/b/`, `c/orphan.md`), `expected-read-context.md`, `README.md` | positiv: ni Q3-beslutninger utøves |
|
||||
| `examples/nav-golden-escape/` | `bundle/`, `expected-read-context.md`, `README.md`, `SHOULD-NOT-BE-READ.md` | negativ: porten må kunne gå rød |
|
||||
|
||||
Splitten, eierskapet og fixturenes innhold er altså ikke spørsmålet. Spørsmålet er hva
|
||||
spec-teksten sier om dem — i dag: ingenting.
|
||||
|
||||
---
|
||||
|
||||
## 2. Tilstanden i dag, verifisert
|
||||
|
||||
Ingen normativ fil nevner klassen. Ikke «få treff» — null, i hver enkelt:
|
||||
|
||||
| Fil | `grep -c 'nav-golden'` |
|
||||
|---|---|
|
||||
| `method-spec.md` | 0 |
|
||||
| `ingest-spec.md` | 0 |
|
||||
| `CONCEPT.md` | 0 |
|
||||
| `README.md` | 0 |
|
||||
| `skills/expert-reviewer/SKILL.md` | 0 |
|
||||
|
||||
Fixturene er dermed i dag rene artefakter: de finnes, de er dokumentert i sine egne
|
||||
`README.md`-filer, og ingenting utenfor katalogen deres viser til dem.
|
||||
|
||||
**Merk om sjekken:** `grep -rn 'nav-golden' *.md` gir i dag **3** treff, alle i `STATE.md`
|
||||
(LOCAL-ONLY, gitignored, ikke-normativ). Underlagets verifiseringslogg
|
||||
(`2026-07-25-amendment-underlag.md:350`) noterte «0 treff» på nettopp den kommandoen, og den
|
||||
reproduserer ikke lenger. Påstanden er uendret sann — sjekken var for vid. Korrigert i samme
|
||||
commit som dette dokumentet.
|
||||
|
||||
---
|
||||
|
||||
## 3. Specen har tre referanseformer, ikke én
|
||||
|
||||
Dette er kjernen, og grunnen til at spørsmålet ikke er binært. «Normativ referanse» er ikke
|
||||
én ting i dette repoet — tre distinkte former er allerede i bruk, med forskjellig
|
||||
bindingskraft:
|
||||
|
||||
| | Form | Hvor den er brukt | Hva den binder |
|
||||
|---|---|---|---|
|
||||
| **A** | **Artefaktnavn som fasit** | method-spec §7 `:332-333`: *«The shared example bundle ships two JSON files that are the only ground truth ("fasit") … Implementations MUST consume them unchanged»* | navngir `validator-input.json` / `golden.json`; konformans-krav på verdiene |
|
||||
| **B** | **Katalogkonvensjon + oppføringstabell** | ingest-spec §11 `:259-267`: *«Golden extraction format: one directory per case, convention `examples/ingest-golden-{source type}/`»* + tabell over påkrevde oppføringer | navngir et **format** for en case-klasse, ikke enkeltcaser |
|
||||
| **C** | **Informativ kataloglenke** | `README.md:19` — `examples/bygg-energi-mikro/`, beskrevet som *«a small dev fixture»* | oppdagbarhet; null konformansvekt |
|
||||
|
||||
nav-golden har i dag **ingen** av de tre. Det er den presise mangelen — skarpere enn «0 treff».
|
||||
|
||||
**En egenskap ved method-spec som begrenser opsjonsrommet:** den navngir **aldri en katalogsti
|
||||
i repoet**. `grep -c 'examples/'` → 0, `grep -c 'bygg-energi-mikro'` → 0. Den navngir filnavn
|
||||
(`validator-input.json`, `golden.json`, `test_okf.py`) og aldri hvor de bor. De ti
|
||||
`/`-holdige tokenene i filen er lenke-syntaks *inne i* en bundle (`sub/index.md`, `/a/b.md`),
|
||||
ikke repo-stier. En form B-referanse i method-spec ville være den **første** repo-stien i det
|
||||
dokumentet.
|
||||
|
||||
---
|
||||
|
||||
## 4. Spørsmålet
|
||||
|
||||
**Skal nav-golden-klassen gis en normativ referanse, og i så fall i hvilken form?**
|
||||
|
||||
Underspørsmål som følger med, uansett svar: hvis klassen blir normativ, må serialiserings-
|
||||
sammenligningen pinnes? (Se O2 og O3 under — det er den ene konsekvensen som ikke er
|
||||
kosmetisk.)
|
||||
|
||||
---
|
||||
|
||||
## 5. Opsjonene, med målt kostnad
|
||||
|
||||
| | Opsjon | Berørt tekst | Frossen tekst endres? | Reverserbar |
|
||||
|---|---|---|---|---|
|
||||
| **O0** | Forbli informativ fixture (status quo) | ingen | nei | — |
|
||||
| **O1** | Form C: lenke fra `README.md` | `README.md:19`-blokken | nei (README er ikke normativ) | trivielt |
|
||||
| **O2** | Form B: katalogkonvensjon i method-spec §11 | ny avsnitt/rad ved `:425` | **ja**, tillegg | middels |
|
||||
| **O3** | Form A: løft til fasit i §7 | `§7:332-333` **og** `§1:23` | **ja**, omskriving av tellende påstand | dyrt |
|
||||
|
||||
### O0 — forbli informativ fixture
|
||||
|
||||
Ingen endring. Fixturene er fortsatt levert, catalog kan fortsatt bygge runner/gate på dem,
|
||||
og D4s commons-halvdel er fortsatt oppfylt (D4 krevde levering, ikke normativ referanse).
|
||||
Kostnaden er at bindingen mellom §11-raden `:425` og beviset for den bare finnes i catalogs
|
||||
runner og i fixturenes egne README-er — ingen spec-tekst sier at de hører sammen.
|
||||
|
||||
### O1 — form C, lenke fra README
|
||||
|
||||
Billigst mulige forbedring av oppdagbarhet. `README.md` er katalog, ikke kontrakt, så ingen
|
||||
implementasjon får en ny plikt. Løser ikke bindingsproblemet i O0; gjør det bare synlig.
|
||||
|
||||
### O2 — form B, katalogkonvensjon i method-spec §11
|
||||
|
||||
Den formen som passer klassen best: en nav-golden-case *er* en formatklasse (`bundle/` inn,
|
||||
`expected-read-context.md` ut), akkurat som ingest-golden er det. Naturlig plassering er ved
|
||||
**Navigation boundary**-raden `:425`, som i dag beskriver rød-betingelsen og siterer
|
||||
referansetesten `test_okf.py (navigation boundary)`, men ikke navngir noe korpus som beviser
|
||||
den.
|
||||
|
||||
Tre konkrete kostnader, alle verifisert:
|
||||
|
||||
1. **Første repo-sti i method-spec** (se §3). Enten aksepteres det som et bevisst brudd, eller
|
||||
så må konvensjonen formuleres uten sti — og da mister den det meste av verdien.
|
||||
2. **Oppføringstabellen blir ikke uniform.** ingest-goldens tabell (`:262-267`) har fire
|
||||
påkrevde oppføringer og ingen valgfrie. nav-golden-casene har **ulike** oppføringssett:
|
||||
escape-casen trenger `SHOULD-NOT-BE-READ.md` som bevisst ligger *utenfor* `bundle/` (den
|
||||
er lokkematen `../SHOULD-NOT-BE-READ.md` peker på), hierarchy-casen har ingen slik fil. En
|
||||
felles konvensjon må derfor ha en valgfri rad — noe ingest-golden ikke har presedens for.
|
||||
3. **Serialiseringen er ikke pinnet.** Dette er den reelle. `nav-golden-hierarchy/README.md`
|
||||
sier eksplisitt: *«The spec does not pin whitespace beyond this shape; a gate MAY compare
|
||||
byte-exact or after trailing-whitespace normalization.»* ingest-goldens motstykke er
|
||||
pinnet: `:267` *«compared file by file, byte for byte»* og §11-raden `:280` *«any byte …
|
||||
diverges»*. Gjør man nav-golden normativ i form B uten å ta stilling, arver klassen en
|
||||
`MAY` der søsterklassen har en `MUST` — to konforme porter kan da være uenige om samme
|
||||
fixture. Enten pinnes sammenligningen, eller så uttales `MAY`-en som bevisst.
|
||||
|
||||
### O3 — form A, løft til fasit i §7
|
||||
|
||||
Dyrest, og kostnaden er ikke størrelsen på diffen, men at to **tellende** påstander må
|
||||
skrives om:
|
||||
|
||||
- `§7:332-333`: *«The shared example bundle ships **two** JSON files that are **the only**
|
||||
ground truth»* — «two» og «the only» er begge usanne i det øyeblikket en tredje ground
|
||||
truth finnes. I tillegg er setningen scoped til **én** bundle («the shared example
|
||||
bundle»), mens nav-golden-casene er *andre* bundler.
|
||||
- `§1:23`, konformansledd 2: *«reproduces the shared golden suite's decided outcomes (§7) on
|
||||
the shared example bundle»* — samme entallsbinding, i selve konformansdefinisjonen.
|
||||
|
||||
O3 er altså ikke et tillegg, men en utvidelse av hva «fasit» betyr i denne specen, fra én
|
||||
bundle til en mengde. Det er en reell arkitekturbeslutning, ikke en redaksjonell.
|
||||
Serialiseringspoenget fra O2 punkt 3 gjelder her med full tyngde: en fasit med en `MAY` i
|
||||
sammenligningsregelen er ikke en fasit.
|
||||
|
||||
### 5.1 Svaret, og hva som faktisk ble gjort (2026-07-31)
|
||||
|
||||
**Vedtatt: O1** — form C. Én oppføring i `README.md`s «Contents (growing)»-liste, i samme form
|
||||
som `examples/bygg-energi-mikro/` på `:19`, som lenker begge casene og peker på deres egne
|
||||
`README.md`-filer for detaljene.
|
||||
|
||||
**Frossen tekst er ikke rørt, og klassen har ikke fått bindingskraft.** Verifisert etter
|
||||
endringen: `grep -c 'nav-golden'` gir fortsatt **0** i `method-spec.md`, `ingest-spec.md`,
|
||||
`CONCEPT.md` og `skills/expert-reviewer/SKILL.md`. Bare `README.md` gikk fra 0 til treff, og
|
||||
README er katalog, ikke kontrakt (§3, form C: «oppdagbarhet; null konformansvekt»). Ingen
|
||||
implementasjon har fått en ny plikt. `git status` viste `README.md` som eneste endrede fil.
|
||||
|
||||
**Oppføringen sier eksplisitt at den er informativ**, og at sammenligningsregelen ikke er pinnet
|
||||
— nettopp for å hindre den ene misforståelsen O1 kunne skapt: at en lenke fra rot-README gjør
|
||||
klassen til fasit i den forstand method-spec §7 bruker ordet.
|
||||
|
||||
**Det O1 med vilje IKKE løser** (uendret fra §5s beskrivelse): bindingsproblemet i O0 står. Ingen
|
||||
spec-tekst sier at §11-raden `:425` og disse fixturene hører sammen — O1 gjør mangelen synlig,
|
||||
ikke borte. Å lukke den krever O2/O3 og en ratifiseringsrunde.
|
||||
|
||||
**Serialiseringsspørsmålet er fortsatt åpent, og er uavhengig av dette vedtaket** (§6): to
|
||||
implementasjoner som sammenligner ulikt vil være uenige om nav-golden enten specen nevner klassen
|
||||
eller ikke. Det kan avgjøres separat og tidligere, og O1 verken løser eller foregriper det.
|
||||
|
||||
---
|
||||
|
||||
## 6. Hva commons vil peke på (ikke en anbefaling)
|
||||
|
||||
Én asymmetri er verdt å ha for hånden når spørsmålet avgjøres: **O0 og O1 er reverserbare,
|
||||
O2 og O3 er det ikke på samme måte.** Frossen tekst i dette repoet endres bare ved
|
||||
ratifisering, og en normativ referanse som senere skal fjernes koster en ny runde. Motsatt
|
||||
koster det ingenting å utsette O2/O3 — fixturene er levert, catalog kan bygge på dem uansett,
|
||||
og ingen implementasjon blokkeres av at referansen mangler.
|
||||
|
||||
Serialiseringsspørsmålet (O2 punkt 3) er derimot **uavhengig** av om referansen kommer: to
|
||||
implementasjoner som sammenligner ulikt vil være uenige om nav-golden enten specen nevner
|
||||
klassen eller ikke. Det kan avgjøres separat, og tidligere.
|
||||
|
||||
---
|
||||
|
||||
## 7. Det som ikke er commons' å avgjøre
|
||||
|
||||
- **Runner og gate** — catalogs eierskap per D4. Om porten sammenligner byte-eksakt eller
|
||||
normalisert er catalogs implementasjonsvalg *inntil* specen pinner det; da blir det vårt.
|
||||
- **De adversarielle aksene** og korpus-containeren — catalog.
|
||||
- Ingenting her berører amendment-pakken. Køpunkt 1–7 i
|
||||
`2026-07-25-amendment-underlag.md` står uendret.
|
||||
|
||||
---
|
||||
|
||||
## 8. Det som ikke endres uansett utfall
|
||||
|
||||
Fixturene selv. `b641741` er levert og korrekt, og ingen av opsjonene over foreslår å røre
|
||||
`bundle/`, `expected-read-context.md` eller case-README-ene. Spørsmålet gjelder utelukkende
|
||||
om — og hvordan — spec-teksten viser til dem.
|
||||
|
||||
---
|
||||
|
||||
## Verifiseringslogg
|
||||
|
||||
| Påstand | Sjekk | Resultat |
|
||||
|---|---|---|
|
||||
| Fixturene levert 2026-07-21 | `git log --oneline -- examples/nav-golden-*` | `b641741`, eneste commit |
|
||||
| Ingen normativ fil nevner klassen | `grep -c 'nav-golden'` per fil | 0 / 0 / 0 / 0 / 0 |
|
||||
| Den siterte sjekken reproduserer ikke | `grep -rn 'nav-golden' *.md` | 3 treff, alle `STATE.md` |
|
||||
| method-spec navngir ingen repo-sti | `grep -c 'examples/' method-spec.md` | 0 |
|
||||
| method-spec navngir ikke fasit-bundelen | `grep -c 'bygg-energi-mikro' method-spec.md` | 0 |
|
||||
| `/`-tokenene er lenke-syntaks, ikke stier | `grep -oE '\`[^\`]*/[^\`]*\`' method-spec.md`, alle 10 lest | `sub/index.md`, `/a/b.md`, `./a.md`, `/` |
|
||||
| §7 sier «two» og «the only» | `method-spec.md:332-333` | ordrett sitert over |
|
||||
| §1 binder konformans til én bundle | `method-spec.md:23` | «on the shared example bundle» |
|
||||
| §11-raden navngir ingen fixture | `method-spec.md:425` | seam + rød-betingelse + `test_okf.py` |
|
||||
| ingest-golden er en katalogkonvensjon | `ingest-spec.md:259-267` | «one directory per case, convention …» + 4-rads tabell |
|
||||
| ingest-golden er byte-pinnet | `ingest-spec.md:267`, `:280` | «byte for byte» / «any byte … diverges» |
|
||||
| nav-golden er IKKE byte-pinnet | `examples/nav-golden-hierarchy/README.md:31-33` | «a gate MAY compare byte-exact or after … normalization» |
|
||||
| Escape-casen har fil utenfor `bundle/` | `ls examples/nav-golden-escape/` | `SHOULD-NOT-BE-READ.md` er søsken av `bundle/` |
|
||||
| README er eneste kataloglenke | `grep -n 'examples/' README.md` | `:19`, `bygg-energi-mikro` |
|
||||
| B1 = D4, ikke okf-eid | `~/.claude/coord/portfolio-optimiser/archive/20260725T041948Z-*` | okf: «never owned here; it is D4» |
|
||||
| Spec-tekst uendret siden 07-21 | `git log --oneline -- method-spec.md ingest-spec.md` | `9801d35` / `bfa5a9b` |
|
||||
130
shared/docs/plan/2026-07-25-ordering-axes-ruling.md
Normal file
130
shared/docs/plan/2026-07-25-ordering-axes-ruling.md
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
# Ordering axes — three distinct axes already present in the frozen spec
|
||||
|
||||
**Status:** INTERPRETATION of frozen spec text. This document changes **no** normative text and
|
||||
introduces **no** new requirement. Where this document and the spec disagree, **the spec wins** and
|
||||
this document is the thing that is wrong. It is a reading aid, not a contract.
|
||||
|
||||
**Issued:** 2026-07-25. Applied by at least one downstream implementation as binding for its own
|
||||
work before this record existed — which is why the record exists.
|
||||
|
||||
**Scope:** the disambiguation is **across** the two specs. Each axis is already stated, normatively,
|
||||
in one of them; nothing here is derived, inferred or extended. What was missing was a single place
|
||||
saying that these are three different things and which one governs a given piece of code.
|
||||
|
||||
## 1. Why this exists
|
||||
|
||||
Three separate implementations reached, independently, for "the ordering rule" and each picked a
|
||||
different one — twice arriving at code that looked correct and sat on the wrong contract. That
|
||||
failure mode is expensive precisely because it is quiet: an implementation on the wrong axis is
|
||||
usually deterministic, passes its own tests, and produces plausible output. Nothing fails until
|
||||
another consumer depends on the property the right axis guaranteed.
|
||||
|
||||
The specs are not at fault. Each axis is stated clearly where it belongs. The gap is that no single
|
||||
document said "there are three, they are not interchangeable, here is how to tell which one you are
|
||||
standing on."
|
||||
|
||||
## 2. The three axes
|
||||
|
||||
| | Axis | Question it answers | Normative source |
|
||||
|---|---|---|---|
|
||||
| **A** | Membership | *Which files does a re-materialization replace?* | `ingest-spec.md:175-177` |
|
||||
| **B** | Generation / index-link order | *In what order are generated files and their index links emitted?* | `ingest-spec.md:178-181`, §6 (`:183`) |
|
||||
| **C** | Navigation order | *In what order does a reader traverse a bundle?* | `method-spec.md:76-78` |
|
||||
|
||||
**Axis A — membership, read from disk.** The set of files a re-materialization replaces is exactly
|
||||
those the bundle already carries the stamp for, *"read from disk (§3), **never a caller-supplied
|
||||
file list**; a caller can therefore never name a file for deletion"* (`:175-177`). This is a safety
|
||||
property, not an ordering one: it is what keeps re-materialization from becoming a caller-controlled
|
||||
delete.
|
||||
|
||||
**Axis B — generation order, supplied by the caller.** *"The ORDER of generated files and their
|
||||
index links (§6) is the manifest's extraction order — supplied by the caller, **never filesystem
|
||||
enumeration order**"* (`:178-181`). This is a determinism property: output must not vary with
|
||||
directory iteration order.
|
||||
|
||||
**Axis C — navigation order, first-seen link order.** *"Traversal is **depth-first in first-seen
|
||||
link order** and fully deterministic (the root index first, then each link followed in the order it
|
||||
appears, recursively)"* (`method-spec.md:76-78`). This governs a **reader** walking an existing
|
||||
bundle. It says nothing about how that bundle came to be ordered.
|
||||
|
||||
A and B are deliberately split by the spec itself — `:175` labels the split *load-bearing* — because
|
||||
fusing them would trade a safety property for a determinism property. B and C are adjacent and
|
||||
easily confused: both are "order", but one describes an act of **writing** and the other an act of
|
||||
**reading**.
|
||||
|
||||
## 3. The test — which axis am I on?
|
||||
|
||||
Ask what the code *does*, not what it is called:
|
||||
|
||||
- **Does it decide what to delete or replace?** → **A**. The answer comes from disk. A caller-supplied
|
||||
list is a defect regardless of how convenient it is.
|
||||
- **Does it emit files or index entries?** → **B**. The order comes from the caller's extraction
|
||||
order. Any order the code chooses for itself is wrong — including a deterministic one.
|
||||
- **Does it read an existing bundle and visit its files?** → **C**. The order comes from the links,
|
||||
depth-first, first seen.
|
||||
|
||||
The trap is that "generator" and "reader" are roles, not file names. A module that both writes an
|
||||
index and reads one is on **both** B and C, and owes each its own rule.
|
||||
|
||||
## 4. Worked applications
|
||||
|
||||
**An index generator that sorts its entries alphabetically.** It emits index entries, so it is on
|
||||
**B**. Alphabetical order is deterministic, which is what makes this hard to see — but B does not ask
|
||||
for *a* deterministic order, it asks for *the caller's* order. Alphabetical is therefore
|
||||
deterministically **wrong**, not merely unlucky, and the fix is to thread the caller's extraction
|
||||
order through, not to substitute a different self-chosen sort. Attributing such a generator to axis A
|
||||
("it is about membership, so ordering is a benign detail") is the specific mis-read that lets the
|
||||
sort survive review.
|
||||
|
||||
**A convention that says a directory index is a "directory enumeration".** Ambiguous on exactly this
|
||||
axis, and worth resolving in the text rather than in the reader's head. If it means the index file
|
||||
*contains* an authored enumeration of that directory's children, it is compatible with C. If it means
|
||||
the reader *enumerates the directory* to navigate, it contradicts `method-spec.md:82-83`, which says
|
||||
an intermediate directory is *"navigated only through the links its own files carry, **never by
|
||||
directory enumeration**."* Prefer wording that cannot be read the second way.
|
||||
|
||||
**A re-materializer narrowing what it deletes.** On **A**. Note that A's text scopes membership by
|
||||
the stamp, and the stamp's identity field is `{manifest filename stem}@{h}` (`ingest-spec.md:168-170`)
|
||||
where `{h}` is a hash of the manifest's bytes. Matching on the stem rather than the full reference is
|
||||
what lets an edited manifest reclaim its own prior output, since `{h}` changes on every edit.
|
||||
(Separately: `ingest-spec.md:171-174` assumes **one** manifest per bundle in version 1 and names the
|
||||
multi-manifest case an extension point. Code that scopes ownership per-manifest is implementing that
|
||||
extension point and runs ahead of the frozen text — see the amendment queue, not this document.)
|
||||
|
||||
## 5. Axis B does not travel alone
|
||||
|
||||
An implementation adopting B inherits §6's idempotence with it, not afterwards:
|
||||
|
||||
- linking is **idempotent by target** — a link whose target is already present is never added twice
|
||||
(`ingest-spec.md:187-190`);
|
||||
- on re-materialization, index links whose target is an ingest-owned file removed in that run MUST be
|
||||
removed, and **all** other links — curated and promoted — are preserved verbatim (`:191-193`).
|
||||
|
||||
Taking the ordering rule without the idempotence rule produces duplicate links on the second run.
|
||||
The two are one package.
|
||||
|
||||
## 6. What this document does not say
|
||||
|
||||
- It does not rule on any axis outside these three. Installation root, bundle root and source root are
|
||||
a **different** family of distinctions and are not addressed here.
|
||||
- It does not make the multi-manifest extension point written, ratified, or safe to assume.
|
||||
- It does not bind any implementation's internal design — only the observable properties the specs
|
||||
already require.
|
||||
|
||||
## 7. Verification
|
||||
|
||||
Testable criteria, so this document can be checked rather than believed:
|
||||
|
||||
- **V1 (the anchors are real and still say what is quoted):**
|
||||
`grep -n 'Membership from disk' ingest-spec.md` → `:175`;
|
||||
`grep -n 'never filesystem enumeration order' ingest-spec.md` → `:179`;
|
||||
`grep -n 'depth-first in first-seen' method-spec.md` → `:76`.
|
||||
Verified 2026-07-25.
|
||||
- **V2 (no normative text was changed by this document):** `git show --stat` for the commit adding
|
||||
this file touches `docs/plan/` only — never `ingest-spec.md`, `method-spec.md` or `examples/`.
|
||||
- **V3 (the axes are genuinely distinct, not three phrasings of one rule):** A is sourced from disk,
|
||||
B from the caller, C from link order in the file. Substituting any one for another changes
|
||||
observable behaviour: A→B would let a caller name files for deletion; B→C would make output order
|
||||
depend on a reader's traversal; C→B would make navigation depend on how the bundle was generated.
|
||||
- **V4 (§6 idempotence is not separable from B):** `grep -n 'idempotent by target' ingest-spec.md` →
|
||||
a hit inside §6, i.e. in the same section B points at for index links.
|
||||
732
shared/docs/plan/2026-07-26-v1-generated-felt-okf-v0.2.md
Normal file
732
shared/docs/plan/2026-07-26-v1-generated-felt-okf-v0.2.md
Normal file
|
|
@ -0,0 +1,732 @@
|
|||
# V1 — hvilken form skal `generated` ha i ingest-spec etter OKF v0.2?
|
||||
|
||||
> **Status: AVGJORT 2026-07-31 — operatøren valgte O2** (`generated: { by: "process:<fast id>",
|
||||
> at: <ingested_at> }`). Underlaget under står uendret som grunnlaget vedtaket ble tatt på;
|
||||
> ingenting i §5–§7 er skrevet om i etterkant. Se **§4.1** for hva vedtaket utløser.
|
||||
>
|
||||
> **Vedtaket er IKKE utført.** `ingest-spec.md` står fortsatt uendret på `bfa5a9b`/`9801d35`.
|
||||
> Utførelsen er gated — se §4.1.
|
||||
>
|
||||
> Opprinnelig status: beslutningsunderlag for operatøren, fire opsjoner med målt kostnad, ingen
|
||||
> anbefaling. Utløst av `llm-ingestion-okf` (coord, 2026-07-26) som spør fordi authorship er
|
||||
> vår: deres DEFAULT-profil staterer ingest-spec §5, og «ingen lokale spec-endringer» er deres
|
||||
> stående non-goal.
|
||||
|
||||
Beslektet: `2026-07-25-amendment-underlag.md` (køen av ratifiserbare punkter — V1 hører hjemme
|
||||
der hvis den ratifiseres), `2026-07-25-b1-nav-golden-normative-status.md` (samme form).
|
||||
|
||||
---
|
||||
|
||||
## 1. Det som ikke er oppe til vurdering
|
||||
|
||||
- **Ærlighetsregelen selv** (`ingest-spec.md:33-34`, avledet av `method-spec.md:26`) er
|
||||
*unwaivable*. Alle fire opsjoner under oppfyller den. Spørsmålet er hvilken **form**
|
||||
merkingen har, aldri **om** den finnes.
|
||||
- **Eierskaps-stempelets funksjon** (`:70`, `:82`): at re-materialisering bare rører egne filer,
|
||||
og at en kurert skriver avviser det *komplette* stempelet. Ingen opsjon svekker den regelen.
|
||||
- **`ingested_at` som eksplisitt påkrevd argument** (`:139-143`). Ingen opsjon innfører
|
||||
wall-clock. Der `at` finnes, bindes den til `ingested_at`.
|
||||
- **Hastegrad.** `llm-ingestion-okf` er *ikke* blokkert: deres v0.2-støtte kommer som en ny
|
||||
profil ved siden av DEFAULT, additivt. Ingenting her er en brannslukking.
|
||||
|
||||
## 2. Premissene, verifisert
|
||||
|
||||
`llm-ingestion-okf`s to påstander er kontrollert mot kilden, ikke overtatt:
|
||||
|
||||
- **Oppstrøms:** OKF v0.2 (`GoogleCloudPlatform/knowledge-catalog`, `okf/SPEC.md`) definerer
|
||||
`generated: { by, at }`, og `generated.by` er **REQUIRED within `generated`**, typet som en
|
||||
aktør per §7. Aktørkonvensjonen har **tre** former: `<producer>/<version>` for agenter og
|
||||
verktøy, `human:<id>` for en person, `process:<id>` for en automatisert prosess.
|
||||
- **Toleransen dekker det ikke.** v0.2 sier at konsumenter «MUST NOT reject documents with
|
||||
unrecognized fields» og ikke skal avvise for *manglende valgfrie* felter eller *ukjente*
|
||||
nøkler. Ingen setning pålegger en leser å svelge en **kjent nøkkel med feil type**.
|
||||
`llm-ingestion-okf`s lesning holder.
|
||||
- **Historikken frikjenner valget.** `generated` var ikke reservert i v0.1. Ingen innførte en
|
||||
defekt; v0.2 tok navnet etterpå.
|
||||
|
||||
**Én forbeholdsrad:** deres egen plan (`okf-v0.2-alignment.md`) slår fast at spec-en er lest på
|
||||
`main` — en gren, ikke en tag — og at «an enumeration read off a moving branch is a premise, not
|
||||
a fact». Vår kontroll traff samme bevegelige gren. **Ingen frossen spec-tekst bør endres før
|
||||
oppstrømsversjonen er pinnet til en commit.** Det gjelder O1, O2 og O3 likt.
|
||||
|
||||
## 3. `generated` bærer to laster i vår spec, ikke én
|
||||
|
||||
Dette er funnet som former underlaget, og grunnen til at opsjonssettet ikke er tre.
|
||||
|
||||
Nøkkelen opptrer 7 ganger som kontraktsreferanse, fordelt på fem seksjoner. De deler seg på
|
||||
**to akser** som ingen av dem navngir:
|
||||
|
||||
| Last | Hvor | Hva den svarer på | Hvem leser den |
|
||||
|---|---|---|---|
|
||||
| **Ærlighetsmarkør** | §1 `:34`, §7 `:214` | «er dette maskingenerert?» | en leser/presentatør av bundelen |
|
||||
| **Eierskapspredikat** | §3 `:70`, §3 `:82`, §11 `:275` | «hvilke filer eier ingest — og hva skal en kurert skriver avvise?» | materialiseringen og dør C |
|
||||
|
||||
Oppstrøms `generated { by, at }` er **provenance/attribusjon** — den første lasten, ikke den
|
||||
andre. v0.2 har ingen skriveeierskaps-semantikk; feltet er valgfritt, konsument-skrivbart og
|
||||
fritt for enhver aksesskontrollmening.
|
||||
|
||||
Konsekvensen er ikke at feltet ikke *kan* bære begge, men at en opsjon som flytter
|
||||
eierskapspredikatet over på et oppstrømsdefinert felt **må uttale det**: predikatet går fra en
|
||||
literal-sjekk (`generated == true`) til en parse-og-match på et felt hvis grammatikk oppstrøms
|
||||
eier og fritt kan revidere. Konjunksjonen med `ingest_manifest` — vår egen nøkkel, ikke reservert
|
||||
oppstrøms i noen versjon — bærer fortsatt uforfalskbarhet-mot-uhell, så stempelet kollapser
|
||||
ikke. Men `:82`s «permitting either field alone» må omformuleres: under v0.2 vil *kurert*
|
||||
innhold legitimt kunne bære en `generated`-mapping med en helt annen `by`.
|
||||
|
||||
> **Empirisk bekreftet 2026-07-26, ikke lenger bare utledet.** Oppstrøms skriver `generated` på
|
||||
> **håndskrevet** innhold. Verifisert her:
|
||||
> `generated: { by: human:jsmith@acme, at: 2024-01-15T10:00:00Z }`
|
||||
> (`okf/bundles/acme_retail/metrics/gross-margin-legacy.md`, en menneskeskrevet metrikkdefinisjon).
|
||||
>
|
||||
> **Konsekvensen gjelder uansett hvilken opsjon som vedtas:** en implementasjon som utleder «er
|
||||
> dette maskingenerert?» eller «eier vi denne fila?» fra at `generated` **finnes**, er feil under
|
||||
> v0.2. Under O1/O2 må predikatet derfor være `generated.by == <vår aktør>` **og**
|
||||
> `ingest_manifest` — aldri nøkkelens tilstedeværelse. Under O0 og O3 er poenget uten virkning,
|
||||
> siden markøren vår da ikke deler navn med et oppstrømsfelt som brukes slik.
|
||||
|
||||
### 3.1 Målt i den pinnede implementasjonen — predikatet er KONJUNKSJONEN, og det er konformt
|
||||
|
||||
`portfolio-optimiser-claude` meldte 2026-07-26 at `generated` «er et eierskaps-predikat i den
|
||||
pinnede v0.3.2», som en spenning mot oppstrøms' rene attribusjon. **Vi målte deres kilde. Funnet
|
||||
er ekte, men konklusjonen er én hakk for sterk — og forskjellen er akkurat den V1 handler om.**
|
||||
|
||||
Lest i `llm_ingestion_okf/materialize.py` @ **0.3.2** (deres `.venv`, verifisert her):
|
||||
|
||||
```python
|
||||
# :86-89 §3/§5 ownership: the ingest stamp is `generated: true` AND an
|
||||
# `ingest_manifest` reference. ← deres egen kodekommentar
|
||||
def _is_ingest_owned(path: Path) -> bool:
|
||||
frontmatter = _parse_frontmatter(path)
|
||||
return frontmatter.get("generated") == "true" and "ingest_manifest" in frontmatter
|
||||
# :103 emitter: "generated": "true"
|
||||
# :258 eneste kaller → bygger eierskapsmengden
|
||||
# :260-268 kollisjonsporten → MaterializationError(code="collision_unstamped")
|
||||
```
|
||||
|
||||
Porten leser **konjunksjonen**, ikke feltet. Det er nøyaktig det frossen tekst krever, ordrett:
|
||||
|
||||
> **`ingest-spec.md` §3, «No other writer may forge the stamp»** — «the *complete* ownership
|
||||
> stamp — `generated: true` together with an `ingest_manifest` reference — while permitting
|
||||
> either field alone … **The check is on the complete stamp, never on the individual field
|
||||
> names**»
|
||||
|
||||
**Så det finnes ingen «feltet ER to ting»-konflikt mellom oss og oppstrøms på predikat-nivå:**
|
||||
`generated` alene har aldri båret eierskap, verken i vår tekst eller i deres implementasjon.
|
||||
Aksefunnet i §3 står uendret — de to lastene er reelle — men eierskapslasten hviler på
|
||||
konjunksjonen, og bare ærlighetsmarkør-lasten ligger på feltet alene.
|
||||
|
||||
**Det meldingen FAKTISK avdekker, og som er en ny kostnadsrad:** emitter (`:103`) og port
|
||||
(`:89`) er koblet gjennom strengliteralen `"true"`. Blir `generated` en mapping, feiler
|
||||
konjunksjonens **første ledd** — og da faller emitter og port samtidig, i samme fil. Det er en
|
||||
migrasjonskostnad **under O1 og O2 likt**, og den er ikke synlig fra spec-teksten alene:
|
||||
|
||||
| Opsjon | Hva `_is_ingest_owned` må bli |
|
||||
|---|---|
|
||||
| O0 / O3 | uendret (`== "true"` består; under O3 med ny nøkkel) |
|
||||
| O1 / O2 | `generated.by == <vår aktør>` **og** `ingest_manifest` — parse, ikke literal-sammenligning |
|
||||
|
||||
**Versjonsforskjellen som ingen av de to meldingene har sett** (målt her, i begge trær):
|
||||
|
||||
| Tre | `_is_ingest_owned`-signatur | Eierskapsporten er |
|
||||
|---|---|---|
|
||||
| v0.3.2 (det BEGGE konsumenter kjører) | `(path)` — `:84` | **global** over bundelen |
|
||||
| `llm-ingestion-okf` HEAD | `(path, manifest_stem, *, profile)` — `:132`, kalt `:393` | **per manifest** |
|
||||
|
||||
`portfolio-optimiser-claude` beskriver v0.3.2-oppførsel; `llm-ingestion-okf` beskriver
|
||||
HEAD-oppførsel (deres Q3-punkt om at «stemmen bærer per-manifest eierskap i kollisjonsporten»).
|
||||
**Begge er sanne om hvert sitt tre, og de er ulike på nøyaktig det punktet begge meldingene
|
||||
handler om.** Samme akseklasse som resten av dette dokumentet: «implementasjonen» er ikke én
|
||||
ting når trærne ikke er pinnet til hverandre.
|
||||
|
||||
## 4. Spørsmålet
|
||||
|
||||
> Skal `generated` i ingest-spec §5/§7 forbli den literale `true`, anta v0.2-formen
|
||||
> `{ by, at }` — og i så fall med hvilken aktørstreng — eller skal vår markør flytte til en
|
||||
> nøkkel oppstrøms ikke eier?
|
||||
|
||||
### 4.1 Svaret, og hva det utløser (2026-07-31)
|
||||
|
||||
**Vedtatt: O2** — `generated: { by: "process:<fast id>", at: <ingested_at> }`, predikatet utledet
|
||||
av `generated.by`. Aktørstrengen er specens, ikke produsentens.
|
||||
|
||||
**Hva vedtaket IKKE avgjør.** O2 ble valgt uten å ta stilling til `:29` — og trenger det ikke.
|
||||
Det var nettopp O1 som ikke kunne vedtas alene (§6). O2 lar `:29` stå ordrett som den er, og
|
||||
fasit-bytene forblir produsent-nøytrale. `:29` er dermed ikke i køen som følge av dette vedtaket.
|
||||
|
||||
**⛔ Utførelsen er gated, og gaten er ikke vår.** Ingen frossen tekst endres før
|
||||
`llm-ingestion-okf` er **pinnet til en commit**. Vår forrige kryssgrense-kontroll traff `main` —
|
||||
en bevegelig gren — og det ble korreksjonen i `ab0ea8f`. Gaten gjelder O2 som den gjaldt O1/O3.
|
||||
|
||||
**Køen dette legger seg i, når gaten åpner** (5 av 7 kontraktslinjer, uendret fra §5):
|
||||
`:34`, `:70`, `:82`, `:214`, `:275`. `:152` og `:309` navngir bare nøkkelen og overlever.
|
||||
V1 hører hjemme i `2026-07-25-amendment-underlag.md`s kø ved ratifisering.
|
||||
|
||||
**Varslingsplikten er utløst, ikke lenger betinget.** §9s formulering var «vedtas O2, skal
|
||||
`llm-ingestion-okf` ha beskjed FØR de fryser en v0.2-fikstur som bærer `generated`». O2 ER
|
||||
vedtatt, så plikten er live og forfaller ved deres neste fikstur-frys — ikke ved vår utførelse.
|
||||
Varslet sendt 2026-07-31, samtidig som dette føres.
|
||||
|
||||
**Serialiseringsformen er BUNDET, og ikke av dette vedtaket** (tilført 2026-07-31 etter at
|
||||
`portfolio-optimiser-claude` og deres måling reiste spørsmålet to ganger). Begge spurte om §4.1
|
||||
binder inline flow normativt eller lar serialiseringen være implementasjonsvalg — forskjellen er
|
||||
6 mot 7 sider hos dem. **Svaret er at spørsmålet allerede er avgjort av frossen tekst, og at V1
|
||||
derfor ikke skal binde noe:**
|
||||
|
||||
- `ingest-spec.md:158` (inne i §5s frontmatter-punkt, som definerer det påkrevde ordnede
|
||||
prefikset `generated` ligger i): **«All values MUST be single-line»**. `MUST`, i kraft i dag,
|
||||
uendret siden `bfa5a9b`.
|
||||
- Blokk-form er per definisjon flerlinjes. Den er derfor **allerede ikke-konform** — ikke som
|
||||
følge av O2, men som følge av en regel som har stått hele tiden.
|
||||
- **Inline flow er den eneste konforme serialiseringen.** `generated: { by: …, at: … }` er
|
||||
single-line og line-oriented, og oppfyller `:150` og `:158` slik §5 allerede slår fast.
|
||||
|
||||
**Konsekvens: 6 er invariant, ikke betinget.** Den betingede raden (`O2, blokk-form → 7 sider`)
|
||||
beskriver en form specen ikke tillater. V1 trenger ingen ny kontraktslinje for å binde formen,
|
||||
og `:152`/`:309` overlever fortsatt. Samme klasse som D-B: **et spørsmål som ser åpent ut, men
|
||||
er avgjort av tekst som allerede er frossen.** Målingen deres er likevel verdifull, og av en
|
||||
annen grunn enn de sendte den: den viser at `test_provenance_keys_are_in_the_spec_order`
|
||||
faktisk er en søm på `:158`-konformitet — den ville gått rød hvis noen emitterte blokk-form,
|
||||
altså brøt `:158`. Det er en egenskap ingen hadde lagt merke til.
|
||||
|
||||
**Konsument-kostnaden er kjent på forhånd** (§5.1, målt @ `8a14137`): **6 sider** hos
|
||||
`portfolio-optimiser-claude` — 4 byte-frosne fasit-blober + 2 verbatim likhets-assert. De 3
|
||||
navn/orden-sidene rører O2 **ikke** (ordens-testen bygger nøkkelen med `ln.split(":", 1)[0]`, som
|
||||
fortsatt gir `generated`). Fasit-blobene er en **fasit-endring, ikke en kodeendring** — method
|
||||
spec §7 gjør goldenen til eneste fasit.
|
||||
|
||||
**Det som fortsatt er åpent og IKKE følger av dette vedtaket:** hvilken `<fast id>` strengen
|
||||
skal bære. O2 fastslår formen (`process:<id>`, som v0.2 §7 eksplisitt tillater) og at aktøren
|
||||
navngir prosessen specen definerer. Selve id-en er en redaksjonell avgjørelse som tas når
|
||||
kontraktslinjene skrives, og bør avklares med `llm-ingestion-okf` i samme runde som pinnen.
|
||||
|
||||
### 4.2 Pinnen, id-en og siteringen — avgjort 2026-07-31 (økt 4)
|
||||
|
||||
`llm-ingestion-okf` svarte samme dag. Tre ting falt på plass, og ett premiss i §4.1 viste seg
|
||||
for svakt formulert. Grunnlaget over står ordrett uendret; dette er status, ikke omskriving.
|
||||
|
||||
**Pinnen foreligger: `2504011`** — «feat(okf-v0.2): D5 — the v0.2 golden fixture, with
|
||||
`okf_version` in root frontmatter», pushet til `open/llm-ingestion-okf`. Det er en PIN, sagt
|
||||
eksplisitt som sådan. `6f42c10`/`ed08ac1` var siterte refs; skillet holdt.
|
||||
|
||||
**Deres v0.2-fikstur var aldri rammet, og det er målt, ikke antatt.** Den bar O2-formen fra
|
||||
`c90171d` (07-27), vedtatt uavhengig av oss. Vår varsling traff et annet sett: de fire
|
||||
DEFAULT-profil-fasitene (`ingest-golden-{file,sql,http}`, alle `:8`), som er VÅRT lag å endre.
|
||||
|
||||
**`<fast id>` = `process:okf-ingest`** (operatøren, 2026-07-31). Okfs eget forslag. De foreslo
|
||||
først `process:llm-ingestion-okf` og argumenterte samtidig mot den — riktig, og strengere enn
|
||||
de kunne se herfra:
|
||||
|
||||
| Kilde | Ordrett | Frossen siden |
|
||||
|---|---|---|
|
||||
| `ingest-spec.md:8-9` | «The prose is framework-neutral by rule: it never names a concrete agent toolkit or vendor stack, and a guard test keeps it that way» | `7aa53fc` (07-03) |
|
||||
| `ingest-spec.md:7-8` | «implemented **from this spec alone** — without reverse-engineering any existing implementation» | `7aa53fc` |
|
||||
| `ingest-spec.md:282` (§11-seam «Spec integrity») | «this spec goes missing, **names a concrete agent toolkit**, or stops documenting a contract field» | `7aa53fc` |
|
||||
|
||||
**Presisjonsforbehold, så raden ikke føres for sterkt:** `llm-ingestion-okf` er en
|
||||
ingest-implementasjon, ikke strengt tatt en «agent toolkit» — `:282` treffer derfor ikke
|
||||
ordrett. Det er `:7-8` som treffer uten tolkningsrom: å normere produsentens repo-navn ville
|
||||
tvunget enhver annen konform implementasjon til å skrive det navnet i sin egen output. Samme
|
||||
klasse som `:29` tvang O1 ut på, bare svakere. **Utelukkelsen holder, men på `:7-8`, ikke `:282`.**
|
||||
|
||||
**Siteringen er IKKE normativ — formen er usitert.** Okf spurte om anførselstegnene i
|
||||
`by: "process:<fast id>"` var normative eller illustrative. **Spørsmålet er avgjort av frossen
|
||||
tekst — fjerde gang** (etter D-B, `:29`-vs-O1 og `:158`):
|
||||
|
||||
- `method-spec.md:90` (frossen `7d2b46c`, 07-03): «Frontmatter is the leading `---`-delimited
|
||||
block, **parsed line-oriented as `key: value` strings**».
|
||||
- Vi parser altså ikke YAML. Verdien ER den rå teksten etter `key: `. Det finnes ingen
|
||||
transparent sitering i formatet: et anførselstegn er **et tegn i verdien**, ikke syntaks som
|
||||
en parser fjerner. Normert sitat ⇒ predikatet måtte matchet anførselstegnene som datainnhold.
|
||||
- Målt bekreftelse: v0.3.2s port sammenligner mot **strengen** `"true"` (§3.1 `:100`), ikke mot
|
||||
en boolean — nøyaktig fordi parsingen er line-oriented.
|
||||
- Oppstrøms er selv usitert **med kolon i verdien**: `by: human:jsmith@acme` (§3s empiri).
|
||||
|
||||
Kanonisk form, vedtatt: `generated: { by: process:okf-ingest, at: <ingested_at> }`
|
||||
|
||||
**⛔ KORREKSJON av §4.1: pin var ikke den siste gaten.** §4.1 skrev «Ingen frossen tekst endres
|
||||
før `llm-ingestion-okf` er pinnet til en commit». Det er en nødvendig, ikke tilstrekkelig
|
||||
betingelse, og formuleringen ville — lest alene — hjemlet å skrive de 5 linjene nå. Den er
|
||||
korrigert av avstemt tekst i søsterunderlaget:
|
||||
|
||||
> `2026-07-25-amendment-underlag.md:495-496` — «vi forbereder underlaget, **operatøren
|
||||
> ratifiserer**, og **frossen tekst endres ikke uten den ratifiseringen**»
|
||||
|
||||
Køens rad 8 er B1/D4 — **V1 står ennå ikke i køen**, slik §8 alltid har sagt («V1 er punkt
|
||||
nummer 8 *hvis den ratifiseres*»). Pin + id lukket gaten for at V1 kan gå I KØ; ratifiseringen
|
||||
er en separat, operatør-eid handling, og amendment-pakken er på bevisst hold. **`ingest-spec.md`
|
||||
står uendret på `bfa5a9b`; de 5 linjene bærer fortsatt literal `generated: true`.**
|
||||
|
||||
**B1 er ikke presedens for det motsatte:** `8a7d430` rørte `README.md` («katalog, ikke
|
||||
kontrakt») og to planfiler — null normative filer. Commit-meldingen sier det selv om V1: «Ingen
|
||||
frossen tekst er rørt.»
|
||||
|
||||
**Varslet til okf er sendt** (07-31, som svar på pinnen): id + siteringsform vedtatt, og et
|
||||
eksplisitt **ikke regenerer ennå** — regenerering mot ikke-ratifisert tekst ville pekt fasiten
|
||||
deres på en spec som ikke finnes. Varslingsplikten ved faktisk tekstendring står fortsatt live.
|
||||
|
||||
## 5. Opsjonene, med målt kostnad
|
||||
|
||||
Kostnad er talt som **kontraktslinjer som må skrives om** av de 7 (`:34`, `:70`, `:82`, `:152`,
|
||||
`:214`, `:275`, `:309`).
|
||||
|
||||
**Det som ikke koster noe i noen opsjon:** §5s formkrav overlever uendret **for `generated`**.
|
||||
Prefikset er «OKF line-oriented `key: value`» (`:150`) og «All values MUST be single-line»
|
||||
(`:158`) — v0.2s kanoniske form for `generated` er en **inline flow mapping** på én linje, så den
|
||||
er single-line og line-oriented allerede (verifisert oppstrøms:
|
||||
`generated: { by: reference_agent/gemini-2.5-pro, at: 2026-06-30T14:00:00Z }`). `:153`
|
||||
(«additional frontmatter keys MAY follow») er heller ikke i veien. Ingen opsjon tvinger fram en
|
||||
rikere frontmatter-modell i *specen*.
|
||||
|
||||
> **Presisering 2026-07-26** (`llm-ingestion-okf`): setningen over er målt på `generated` **alene**,
|
||||
> og skal ikke leses som at `:158` overlever v0.2 i sin helhet. Det gjør den ikke. `sources` —
|
||||
> et annet nytt v0.2-felt — er kanonisk en **blokkliste**, verifisert oppstrøms:
|
||||
> ```yaml
|
||||
> sources:
|
||||
> - id: margin-standard
|
||||
> resource: policies/margin-standard.md
|
||||
> author: human:jsmith@acme
|
||||
> ```
|
||||
> Den formen bryter `:158` direkte. Det er **utenfor V1**, som bare gjelder `generated`, og
|
||||
> DEFAULT-profilen emitterer ikke `sources` — men det er et reelt §5-spørsmål den dagen noen vil
|
||||
> emittere v0.2s øvrige felter, og det er commons' å svare på. Ikke i køen, ikke meldt videre.
|
||||
|
||||
### O0 — la `generated: true` stå, dokumentér avviket
|
||||
|
||||
**0 av 7 linjer.** Ingen frossen tekst røres; ingenting går i ratifiseringskøen. Eneste
|
||||
tilføyelse er en merknad om at nøkkelen kolliderer med et v0.2-definert felt.
|
||||
|
||||
Kostnad: DEFAULT fortsetter å emittere en v0.2-definert nøkkel med v1-verdi. En v0.2-leser har
|
||||
ingen plikt til å akseptere den (§2). Kollisjonen forsvinner ikke — den venter.
|
||||
|
||||
**Reverserbar.** Ja.
|
||||
|
||||
### O1 — v0.2-form med produsent-aktør (`llm-ingestion-okf`s anbefaling)
|
||||
|
||||
`generated: { by: "llm-ingestion-okf/<versjon>", at: <ingested_at> }`, predikatet utledet av
|
||||
`generated.by`.
|
||||
|
||||
**5 av 7 linjer** (`:34`, `:70`, `:82`, `:214`, `:275`). `:152` og `:309` navngir bare nøkkelen
|
||||
og overlever.
|
||||
|
||||
**Denne opsjonen kolliderer med en eksisterende normativ setning — se §6.** Kostnaden er ikke
|
||||
bare fikstur-regenerering.
|
||||
|
||||
> **Evidens FOR O1, tilført 2026-07-26.** `llm-ingestion-okf` korrigerte sin egen anbefaling da de
|
||||
> hadde lest oppstrøms' *bundles* og ikke bare SPEC.md: **oppstrøms referanseimplementasjon bruker
|
||||
> selv produsent/versjon-formen.** Verifisert her, uavhengig:
|
||||
> `generated: { by: reference_agent/gemini-2.5-pro, at: 2026-06-30T14:00:00Z }`
|
||||
> (`okf/bundles/acme_retail/metrics/gross-margin.md`). `<produsent>/<versjon>` er altså ikke en
|
||||
> avvikende lesning av §7 — det er den **kanoniske formen for et verktøy**, brukt av spec-eieren.
|
||||
> O1 er dermed den v0.2-*idiomatiske* opsjonen, og O2 er det bevisste avviket fra idiomet.
|
||||
> Funnet i §6 står uendret ved siden av dette: oppstrøms' bundles er utdata fra én agent, ikke
|
||||
> delte konformansfiksturer, så de to fakta er ikke i motstrid. **Det er nettopp byttet
|
||||
> operatøren skal ta stilling til:** v0.2-idiomatikk mot reproduserbar delt fasit.
|
||||
|
||||
**Reverserbar.** Nei — frossen normativ tekst.
|
||||
|
||||
### O2 — v0.2-form med produsent-nøytral aktør
|
||||
|
||||
`generated: { by: "process:<fast id>", at: <ingested_at> }`. v0.2 §7 tillater eksplisitt
|
||||
`process:<id>` for en automatisert prosess (verifisert, §2). Aktøren navngir da **prosessen
|
||||
specen definerer**, ikke verktøyet som kjørte den.
|
||||
|
||||
**5 av 7 linjer** — nøyaktig samme som O1. Den eneste tekstlige forskjellen mot O1 er
|
||||
aktørstrengen; den avgjør til gjengjeld §6.
|
||||
|
||||
Kostnad: `generated.by` identifiserer ikke lenger *hvilket* verktøy som produserte filen.
|
||||
Produsent-identitet må da bo i implementasjonens egen logg. Om det er et tap eller et krav
|
||||
avhenger av §6.
|
||||
|
||||
**Reverserbar.** Nei.
|
||||
|
||||
### O3 — døp om vår markør, la `generated` være fri
|
||||
|
||||
Vår markør flyttes til en nøkkel oppstrøms ikke eier; `generated` overlates til v0.2-semantikk.
|
||||
|
||||
**7 av 7 linjer** — også `:152` (prefiks-lista) og `:309` (kryss-sjekk-tabellen), fordi nøkkelen
|
||||
selv skifter navn. Dyrest i tekst.
|
||||
|
||||
Til gjengjeld: predikatet bevares **eksakt** som en literal-sjekk, aksene i §3 skilles ved
|
||||
konstruksjon, og kollisjonen kan ikke gjenoppstå ved neste oppstrømsrevisjon. v0.2 forbyr det
|
||||
ikke — de nye feltene er valgfrie oppstrøms.
|
||||
|
||||
Kostnad: interop-tapet er reelt. En v0.2-leser som spør «er dette maskingenerert?» via
|
||||
`generated` får ikke svar. Ærlighetsregelen er oppfylt internt, men ikke lesbar for formatets
|
||||
egne lesere — som er en del av hvorfor vi ligger i OKF i det hele tatt.
|
||||
|
||||
**Reverserbar.** Nei.
|
||||
|
||||
### 5.1 Konsument-kostnaden, målt i `portfolio-optimiser-claude` @ `8a14137`
|
||||
|
||||
Tallene over teller **commons' egne kontraktslinjer**. De sier ingenting om hva en opsjon koster
|
||||
der specen konsumeres. `portfolio-optimiser-claude` meldte 2026-07-31 hva raden koster hos EN
|
||||
konsument. **Hele målingen er reprodusert her, read-only i deres tre, pinnet til `8a14137`
|
||||
(2026-07-26)** — den føres derfor som *målt*, ikke som referert. (Pinnet, ikke `main`: forrige
|
||||
kryssgrense-sjekk traff en bevegelig gren, og det ble korreksjonen i `ab0ea8f`.)
|
||||
|
||||
> Nummerering: meldingen kaller dette «kø-punkt 8». **Vårt** kø-punkt 8 er B1/D4 nav-golden
|
||||
> (`2026-07-25-amendment-underlag.md` §9), og V1 ligger ikke i den køen i det hele tatt. Hvilken
|
||||
> liste deres 8 tilhører, vet vi ikke — og gjetter ikke.
|
||||
|
||||
**To lag, og de faller ikke sammen.** Verdi-laget bærer strengliteralen `"true"`; navn/orden-laget
|
||||
bærer nøkkelnavnet `generated` og plassen det har i §5-prefikset. Mapping-form rører bare det
|
||||
første. En omdøping rører begge.
|
||||
|
||||
| Sted (konsumenten) | Lag | O0 | O1 / O2 | O3 |
|
||||
|---|---|---|---|---|
|
||||
| 4 byte-frosne fasit-blober | verdi | — | RØD | RØD |
|
||||
| 2 verbatim likhets-assert | verdi | — | RØD | RØD (`KeyError`) |
|
||||
| 3 navn/orden-sider | navn/orden | — | — | RØD |
|
||||
| **Sum sider som må endres** | | **0** | **6** | **9** |
|
||||
|
||||
**Fasit-blobene** (`generated: true` på linje 8 i det byte-sammenlignede prefikset, alle fire):
|
||||
`examples/ingest-golden-file/expected-bundle/ingest-costs.md` · `.../ingest-edge.md` ·
|
||||
`examples/ingest-golden-sql/expected-bundle/ingest-costs.md` · `.../ingest-meta.md`.
|
||||
Mekanismen er `tests/test_ingest_golden.py:36` — `read_bytes() == read_bytes()` per fil. Method
|
||||
spec §7 gjør goldenen til eneste fasit, så dette er en **fasit-endring, ikke en kodeendring**.
|
||||
|
||||
**Verdi-assertene:** `tests/test_ingest_loadbearing.py:59` og
|
||||
`tests/test_ingest_sql_loadbearing.py:84` — begge `frontmatter["generated"] == "true"`, begge på
|
||||
§11-sømmen «Provenance stamping». Under O1/O2 feiler sammenligningen; under O3 finnes ikke
|
||||
nøkkelen.
|
||||
|
||||
**Navn/orden-sidene — den raden konsumenten selv ikke priset:**
|
||||
`tests/test_ingest_loadbearing.py:32` og `tests/test_ingest_sql_loadbearing.py:34`
|
||||
(`_PROVENANCE_KEYS`-tuplene), samt `tests/test_ingest_loadbearing.py:67-75`
|
||||
(`test_provenance_keys_are_in_the_spec_order`, navnet på `:74`). Ordens-testen bygger nøklene med
|
||||
`ln.split(":", 1)[0]`, så `generated: { by: …, at: … }` gir fortsatt `generated` — **mapping-form
|
||||
bryter ingen av de tre**, ordrett som meldingen sier. En **omdøping** bryter alle tre. Meldingen
|
||||
tok ikke stilling til O3; denne raden er målt her.
|
||||
|
||||
**Presisjon på det fjerde stedet meldingen fører opp.**
|
||||
`tests/test_ingest_spec_loadbearing.py:49` enumererer navnet `generated` — men i
|
||||
`_CONTRACT_FIELDS`, og testen (`:68-71`) er `assert field in text` over **spec-teksten**, ikke
|
||||
over frontmatter. Den kan derfor ikke bli rød under **noen** opsjon: prosaen «machine-generated»
|
||||
(`ingest-spec.md:33`, `:214`, m.fl.) metter delstrengen selv om begge felt-radene slettes. Annen
|
||||
mekanisme, og for akkurat dette feltet nær vakuøs. Om det er verdt en søm, er konsumentens
|
||||
operatørs sak, ikke vår.
|
||||
|
||||
**Det meldingen bekrefter uten å legge til kostnad:** deres `shared/ingest-spec.md:186` («`generated`
|
||||
| Literally `true` — the machine-generated marker») er vår `:214` — én av de 5 av 7 linjene O1/O2
|
||||
allerede betaler. Normativ tekst må endres i SAMME amendment, ja; den er talt. Prosa-treffene
|
||||
(`docs/extending.md:96,98`, `docs/oppskrift-kunnskapsbase.md:109`) er dokumentasjon, ikke sømmer,
|
||||
og telles ikke.
|
||||
|
||||
**Forholdet til §3.1 — to akser, ikke to tabeller som er uenige.** §3.1 teller hva
|
||||
`_is_ingest_owned` må *bli* (predikatets form: literal-sammenligning vs. parse) og sier O0/O3
|
||||
lar den stå uendret. §5.1 teller hvilke *sider* som blir røde. O3 lar predikatets form stå og
|
||||
flytter kostnaden til navn/orden-laget; O1/O2 gjør det motsatte.
|
||||
|
||||
**Dette er en pris, ikke en dom.** Argumentene for O3 i §5 står uendret — predikatet bevares som
|
||||
literal-sjekk, aksene i §3 skilles ved konstruksjon. Raden sier bare at O3 er dyrest **også** hos
|
||||
konsumenten, ikke bare i commons' tekst. Valget er operatørens.
|
||||
|
||||
## 6. Funnet som omformer opsjonssettet (ikke en anbefaling)
|
||||
|
||||
**O1 som formulert kolliderer med konformansleddet i §1.**
|
||||
|
||||
- `ingest-spec.md:29`: en konform implementasjon MUST «reproduce the shared golden extractions
|
||||
(§11) byte for byte».
|
||||
- `ingest-spec.md:267`: `expected-bundle/` er «compared file by file, **byte for byte**».
|
||||
- `ingest-spec.md:280`: sømmen `Golden regression` fyrer når «any byte of a golden extraction's
|
||||
expected bundle diverges».
|
||||
- `generated` ligger i det **obligatoriske ordnede prefikset** (`:149-152`) — altså inne i de
|
||||
bytene.
|
||||
|
||||
Med `by: "llm-ingestion-okf/<versjon>"` bærer en **delt** fasit én produsents navn og
|
||||
versjonsnummer. Da følger to ting mekanisk:
|
||||
|
||||
1. **Ingen annen konform implementasjon kan reprodusere fasiten byte for byte.** Kravet i `:29`
|
||||
gjelder enhver konform implementasjon, ikke den som lagde fikstursettet. Kravet blir
|
||||
uoppfyllbart ved konstruksjon for alle andre enn én.
|
||||
2. **`Golden regression` fyrer på hver versjonsbump av biblioteket** — uten at noen kontrakt har
|
||||
endret seg. Sømmen slutter å måle det den er satt til å måle.
|
||||
|
||||
O2 unngår begge: aktørstrengen er da specens, ikke produsentens, og bytene forblir
|
||||
produsent-nøytrale. O0 og O3 berører ikke spørsmålet.
|
||||
|
||||
**Merk hva funnet ikke er.** Det er ikke et argument for O2 og mot O1 i seg selv. Operatøren kan
|
||||
gyldig svare at fasit-klassen aldri får mer enn én produsent, eller at `generated` skal ut av
|
||||
det byte-sammenlignede prefikset. Men da er *det* valget som må ratifiseres — funnet sier bare
|
||||
at O1 ikke kan vedtas uten samtidig å ta stilling til `:29`.
|
||||
|
||||
### 6.1 Rekkevidde-forbeholdet, korrigert 2026-07-26
|
||||
|
||||
Første utgave skrev at konflikten er «normativ, ikke observerbar — den utløses den dagen commons
|
||||
publiserer sin første ingest-fasit». **Det var for snevert, og `llm-ingestion-okf` korrigerte
|
||||
det med en måling.** Riktig bilde, etter kontroll i alle tre trær:
|
||||
|
||||
- `examples/ingest-golden-*` finnes fortsatt **ikke i commons** — og har aldri gjort det
|
||||
(`git log --all -- 'examples/ingest-golden-*'` → tomt).
|
||||
- Men fikstursettene **finnes hos implementasjonene**, og `generated` ligger i bytene:
|
||||
fire filer hos `llm-ingestion-okf`, alle på `:8`, inne i prefikset. **Del (ii) av funnet —
|
||||
at `Golden regression` (`:280`) ville fyre på hver versjonsbump — er derfor observerbar i
|
||||
dag, ikke i framtiden.** Den utløses ved deres neste release.
|
||||
|
||||
### 6.2 Funnet under funnet: «the shared golden extractions» har ingen referent
|
||||
|
||||
Kontrollen for 6.1 avdekket noe som gjelder uavhengig av hele V1-spørsmålet, og som ingen har
|
||||
meldt:
|
||||
|
||||
| Repo | `examples/ingest-golden-*/expected-bundle/` |
|
||||
|---|---|
|
||||
| commons | finnes ikke, har aldri funnes |
|
||||
| `llm-ingestion-okf` | `ingest-orders.md`, `ingest-products.md`, `ingest-metrics.md`, `ingest-status.md` (+ 3 `index.md`) |
|
||||
| `portfolio-optimiser-claude` | `ingest-costs.md` ×2, `ingest-edge.md`, `ingest-meta.md` (+ 2 `index.md`) |
|
||||
|
||||
**Overlappet i innholdsfiler er null.** De eneste sammenfallende navnene er `index.md`, og de er
|
||||
byte-ulike (ulik oppsummering, ulike lenkemål — verifisert med `cmp`).
|
||||
|
||||
`ingest-spec.md:29` krever at en konform implementasjon reproduserer «**the shared** golden
|
||||
extractions (§11) byte for byte». Bestemt form forutsetter ett sett. Det finnes to, begge
|
||||
lovlig navngitt etter konvensjonen i `:259-260`, ingen av dem publisert her. **Konformansleddet
|
||||
er dermed ikke-testbart i dag** — ikke fordi fasiten mangler, men fordi det er to av dem, og
|
||||
hver implementasjon reproduserer sin egen per konstruksjon.
|
||||
|
||||
Dette er et **selvstendig punkt**, ikke en del av V1, og det er ikke i køen. Det er tatt med
|
||||
her fordi det avgjør hvor tungt `:29`-argumentet i §6 veier: argumentet er ikke «dette bryter
|
||||
en delt fasit vi har», men «dette sementerer at en delt fasit aldri kan oppstå».
|
||||
|
||||
#### 6.2.1 Svar fra `llm-ingestion-okf` (2026-07-26) — tre målte, én åpen
|
||||
|
||||
Fire spørsmål ble sendt. **Deres målinger er ført som DERES, ikke foldet inn i våre tall** —
|
||||
sømmene under er lest i fiksturer vi ikke har, og en dekningstelling som mater et
|
||||
konformansargument kan ikke blande målt og referert evidens.
|
||||
|
||||
**Q1 — INTENSJON: nei, settet var aldri ment som `:29`s fasit.** Målt i deres egen historikk
|
||||
(`9dd86b1`, «ship the 11 golden fixtures with byte-exact conformance»): bygget som *deres*
|
||||
verifikasjonssett mot §11s FORMAT, uten koordinering med commons noe sted i historikken.
|
||||
**Konsekvens for oss:** dette er en **misforståelse i spec-teksten, ikke en drift som skal
|
||||
lukkes**. Bestemt form — «**the shared** golden extractions» — beskriver noe som aldri ble
|
||||
opprettet, av noen. Det styrker §6.2s konklusjon og gjør den billigere å rette: `:29` beskriver
|
||||
en artefakt som ikke finnes, ikke en artefakt som har kommet i utakt.
|
||||
|
||||
**Q2 — vår dekningstelling var riktig så langt den gikk, men fire sømmer er ikke talt** (okfs
|
||||
måling, lest i fiksturene):
|
||||
|
||||
| # | Søm | Hvorfor den ikke var i vår telling |
|
||||
|---|---|---|
|
||||
| 1 | **NULL ⊥ tom celle er TO sømmer** | vi talte «tom celle» én gang; `products.csv` C-3 har `''`, `metrics.db` rad 2–3 har `NULL` (`None` fra DB-API) — ulike kodestier, ulik rendering |
|
||||
| 2 | **Kredensial-indireksjon** | `connection_ref: OKF_GOLDEN_SQL_DB` resolves ved kjøring; golden-kjøringen beviser at ingen kredensial når output. Finnes i ingen av de to settene ellers |
|
||||
| 3 | **Fler-ekstraksjon i én bundle** | file-caset har to ekstraksjoner → indeks med to oppføringer. Indeksgenerering med N>1 er en egen søm, kun i file-caset |
|
||||
| 4 | **`http` er HERMETISK** | fiksturen er et mock-payload på disk, åpner aldri en socket. Dette er en **betingelse på FORMEN til et delt sett**, ikke en case i det — brytes den, blir conformance-suiten nettverksavhengig for alle |
|
||||
|
||||
Søm 4 er den som endrer hva et delt sett *er*: den er et krav til konstruksjonen, ikke en rad i
|
||||
dekningstabellen. De presiserer at de **ikke** har gått gjennom `portfolio-optimiser-claude`s
|
||||
`ingest-costs.md`/`ingest-meta.md` og ikke påstår noe om den halvdelen av dekningen.
|
||||
|
||||
**Q3 — bekreftet, og verre enn vi skrev: stampen bærer FILNAVNET.** Vi skrev at
|
||||
`ingest_manifest` hashes av manifestets rå bytes. Verifisert her, i begge trær:
|
||||
|
||||
```python
|
||||
stamp = f"{manifest_file.stem}@{hashlib.sha256(raw).hexdigest()[:16]}"
|
||||
# v0.3.2 :191 | llm-ingestion-okf HEAD :326
|
||||
```
|
||||
|
||||
Et delt sett krever altså at commons eier og publiserer **manifestets bytes OG hva fila heter**.
|
||||
To implementasjoner som lastet ned samme manifest og lagret det som `ingest.json` vs.
|
||||
`manifest.json` ville fått ulike stamper av identiske bytes. I okfs HEAD bærer stemmen dessuten
|
||||
**per-manifest eierskap** i kollisjonsporten (§3.1 over) — et delt manifest med et annet filnavn
|
||||
ville endre hvilke filer en kjøring mener å eie.
|
||||
|
||||
**Veien rundt, som de peker på uten å anbefale:** konvergens er ikke nødvendig på HELE bundelen.
|
||||
`ingest_manifest` er én linje i frontmatteret; et delt sett kunne kreve byte-likhet på alt
|
||||
UNNTATT den linja, og da er kravet oppfyllbart uten et delt manifest — mot at `:29` ikke lenger
|
||||
kan si «byte-identisk bundle» uten forbehold. **Valget står mellom å eie manifestet og å svekke
|
||||
kravet. Ingen av dem er gratis, og det er operatørens, ikke vårt.**
|
||||
|
||||
**Q4 — ikke målt, og de svarer ikke.** Vi ba om målt kostnad, ikke overslag; de holdt den
|
||||
disiplinen begge veier. Målingen som er utestående: bytte manifestene i de tre casene til et
|
||||
hypotetisk commons-eid sett, kjøre conformance-suiten, telle hvilke expected-bundle-bytes som
|
||||
endrer seg og hvilke av de elleve sømmene som forsvinner. **Kjørbar måling, ikke en vurdering.**
|
||||
Ikke gjort fordi kvoten deres er lav og fordi den bare er verdt å gjøre hvis operatøren
|
||||
køplasserer konvergens over v0.2-arbeidet. De står klare til å ta den først neste økt hvis vi
|
||||
sier fra. **Svart tilbake: ikke bestilt ennå — køplasseringen er operatørens, og den er ikke
|
||||
tatt.**
|
||||
|
||||
## 7. Rutet hit: aktørkonvensjonen for intervju-født innhold — og hvorfor den ikke blokkerer V1
|
||||
|
||||
`llm-ingestion-okf` rutet 2026-07-26 et spørsmål fra `ms-ai-architect` hit, «fordi spec §7s
|
||||
aktørkonvensjon er deres tekst». **Spørsmålet er reelt. Rutingen hviler på en navnekollisjon,
|
||||
og den er verdt å rette før noen bygger på den.**
|
||||
|
||||
**Spørsmålet:** hva skal en produsent skrive når innholdet er *intervju-født* — et menneske
|
||||
svarer, en LLM strukturerer svarene og skriver fila, og mennesket leser ikke nødvendigvis
|
||||
resultatet? Substansen er menneskets, formuleringen er modellens. `process:`/`<produsent>/<versjon>`
|
||||
**underrapporterer** (bare et menneske kan ha visst det organisasjonsspesifikke); `human:<id>`
|
||||
**overrapporterer** (ingen har verifisert at formuleringen gjengir svaret riktig — et
|
||||
tillitssignal ingen har fortjent). Deres tre alternativer: (a) maskin-aktør ved skriving +
|
||||
separat `verified`-oppføring med `human:<id>` først når mennesket faktisk har lest; (b)
|
||||
`human:<id>` direkte; (c) en egen aktørform. Deres lesning, uttalt som lesning: (a).
|
||||
|
||||
### 7.1 To ulike §7-er — kollisjonen, målt
|
||||
|
||||
| «§7» | Dokument | Innhold |
|
||||
|---|---|---|
|
||||
| Aktørkonvensjonen med tre former | **OKF `SPEC.md` §7** (Google, oppstrøms) | `<producer>/<version>` / `human:<id>` / `process:<id>` |
|
||||
| **Vår** §7 | `ingest-spec.md` «Provenance — a separate layer» | `generated` = «**Literally `true`** — the machine-generated marker» |
|
||||
|
||||
Målt i vår frosne tekst: `grep -n "human:\|process:\|by: " ingest-spec.md method-spec.md
|
||||
CONCEPT.md` → **0 treff**. **Commons har ingen aktørkonvensjon å endre.** Vår markør er boolsk
|
||||
og aktørløs. Å be commons avgjøre aktørformen i dag er å be om en verdi vi ikke har — og å
|
||||
levere en ville vært å foregripe nettopp det V1 legger fram for operatøren.
|
||||
|
||||
### 7.2 Og et lag-skille som gjelder uansett opsjon
|
||||
|
||||
Ingest-spec §7 stempler **filer materialisert av ingest-pipelinen fra en manifest-definert
|
||||
kilde**. Intervju-født innhold er ikke ingest-materialisert — det er **kurert** innhold skrevet
|
||||
gjennom en authoring-primitiv, og vår §3 krever eksplisitt at den primitiven **avviser** det
|
||||
komplette ingest-stempelet. Vårt lag kan altså ikke stemple det uansett, og for filer vårt lag
|
||||
FAKTISK stempler er aktøren maskinell per konstruksjon — det finnes ingen intervju-født variant
|
||||
av en manifest-drevet ekstraksjon.
|
||||
|
||||
**Konsekvens for opsjonene, som er det operatøren trenger:**
|
||||
|
||||
| Opsjon | Arver vi spørsmålet? |
|
||||
|---|---|
|
||||
| **O0** | Nei — ingen aktørakse i markøren i det hele tatt |
|
||||
| **O1** | Nei for vårt lag — aktøren er alltid `llm-ingestion-okf/<versjon>` (materialiseringen skrev fila) |
|
||||
| **O2** | Nei — aktøren er en fast prosess-id uavhengig av opphav, per konstruksjon |
|
||||
| **O3** | Nei — `generated` frigjøres til oppstrøms' form, og hvem som skriver den er ikke vår tekst |
|
||||
|
||||
> **Derfor: operatøren trenger IKKE å løse dette for å løse V1.** Ingen av de fire opsjonene
|
||||
> endrer svaret, og ingen av dem blir billigere eller dyrere av det. Spørsmålet er ikke en
|
||||
> femte kostnadsrad — det er en sak i et annet lag som deler et seksjonsnummer med vårt.
|
||||
|
||||
### 7.3 Hva vi faktisk kan si, som lesning og ikke vedtak
|
||||
|
||||
Aksehygienisk er okfs (a) den eneste av de tre som ikke lyver i noen retning, og grunnen er
|
||||
den samme aksen dette repoet har ført ni ganger: (a) gjør tillitssignalet til en funksjon av en
|
||||
**handling** (noen leste det) i stedet for av en **opprinnelse** (noen sa det). `generated` ⊥
|
||||
`verified` er samme skille som ærlighetsmarkør ⊥ eierskapspredikat i §3. (c) koster nytt
|
||||
vokabular for et skille som allerede kan uttrykkes. **(a) er dessuten kompatibel med både O1 og
|
||||
O2** — den flytter tillitssignalet UT av `generated` og inn i `verified`, og rører derfor ikke
|
||||
V1s valg i noen retning.
|
||||
|
||||
Én målt ting som støtter (a)s gjennomførbarhet, gjort av okf mot guard 0.2.0 og referert som
|
||||
**deres** måling: `verified` som blokkliste går allerede gjennom persist-gaten
|
||||
(`verified:\n - human:ktg` parses som `['human:ktg']`), mens en `generated`-**mapping** ikke gjør
|
||||
det i noen form (flow feiler på `{`, blokk på nested mappings, punktnøkler på key-regexen).
|
||||
Ikke et argument for (a) i seg selv — men (a)s ene nye mekanisme møter ingen vegg der v0.2s
|
||||
`generated` møter en. **Ikke verifisert av oss; deres tre, deres måling.**
|
||||
|
||||
**Rett respondent:** aktørkonvensjonen er OKFs. Skal en fjerde form eller en (a)-konvensjon
|
||||
ratifiseres, hører den hjemme oppstrøms i `SPEC.md` §7 — ikke i ingest-spec. Meldt tilbake med
|
||||
den lesningen, uten vedtak.
|
||||
|
||||
## 8. Det som ikke er commons' å avgjøre
|
||||
|
||||
- **Hvilken opsjon som velges.** Spec-teksten er frossen; endringer går gjennom
|
||||
ratifiseringskøen. V1 er punkt nummer 8 hvis den ratifiseres — den står **utenfor** de 7 i
|
||||
`2026-07-25-amendment-underlag.md` og fortrenger ingen av dem.
|
||||
- **Hva `llm-ingestion-okf` gjør i sin egen v0.2-profil.** Den er additiv og deres authorship.
|
||||
Bare DEFAULT staterer vårt lag.
|
||||
- **Om produsent-identitet skal logges et annet sted** (O2s pris). Implementasjonens valg.
|
||||
|
||||
## 9. Det som ikke endres uansett utfall
|
||||
|
||||
Ærlighetsregelen, eierskapsstempelets funksjon, `ingested_at`-argumentet, §5s single-line- og
|
||||
line-oriented-krav, og `ingest_manifest` som stempelets andre halvdel. Ingen opsjon rører
|
||||
`method-spec.md` — nøkkelen forekommer ikke der (`grep -c '\`generated' method-spec.md` → 0).
|
||||
|
||||
---
|
||||
|
||||
## Verifiseringslogg
|
||||
|
||||
| Påstand | Sjekk | Resultat |
|
||||
|---|---|---|
|
||||
| v0.2 definerer `generated: { by, at }` | `okf/SPEC.md` @ `main`, hentet 2026-07-26 | «generated: { by: reference_agent/gemini-2.5-pro, at: 2026-06-20T22:53:05Z }» |
|
||||
| `by` er påkrevd | samme | «generated.by: REQUIRED within `generated`. An actor (§7).» |
|
||||
| Aktørkonvensjonen har tre former | samme, §7 | `<producer>/<version>` / `human:<id>` / `process:<id>` |
|
||||
| `timestamp` er superseded | samme | «`timestamp` is superseded by `generated.at`» |
|
||||
| Toleransen gjelder ukjente, ikke feiltypede | samme | «MUST NOT reject documents with unrecognized fields»; lista dekker manglende valgfrie + ukjente |
|
||||
| Oppstrøms er lest på en gren, ikke en tag | `llm-ingestion-okf/docs/plan/okf-v0.2-alignment.md` | «`main` on 2026-07-26 — a branch, not a tag» |
|
||||
| Kontraktsreferanser til nøkkelen | `grep -n '\`generated' ingest-spec.md` | 7 linjer: `:34 :70 :82 :152 :214 :275 :309` |
|
||||
| Nøkkelen finnes ikke i method-spec | `grep -c '\`generated' method-spec.md` | 0 |
|
||||
| Ærlighetsregelen er unwaivable | `ingest-spec.md:33-34`, `method-spec.md:26` | «unwaivable» begge steder |
|
||||
| Stempelet er `generated: true` + `ingest_manifest` | `ingest-spec.md:70`, `:82` | ordrett sitert over |
|
||||
| Dør C avviser kun det komplette stempelet | `ingest-spec.md:82-85` | «while permitting either field alone» |
|
||||
| `generated` ligger i det ordnede prefikset | `ingest-spec.md:149-152` | 7 nøkler, `generated` er den sjuende |
|
||||
| Prefikset er åpent bakover | `ingest-spec.md:153` | «additional frontmatter keys MAY follow it» |
|
||||
| Single-line-kravet holder for flow mapping | `ingest-spec.md:150`, `:158` | «line-oriented `key: value`» / «All values MUST be single-line» |
|
||||
| Konformans krever byte-lik delt fasit | `ingest-spec.md:29` | «reproduce the shared golden extractions (§11) byte for byte» |
|
||||
| Fasiten sammenlignes byte for byte | `ingest-spec.md:267`, `:280` | «byte for byte» / «any byte … diverges» |
|
||||
| Ingen ingest-fasit finnes i commons | `ls -d examples/ingest-golden-*` | ingen treff |
|
||||
| …og har aldri funnes | `git log --all -- 'examples/ingest-golden-*'` | tomt |
|
||||
| `generated` ligger i fasit-bytene hos okf | `grep -rn '^generated' llm-ingestion-okf/examples/ingest-golden-*/expected-bundle/*.md` | 4 filer, alle `:8` |
|
||||
| …inne i det ordnede prefikset | `sed -n '1,9p' .../ingest-orders.md` | 7 nøkler `:2-8`, `generated` sist |
|
||||
| Predikatet nøkler på literalen i dag | `materialize.py:131-150` **@ `llm-ingestion-okf` HEAD** (ref tilføyd 07-27 — raden var ref-løs) | `frontmatter.get("generated") != "true"` + stem-match |
|
||||
| To disjunkte fasitsett finnes | `comm -12` over begge trærs `expected-bundle/*.md` | kun `index.md`-navn felles |
|
||||
| …og de felles `index.md` er ulike | `cmp` på `ingest-golden-file/.../index.md` | ULIKE |
|
||||
| §1 sier «**the shared** golden extractions» | `ingest-spec.md:29` | bestemt form, én mengde forutsatt |
|
||||
| Oppstrøms bruker selv `<produsent>/<versjon>` | `okf/bundles/acme_retail/metrics/gross-margin.md` | `by: reference_agent/gemini-2.5-pro` |
|
||||
| Oppstrøms skriver `generated` på HÅNDSKREVET fil | `…/gross-margin-legacy.md` | `by: human:jsmith@acme` |
|
||||
| `sources` er blokkliste, ikke inline flow | samme fil, `sources:` | `- id:` + 4 nøkler, flerlinjes → bryter `:158` |
|
||||
| Konsument-treet er pinnet for §5.1 | `git -C ~/repos/portfolio-optimiser-claude log -1` | `8a14137` (2026-07-26) — ikke `main` |
|
||||
| 4 fasit-blober bærer `generated: true` | `grep -rn generated examples/ingest-golden-*/expected-bundle/*.md` @ `8a14137` | 4 filer, alle `:8` |
|
||||
| Fasiten er byte-sammenligning per fil | `tests/test_ingest_golden.py:36` | `read_bytes() == read_bytes()` |
|
||||
| 2 verbatim verdi-assert | `tests/test_ingest_loadbearing.py:59`, `tests/test_ingest_sql_loadbearing.py:84` | `frontmatter["generated"] == "true"` |
|
||||
| 3 navn/orden-sider, ikke 4 | `_PROVENANCE_KEYS` `:32` + `:34` (sql) + ordens-lista `:67-75` | sql-fila har INGEN ordens-test |
|
||||
| Ordens-testen overlever mapping-form | `tests/test_ingest_loadbearing.py:67` | `ln.split(":", 1)[0]` → `generated` uansett verdi |
|
||||
| `:49` er delstreng over spec-tekst | `tests/test_ingest_spec_loadbearing.py:68-71` | `assert field in text` — ikke frontmatter, kan ikke bli rød |
|
||||
| …og «machine-generated» metter den | `grep -n generated ingest-spec.md` | `:33`, `:214` m.fl. — prosa alene holder |
|
||||
| Deres `:186` er vår `:214` | `sed -n '186p' …/shared/ingest-spec.md` | «Literally `true`» — samme rad, én commit bak |
|
||||
| Vårt kø-punkt 8 er ikke deres | `2026-07-25-amendment-underlag.md` §9 | rad 8 = B1/D4 nav-golden; V1 står ikke i køen |
|
||||
| SPEC.md er endret ETTER v0.2-migreringen | okf-melding, deres pin `3fcbb9f` | «v0.2-commiten» ≠ «dagens spec-tekst» — uverifisert av oss |
|
||||
| `ingested_at` har ingen wall-clock-default | `ingest-spec.md:141` | «there is NO wall-clock default» |
|
||||
| Spec-tekst uendret siden 07-21 | `git log --oneline -- ingest-spec.md` | `bfa5a9b`, forrige `7aa53fc` — ingen commit etter |
|
||||
|
||||
**Tilført 2026-07-27** (innboksrunden — fire meldinger, alle premiss målt mot kilden):
|
||||
|
||||
| Påstand | Sjekk | Resultat |
|
||||
|---|---|---|
|
||||
| v0.3.2s eierskapsport er en KONJUNKSJON | `materialize.py:86-89` @ `portfolio-optimiser-claude/.venv` (0.3.2) | `generated == "true" AND "ingest_manifest" in frontmatter` — deres kodekommentar sier «AND» eksplisitt |
|
||||
| …og det er konformt med frossen tekst | `ingest-spec.md` §3 | «The check is on the complete stamp, never on the individual field names» |
|
||||
| Emitter og port kobles av literalen `"true"` | `materialize.py:103` (emitter) vs. `:89` (port) | samme strengliteral begge steder |
|
||||
| Porten er GLOBAL i v0.3.2 | `grep -n _is_ingest_owned` @ 0.3.2 (289 linjer) | def `:84`, ENESTE kall `:258`, signatur `(path)` |
|
||||
| Porten er PER MANIFEST i okfs HEAD | samme grep @ `~/repos/llm-ingestion-okf` (424 linjer) | def `:132`, kall `:393`, signatur `(path, manifest_stem, *, profile)` |
|
||||
| Stampen bærer FILNAVNET (okfs Q3) | `stamp = f"{manifest_file.stem}@{sha256(raw)[:16]}"` | v0.3.2 `:191`; okf HEAD `:326` — bekreftet i BEGGE trær |
|
||||
| okfs egne `:251`/`:318` peker i intet av de to trærne | `wc -l` + `grep -n` begge trær | substansen bekreftet, linjenumrene ikke — ref ikke oppgitt i meldingen |
|
||||
| Commons har INGEN aktørkonvensjon | `grep -n "human:\|process:\|by: " ingest-spec.md method-spec.md CONCEPT.md` | **0 treff** |
|
||||
| Vår §7 definerer `generated` som boolsk | `ingest-spec.md` §7, felt-tabellen | «Literally `true` — the machine-generated marker (§1 honesty rule)» |
|
||||
| De tre aktørformene er OKFs §7, ikke vår | V1 §2 + `okf/SPEC.md` §7 | navnekollisjon mellom to ulike §7-er |
|
||||
| Begge konsumenter står på `7aa53fc` | `diff <(git show 7aa53fc:ingest-spec.md) ~/repos/<konsument>/shared/ingest-spec.md` | **IDENTISK** for `portfolio-optimiser` OG `portfolio-optimiser-claude` |
|
||||
| …altså ÉN commit bak, ikke to | `git rev-list --count 7aa53fc..HEAD -- ingest-spec.md` | **1** (`bfa5a9b`) |
|
||||
| MCP-ankeret finnes i den pinnede kopien | `git show 7aa53fc:ingest-spec.md \| grep -n "An MCP-based"` | `:101` — S2.2/S2.4-ugatingen henger ikke på en pull |
|
||||
| `:112-114` hos konsumenten er IKKE tomt | `git show 7aa53fc:ingest-spec.md \| sed -n '112,114p'` | felt-tabellen: `id` / `title` / `query` (`okf_type`/`max_rows` er `:115-116`) |
|
||||
|
||||
**Tilført 2026-07-31 (økt 4 — pin, id, sitering, ratifiseringsgaten):**
|
||||
|
||||
| Påstand | Sjekk | Resultat |
|
||||
|---|---|---|
|
||||
| Frontmatter parses som STRENGER, ikke YAML | `method-spec.md:90` | «parsed line-oriented as `key: value` **strings**» — derav er sitering datainnhold, ikke syntaks |
|
||||
| …og har vært frossen hele veien | `git log -1 -S 'parsed line-oriented as' -- method-spec.md` | `7d2b46c` (2026-07-03) — eldste spec-commit |
|
||||
| Prosaen er framework-nøytral ved regel | `ingest-spec.md:8-9` | «never names a concrete agent toolkit or vendor stack, and a guard test keeps it that way» |
|
||||
| …håndhevet som §11-seam | `ingest-spec.md:282` | «Spec integrity \| this spec … names a concrete agent toolkit …» |
|
||||
| …men `:282` treffer ikke okf ordrett | lesning: okf er ingest-impl., ikke «agent toolkit» | **utelukkelsen hviler på `:7-8`**, ikke `:282` — ført som forbehold, ikke som treff |
|
||||
| Commons har fortsatt INGEN aktørkonvensjon | `grep -n "human:\|process:\|by: " ingest-spec.md method-spec.md CONCEPT.md \| wc -l` | **0** (uendret fra 07-27) |
|
||||
| Spec-en kaller laget «ingest», prosessen «materialization» | `grep -io 'materializ[a-z]*' ingest-spec.md \| wc -l` + `:1`, `:18`, `:28` | 27 forekomster; tittel + §1 bruker «Ingest» som lagets navn → `okf-ingest` |
|
||||
| `ingest-spec.md` er URØRT | `git log --oneline -2 -- ingest-spec.md` | `bfa5a9b`, forrige `7aa53fc` — ingen commit i økt 3 eller 4 |
|
||||
| De 5 linjene er urørte — men bærer IKKE samme form | `grep -n '\`generated' ingest-spec.md` | **4 av 5** bærer `generated: true` ordrett (`:34`, `:70`, `:82`, `:275`). `:214` er §7-feltradstabellens rad («\`generated\` … Literally \`true\` — the machine-generated marker») og bærer ikke literalen. Se raden under |
|
||||
| …og `:214` er derfor IKKE en strengerstatning | O2-formen (`generated: { by: …, at: … }`) vs. radens «Literally `true`» | raden **beskriver feltets verdi**. Under O2 er verdien et objekt med `by`/`at`, så raden må skrives om (evt. splittes), ikke søk-og-erstattes. **Egen redigering, samme amendment** |
|
||||
| Frossen tekst krever RATIFISERING, ikke bare pin | `2026-07-25-amendment-underlag.md:495-496` | «operatøren ratifiserer, og frossen tekst endres ikke uten den ratifiseringen» |
|
||||
| V1 står ikke i køen | samme fil §9, rad 8 | rad 8 = **B1/D4**, ikke V1 — uendret fra 07-27 |
|
||||
| B1 rørte ingen normativ fil | `git show 8a7d430 --name-only` | `README.md` + 2 planfiler; **0** normative filer |
|
||||
| Pinnen er en pin, ikke en ref | okf-melding 07-31, `2504011` | sagt eksplisitt som pin; skillet fra `6f42c10`/`ed08ac1` holdt |
|
||||
| Deres v0.2-fikstur bar O2-formen allerede | okfs måling, `c90171d` (07-27) | **ført som DERES**, ikke reprodusert her |
|
||||
|
||||
**Korreksjon 2026-07-31 (økt 6) — «de 5 linjene» er ikke homogene.** Raden «De 5 linjene bærer
|
||||
fortsatt literalen» påsto at alle fem bar `generated: true` ordrett. Det er feil, og planen
|
||||
motsa seg selv: §5.1 fører `:214` korrekt opp som «Literally `true`»-raden, og
|
||||
verifiseringstabellens egen `grep -n '\`generated'`-rad lister `:214` blant de 7 uten å skille
|
||||
form. Målt nå: `grep -n 'generated: true' ingest-spec.md` gir **4** treff (`:34`, `:70`, `:82`,
|
||||
`:275`) — ikke 5.
|
||||
|
||||
Feilen var arvet ordrett inn i `STATE.md`s NESTE-blokk («skriv om de 5 kontraktslinjene … fra
|
||||
literal `generated: true`»). Konsekvensen er ikke kosmetisk: en økt som utfører V1 mekanisk
|
||||
etter den formuleringen finner 4 av 5 treff og står igjen med to like sannsynlige feiltolkninger
|
||||
— (a) drift i frossen tekst, eller (b) `:214` hoppes over, som etterlater **ærlighetsmarkørens
|
||||
§7-halvdel** (§3s ærlighetsmarkør-rad: «§1 `:34`, §7 `:214`») ukonvertert mens §1-halvdelen er
|
||||
O2. Tellingen «5 av 7» (§4.1, §5) står uendret — det var formen, ikke antallet, som var feil ført.
|
||||
|
||||
*Ingen normativ fil rørt av denne korreksjonen; `ingest-spec.md` står fortsatt på `bfa5a9b`.*
|
||||
93
shared/docs/plan/2026-07-31-okf-versjonsakse-sjekk.md
Normal file
93
shared/docs/plan/2026-07-31-okf-versjonsakse-sjekk.md
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
# Google OKF v0.2 — sjekken er utført, og hypotesen holdt ikke
|
||||
|
||||
**Dato:** 2026-07-31 (økt 5) · **Status:** LUKKET, ingen melding sendt · **Marker:** `okf-second-brain-convention`
|
||||
|
||||
STATE bar siden 07-27 en uverifisert observasjon som NESTE STEG: *«catalog bumpet 0.1→0.2 for å
|
||||
ikke være forvekslbar med Google OKF v0.1 — er Google nå på 0.2, kan avklaringen ha kollapset.»*
|
||||
Sjekken er nå gjort. **Avklaringen har ikke kollapset, og spørsmålet var feilstilt.** Tre av
|
||||
premissene i formuleringen viste seg å avvike fra ground truth.
|
||||
|
||||
## 1. Google er på v0.2 — men det visste vi allerede
|
||||
|
||||
Verifisert mot primærkilde: Google Cloud Blog, *«Open Knowledge format v0.2 tackles agentic
|
||||
trust»*, publisert **2026-07-25**.
|
||||
|
||||
Men søket var strengt tatt overflødig. Svaret lå i vår egen arkiverte innboks, fem dager gammelt:
|
||||
`llm-ingestion-okf`, `20260726T114345Z`, første linje i brødteksten — ordrett **«OKF v0.2 er ute
|
||||
(2026-07-25).»** Hele V1-sporet er *bygget på* v0.2 (`generated`-feltets form etter v0.2; se
|
||||
`2026-07-26-v1-generated-felt-okf-v0.2.md`, og filnavnet sier det selv).
|
||||
|
||||
**Dette er en STATE-defekt, ikke et funn.** Observasjonen ble ført som «uverifisert» i fire økter
|
||||
mens den samtidig var bærende premiss for arbeidet i nabosporet. Premiss-verifiseringsregelen ble
|
||||
anvendt på output, ikke på STATEs egen påstandsliste. Billigste sjekk som fantes var `grep` i eget
|
||||
arkiv — ikke WebSearch.
|
||||
|
||||
## 2. Catalog er ikke på 0.2. De er på 0.3.
|
||||
|
||||
| Påstand i STATE | Ground truth |
|
||||
|---|---|
|
||||
| catalog er på 0.2 | **0.3** — `1ca27f6`, 2026-07-31 (i dag) |
|
||||
| bumpet skjedde «for å ikke være forvekslbar» | primærgrunnen var **§3-gulvet** |
|
||||
|
||||
`6a72b26` (2026-07-25), commit-subjekt ordrett: `feat(okf): enforce §3 okf_version shape, bump
|
||||
convention 0.1 -> 0.2`. Og specens egen header, `spec.md:12-14`:
|
||||
|
||||
> 0.2 had tightened the §3 floor: `okf_version` enforced on shape. Distinct from — and
|
||||
> deliberately no longer numerically confusable with — upstream Google OKF v0.1, which this
|
||||
> convention targets and does not version.
|
||||
|
||||
Ikke-forvekslbarheten er ført som **bevisst sidegevinst**, ikke som årsak. STATE byttet om primær
|
||||
og sekundær og bygget et neste steg på den omvendingen. (Jf. driftsmodellen: *før en rad ikke
|
||||
sterkere enn den bærer* — her førte vi vår egen rad for sterkt.)
|
||||
|
||||
## 3. Hvorfor avklaringen ikke kan kollapse: det er to akser
|
||||
|
||||
Dette er **akse-forveksling nr. 13**, og denne gangen var det vår.
|
||||
|
||||
- **Akse A — catalogs konvensjonsversjon:** 0.1 → 0.2 → 0.3. Beskriver catalogs *eget* dokument.
|
||||
- **Akse B — `okf_version`-verdien:** hvilken upstream Google-versjon en bundle targeter.
|
||||
|
||||
Catalog sier eksplisitt at konvensjonen *«targets and does not version»* upstream (`spec.md:13-14`),
|
||||
og i §12 (`:245-246`): *«Its value set is owned by Google.»* Gaten er tilsvarende renset for
|
||||
akse-lekkasje (`:70-72`): den *«asserts **nothing** about which upstream versions exist ... a bundle
|
||||
targeting a newer upstream version passes.»*
|
||||
|
||||
At Google flyttet seg på akse B kan derfor ikke kollapse en avklaring som lever på akse A.
|
||||
Tallsammenfallet som hypotesen fryktet inntreffer uansett ikke: catalog 0.3 vs. Google 0.2.
|
||||
|
||||
## 4. Det ene som faktisk står igjen — og det er ikke vårt
|
||||
|
||||
`spec.md` sier to steder at upstream-versjonen bundelen targeter er «currently `0.1`» (`:63`,
|
||||
`:246`), mens `:67` i samme dokument siterer upstreams kanoniske eksempel `okf_version: "0.2"`
|
||||
(`okf/SPEC.md:773`).
|
||||
|
||||
Det er **ikke en defekt**, og skal ikke meldes som en. Catalog har foregrepet situasjonen i egen
|
||||
tekst, §12 `:246-247`: *«When Google bumps OKF, each plugin re-checks conformance.»* Google har nå
|
||||
bumpet. Re-sjekken er dermed utløst — men den er **catalogs å utløse, på catalogs akse**, og
|
||||
plugin-eiernes å utføre. Vi er ikke respondent.
|
||||
|
||||
Per driftsmodellen: navngi aksen, pek på rett respondent, ikke lever en verdi vi ikke eier.
|
||||
|
||||
## Konklusjon
|
||||
|
||||
- Sjekken STATE hjemlet: **utført**. Hypotesen: **falsifisert**.
|
||||
- **Ingen melding skal sendes** på det opprinnelige grunnlaget — grunnlaget fantes ikke.
|
||||
- Det som *kan* sendes er noe annet og mindre: en `--fyi` til `catalog` om at Google er på 0.2 og
|
||||
at deres egen §12-re-sjekk dermed er utløst. **Fortsatt gated på operatør-go**, og lavt prioritert
|
||||
— catalog eier både aksen og triggeren, og `1ca27f6` (i dag) viser at de følger upstream tett.
|
||||
- Sporet `okf-second-brain-convention` er dermed **lukket fra vår side**.
|
||||
|
||||
## Verifiseringslogg
|
||||
|
||||
| Påstand | Kilde |
|
||||
|---|---|
|
||||
| Google OKF v0.2, publisert 2026-07-25 | Google Cloud Blog, `okf-v0-2-adds-trust-signals` (WebFetch) |
|
||||
| v0.2 var kjent for oss 2026-07-26 | `coord/.../archive/20260726T114345Z-3155211798-from-llm-ingestion-okf.md` |
|
||||
| catalog er på 0.3 per 2026-07-31 | `catalog@1ca27f6`; `spec.md:7` |
|
||||
| 0.2-bumpens primærgrunn = §3-gulvet | `catalog@6a72b26` commit-subjekt; `spec.md:12` |
|
||||
| konvensjonen versjonerer ikke upstream | `spec.md:13-14`, `:245-246` |
|
||||
| gaten godtar nyere upstream-versjon | `spec.md:70-72` |
|
||||
| re-sjekk-plikten er plugin-eiernes | `spec.md:246-247` |
|
||||
|
||||
Catalog-ankrene er lest read-only i `~/repos/ktg-plugin-marketplace/catalog` @ `1ca27f6`. Ingenting
|
||||
skrevet i det repoet.
|
||||
27
shared/examples/nav-golden-escape/README.md
Normal file
27
shared/examples/nav-golden-escape/README.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# nav-golden-escape — negative navigation golden (the gate must be able to go red)
|
||||
|
||||
Per catalog's requirement that step 0's gate must be able to **fail** (a gate that can
|
||||
only pass proves nothing), this negative case makes a conformant navigator and a naive one
|
||||
**provably diverge**. It is the counterpart to `nav-golden-hierarchy` (the positive case).
|
||||
|
||||
`bundle/index.md` links one valid sibling and three escaping / trap targets:
|
||||
|
||||
| Link | Class | Conformant outcome |
|
||||
|---|---|---|
|
||||
| `valid.md` | in-bundle | returned |
|
||||
| `../SHOULD-NOT-BE-READ.md` | `..` escape (the decoy really exists, one level up) | skipped; decoy never read |
|
||||
| `../../../../etc/passwd` | deep `..` escape | skipped |
|
||||
| `/etc/passwd` | root-relative **trap** | skipped |
|
||||
|
||||
The trap is the load-bearing one. Under the ratified rule, a leading `/` denotes the
|
||||
**bundle root**, so `/etc/passwd` resolves to `{bundle}/etc/passwd` (no such file → skip),
|
||||
NOT the filesystem path. A naive implementation that reads `/` as filesystem-absolute opens
|
||||
the real `/etc/passwd` — a path-traversal breach. This case fails for any implementation
|
||||
that raises, reads the decoy, or leaks `/etc/passwd` content; it passes only when the
|
||||
read-context is exactly `valid.md`.
|
||||
|
||||
The malformed-target sub-class (an embedded null byte, other invalid path components) is
|
||||
covered by the method-spec §11 **Navigation boundary** seam as a unit test — a literal null
|
||||
byte does not belong in a committed text fixture. (See the relocated finding
|
||||
`docs/plan/2026-07-20-funn-portfolio-optimiser-nullbyte.local.md` for the concrete
|
||||
null-byte reproduction that motivates the seam.)
|
||||
6
shared/examples/nav-golden-escape/SHOULD-NOT-BE-READ.md
Normal file
6
shared/examples/nav-golden-escape/SHOULD-NOT-BE-READ.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
type: project
|
||||
title: "Decoy — MUST NOT be read"
|
||||
---
|
||||
|
||||
This file sits ONE level above `bundle/`, outside the bundle. If its text ever appears in a read-context, the navigator resolved a `..` escape and breached the bundle boundary. It MUST never be reached.
|
||||
12
shared/examples/nav-golden-escape/bundle/index.md
Normal file
12
shared/examples/nav-golden-escape/bundle/index.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
type: index
|
||||
okf_version: 0.1
|
||||
title: "Nav-golden — escape / boundary case"
|
||||
---
|
||||
|
||||
Nav-golden escape case. Every link but the first escapes the bundle or is a root-relative trap; a conformant navigator skips them all, reads none of them, raises nothing, and still returns the one valid sibling.
|
||||
|
||||
- [Valid sibling](valid.md)
|
||||
- [Parent escape](../SHOULD-NOT-BE-READ.md)
|
||||
- [Deep escape](../../../../etc/passwd)
|
||||
- [Root-relative trap](/etc/passwd)
|
||||
6
shared/examples/nav-golden-escape/bundle/valid.md
Normal file
6
shared/examples/nav-golden-escape/bundle/valid.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
type: project
|
||||
title: "Valid sibling"
|
||||
---
|
||||
|
||||
The only in-bundle target. A conformant navigator returns exactly this file.
|
||||
10
shared/examples/nav-golden-escape/expected-read-context.md
Normal file
10
shared/examples/nav-golden-escape/expected-read-context.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
Nav-golden escape case. Every link but the first escapes the bundle or is a root-relative trap; a conformant navigator skips them all, reads none of them, raises nothing, and still returns the one valid sibling.
|
||||
|
||||
- [Valid sibling](valid.md)
|
||||
- [Parent escape](../SHOULD-NOT-BE-READ.md)
|
||||
- [Deep escape](../../../../etc/passwd)
|
||||
- [Root-relative trap](/etc/passwd)
|
||||
|
||||
## project: Valid sibling
|
||||
|
||||
The only in-bundle target. A conformant navigator returns exactly this file.
|
||||
34
shared/examples/nav-golden-hierarchy/README.md
Normal file
34
shared/examples/nav-golden-hierarchy/README.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# nav-golden-hierarchy — hierarchical navigation golden
|
||||
|
||||
Fixture class **nav-golden** (commons-owned, per the D4 step-0 split): `bundle/` in,
|
||||
`expected-read-context.md` out. Unlike the ingest-golden class (ingest-spec §11,
|
||||
extraction-shaped: `manifest.json` → `expected-bundle/`), a nav-golden case is
|
||||
**bundle → read-context** — the only shape that can express the load-bearing property
|
||||
"two conformant implementations MUST produce an identical read-context from the same
|
||||
bundle" (method-spec §3 Step 1).
|
||||
|
||||
This **positive** case exercises every decision the Q3 navigation contract pins:
|
||||
|
||||
| Decision (method-spec §3 Step 1) | How this bundle exercises it |
|
||||
|---|---|
|
||||
| Hierarchy allowed; escape, not depth, forbidden | `a/`, `a/b/` are navigated; nothing escapes |
|
||||
| Link syntax: leading `/` = bundle root, else relative | `index.md`: relative `overview.md` + root-relative `/a/index.md`; `a/index.md`: relative `b/index.md` + root-relative `/overview.md` |
|
||||
| Depth-first, first-seen order | render order is `overview` → `doc-a` → `doc-b` |
|
||||
| Dedup on resolved path | `/overview.md` (from `a/`) resolves to the already-seen `overview.md` → one entry |
|
||||
| Cycle termination | `a/b/index.md` links back to `/a/index.md` (already seen) → stops |
|
||||
| One segment per level | each index links only its immediate children |
|
||||
| Missing `index.md` binds the root alone | `c/` has no `index.md` and nothing links `c/orphan.md`; it is simply unreachable, not an error |
|
||||
| Verdict exclusion = type check per reached file, recursive | `a/verdict-nested.md` is reached but excluded |
|
||||
| Flat render regardless of depth | nested concepts render as the same `## {type}: {title}` sections; no level heading; nested index bodies are navigation, not content, and are not rendered |
|
||||
|
||||
**Traversal trace:** `index.md` (its body is the leading summary) → `overview.md` →
|
||||
`/a/index.md` → `a/doc-a.md` → `a/verdict-nested.md` (reached, excluded) → `a/b/index.md`
|
||||
→ `a/b/doc-b.md` → `/a/index.md` (seen, deduped) → `/overview.md` (seen, deduped).
|
||||
Unreachable: `c/orphan.md`.
|
||||
|
||||
**Serialization** the gate compares against: the root index body verbatim, then each
|
||||
**non-index** concept as `## {type}: {title}` + blank line + the file body, sections
|
||||
separated by one blank line, file ending in exactly one trailing newline. The spec does
|
||||
not pin whitespace beyond this shape; a gate MAY compare byte-exact or after
|
||||
trailing-whitespace normalization. **Only the root index body is the summary** — nested
|
||||
index bodies (`a/index.md`, `a/b/index.md`) are navigation and do not appear.
|
||||
6
shared/examples/nav-golden-hierarchy/bundle/a/b/doc-b.md
Normal file
6
shared/examples/nav-golden-hierarchy/bundle/a/b/doc-b.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
type: reference
|
||||
title: "Doc B"
|
||||
---
|
||||
|
||||
Level-2 concept reached depth-first, deepest in the traversal.
|
||||
8
shared/examples/nav-golden-hierarchy/bundle/a/b/index.md
Normal file
8
shared/examples/nav-golden-hierarchy/bundle/a/b/index.md
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
type: index
|
||||
okf_version: 0.1
|
||||
title: "Subsection B index"
|
||||
---
|
||||
|
||||
- [Doc B](doc-b.md)
|
||||
- [Back to A](/a/index.md)
|
||||
6
shared/examples/nav-golden-hierarchy/bundle/a/doc-a.md
Normal file
6
shared/examples/nav-golden-hierarchy/bundle/a/doc-a.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
type: methodology
|
||||
title: "Doc A"
|
||||
---
|
||||
|
||||
Level-1 concept reached depth-first after Overview.
|
||||
10
shared/examples/nav-golden-hierarchy/bundle/a/index.md
Normal file
10
shared/examples/nav-golden-hierarchy/bundle/a/index.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
type: index
|
||||
okf_version: 0.1
|
||||
title: "Section A index"
|
||||
---
|
||||
|
||||
- [Doc A](doc-a.md)
|
||||
- [Nested verdict](verdict-nested.md)
|
||||
- [Subsection B](b/index.md)
|
||||
- [Overview again](/overview.md)
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
type: verdict
|
||||
title: "Nested verdict — must be excluded"
|
||||
decision: approved
|
||||
description: "A verdict file at a nested level. A conformant navigator reaches it (dedup marks it seen) but MUST exclude it from the read-context — recursively, not only at the root. This is the leakage surface a graph-based exclusion would miss."
|
||||
---
|
||||
|
||||
This body MUST NOT appear in the read-context.
|
||||
6
shared/examples/nav-golden-hierarchy/bundle/c/orphan.md
Normal file
6
shared/examples/nav-golden-hierarchy/bundle/c/orphan.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
type: project
|
||||
title: "Orphan — unreachable"
|
||||
---
|
||||
|
||||
Directory `c/` has no `index.md`, and nothing links to this file. A link-following navigator never reaches it; a directory-walking one wrongly would. It MUST be absent from the read-context, and its parent directory's missing `index.md` MUST NOT be an error (that rule binds the bundle root alone).
|
||||
10
shared/examples/nav-golden-hierarchy/bundle/index.md
Normal file
10
shared/examples/nav-golden-hierarchy/bundle/index.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
type: index
|
||||
okf_version: 0.1
|
||||
title: "Nav-golden — hierarchical navigation case"
|
||||
---
|
||||
|
||||
Nav-golden hierarchical case. This bundle exercises depth-first traversal, both link forms (root-relative and relative), recursive verdict exclusion, resolved-path de-duplication, cycle termination, and the root-only binding of the missing-index rule.
|
||||
|
||||
- [Overview](overview.md)
|
||||
- [Section A](/a/index.md)
|
||||
6
shared/examples/nav-golden-hierarchy/bundle/overview.md
Normal file
6
shared/examples/nav-golden-hierarchy/bundle/overview.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
type: project
|
||||
title: "Overview"
|
||||
---
|
||||
|
||||
Root-level concept reached by a relative link.
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
Nav-golden hierarchical case. This bundle exercises depth-first traversal, both link forms (root-relative and relative), recursive verdict exclusion, resolved-path de-duplication, cycle termination, and the root-only binding of the missing-index rule.
|
||||
|
||||
- [Overview](overview.md)
|
||||
- [Section A](/a/index.md)
|
||||
|
||||
## project: Overview
|
||||
|
||||
Root-level concept reached by a relative link.
|
||||
|
||||
## methodology: Doc A
|
||||
|
||||
Level-1 concept reached depth-first after Overview.
|
||||
|
||||
## reference: Doc B
|
||||
|
||||
Level-2 concept reached depth-first, deepest in the traversal.
|
||||
|
|
@ -70,7 +70,19 @@ approved knowledge into. Two rules keep ingest and the learning loop apart:
|
|||
the ingest stamp (`generated: true` plus an `ingest_manifest` reference, §7) and MUST NOT
|
||||
touch curated or promoted files. If a generated filename collides with an existing file that
|
||||
does NOT carry the stamp, materialization MUST fail — never overwrite curated content.
|
||||
Index updating is idempotent and preserves curated links (§6).
|
||||
Index updating is idempotent and preserves curated links (§6). The stamp is unforgeable
|
||||
against **accident**, not against **will**: an operator who hand-copies a generated file —
|
||||
stamp and all — into curated content makes it indistinguishable from ingest-owned content,
|
||||
and a later re-materialization may remove it. This residual risk is **documented, not
|
||||
prevented**; the boundary's honesty rests on curated authoring not forging the stamp (next
|
||||
bullet) and on operators not hand-copying generated files.
|
||||
- **No other writer may forge the stamp.** The stamp is the sole mark distinguishing
|
||||
ingest-owned files from curated ones, so any authoring primitive that materializes a concept
|
||||
file from **caller-supplied** frontmatter MUST reject a frontmatter carrying the *complete*
|
||||
ownership stamp — `generated: true` together with an `ingest_manifest` reference — while
|
||||
permitting either field alone (curated content may legitimately carry a single provenance
|
||||
field). The check is on the complete stamp, never on the individual field names, so a
|
||||
legitimate verbatim round-trip is preserved; it is a **validation, never a repair**.
|
||||
|
||||
## 4. The ingest manifest (the contract)
|
||||
|
||||
|
|
@ -110,7 +122,7 @@ environment. A manifest is therefore versionable and shareable without secrets.
|
|||
| Field | Meaning |
|
||||
|---|---|
|
||||
| `id` | Unique within the manifest, matching `[a-z0-9][a-z0-9-]*`; names the generated file (§5). |
|
||||
| `title` | Human-readable title; becomes the `title` frontmatter and the index link label. Single-line. |
|
||||
| `title` | Human-readable title; becomes the `title` frontmatter and the index link label. Single-line, and MUST NOT contain `[` or `]` (they would break index-link and navigation parsing, method spec §3 Step 1); `(`/`)` remain permitted. Validated fail-fast at manifest load — rendered verbatim thereafter (§5, §6). |
|
||||
| `query` | The extraction query, polymorphic on the source type (below). |
|
||||
| `okf_type` | The generated file's OKF `type`. MUST NOT be `verdict` (case-insensitive; §3). |
|
||||
| `max_rows` | Required positive integer cap on the extraction size (§8). |
|
||||
|
|
@ -134,11 +146,19 @@ Materialization takes three EXPLICIT inputs: the manifest, the target bundle dir
|
|||
grammar in §4 makes the name path-safe; the `ingest-` prefix keeps the namespace disjoint
|
||||
from `index.md` and `promoted-verdict-*`). Splitting one extraction into per-row files is
|
||||
an extension point, not version 1.
|
||||
- **Frontmatter** — exactly these keys, in exactly this order (OKF line-oriented
|
||||
`key: value`; §7 defines the provenance semantics):
|
||||
- **Frontmatter** — these seven keys, in this order, form a **mandatory ordered prefix** on a
|
||||
stamped file (OKF line-oriented `key: value`; §7 defines the provenance semantics):
|
||||
`type`, `title`, `source_system`, `source_query`, `ingested_at`, `ingest_manifest`,
|
||||
`generated`. All values MUST be single-line; the materializer MUST collapse whitespace runs
|
||||
(including newlines) in `source_query` to single spaces.
|
||||
`generated`. The version-1 materializer emits exactly this prefix and nothing after it, but
|
||||
the CONTRACT is the prefix, not a closed set: additional frontmatter keys MAY follow it,
|
||||
unconstrained in presence and order (OKF consumers preserve unknown fields, §7), so a
|
||||
stamped file stays within the general OKF concept-frontmatter contract. Conformance to the
|
||||
prefix is a **per-file** property — a non-conforming file is an individual failure and never
|
||||
invalidates the rest of the bundle; the bundle carries no whole-bundle completeness
|
||||
property. All values MUST be single-line; `title` is emitted **verbatim** (it is `[`/`]`-free
|
||||
by §4, so verbatim rendering is safe — the invariant is met by validation, not repair); the
|
||||
materializer MUST collapse whitespace runs (including newlines) in `source_query` to single
|
||||
spaces.
|
||||
- **Body rendering** — for `file` and `sql`: a markdown table, header row = column names,
|
||||
data rows in source order. Cell values: text verbatim with `\` escaped as `\\`, `|` escaped
|
||||
as `\|`, and newlines replaced by single spaces; integers in plain decimal; non-integral
|
||||
|
|
@ -152,6 +172,13 @@ Materialization takes three EXPLICIT inputs: the manifest, the target bundle dir
|
|||
whose frontmatter carries the ingest stamp, then writes the new set, then updates the index
|
||||
(§6). Version 1 assumes ONE manifest per bundle; multiple manifests feeding one bundle is
|
||||
an extension point.
|
||||
- **Membership from disk, ordering from the caller (load-bearing axis).** The set of files a
|
||||
re-materialization replaces is exactly those the bundle already carries the stamp for — read
|
||||
from disk (§3), never a caller-supplied file list; a caller can therefore never name a file
|
||||
for deletion. The ORDER of generated files and their index links (§6) is the manifest's
|
||||
extraction order — supplied by the caller, never filesystem enumeration order. Splitting the
|
||||
two keeps re-materialization from becoming a caller-controlled delete while keeping output
|
||||
deterministic regardless of directory iteration order.
|
||||
|
||||
## 6. Index generation
|
||||
|
||||
|
|
@ -164,9 +191,10 @@ Materialization takes three EXPLICIT inputs: the manifest, the target bundle dir
|
|||
- On re-materialization, index links whose target is an ingest-owned file removed in this run
|
||||
(§5) MUST be removed; ALL other links — curated and promoted — are preserved verbatim. A
|
||||
promoted verdict's index link therefore survives re-ingest (load-bearing, §11).
|
||||
- The link label is the extraction `title` — generated files are context-layer content, so a
|
||||
descriptive label is correct here. (The fixed-neutral-label rule of method spec §6 protects
|
||||
the VERDICT layer and is untouched by this spec.)
|
||||
- The link label is the extraction `title`, rendered **verbatim** — safe because `title` is
|
||||
`[`/`]`-free by §4. Generated files are context-layer content, so a descriptive label is
|
||||
correct here. (The fixed-neutral-label rule of method spec §6 protects the VERDICT layer and
|
||||
is untouched by this spec.)
|
||||
- *(reference limitation)* the index read-modify-write is not atomic — single-process use is
|
||||
assumed, as in the promotion gate.
|
||||
|
||||
|
|
@ -244,8 +272,10 @@ spec §11 regime):
|
|||
| Seam | The test MUST fail when… |
|
||||
|---|---|
|
||||
| Provenance stamping | a generated file no longer carries the §7 layer |
|
||||
| Stamp integrity (curated writers) | a caller-supplied frontmatter carrying the complete ownership stamp (`generated: true` with `ingest_manifest`) stops being rejected by the verbatim authoring path (§3) |
|
||||
| Navigability | the generated bundle stops being consumable by the UNCHANGED bundle-navigation code, index links included |
|
||||
| Verdict reservation | a manifest mapping to `type: verdict` (or the reserved filename namespace) stops being rejected |
|
||||
| Title link-safety | a `title` containing `[` or `]` stops being rejected fail-fast at manifest load (§4) |
|
||||
| Re-ingest layer safety | re-materialization over a bundle with a promoted verdict deletes the verdict file or its index link |
|
||||
| Golden regression | any byte of a golden extraction's expected bundle diverges |
|
||||
| Network gate | an `http` source stops refusing fail-fast without the opt-in flag |
|
||||
|
|
|
|||
|
|
@ -65,20 +65,42 @@ progressive disclosure — never by stuffing the whole bundle (or keyword-retrie
|
|||
it) into the prompt:
|
||||
|
||||
- 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. Repeated links are de-duplicated; order is deterministic (index first, then links
|
||||
in first-seen order). *(reference: the link pattern is `\]\(([^)]+\.md)\)`)*
|
||||
- A missing `index.md` is an error (a bundle has no entry point without it). A broken or
|
||||
bundle-escaping cross-link MUST be tolerated — skipped, never raised (OKF robustness rule);
|
||||
path resolution MUST be boundary-checked against the bundle directory, fail-closed.
|
||||
(`](target.md)`). A target is resolved **relative to the bundle** and boundary-checked
|
||||
fail-closed (below): a leading `/` denotes the **bundle root** (NEVER a filesystem-absolute
|
||||
path), any other form is relative to the linking file's own directory — so a target MAY
|
||||
address a nested directory (`sub/index.md`, `/a/b.md`). It is **escape, not depth**, that is
|
||||
forbidden: a target resolving outside the bundle is skipped; a legitimate nested target is
|
||||
followed. (This resolve-and-boundary-check REPLACES the old "a path separator means
|
||||
out-of-bundle" heuristic, which conflated depth with escape and forbade valid hierarchy.)
|
||||
Each index entry descends **one path segment per level** — a per-level index links its
|
||||
immediate children, never a multi-segment path. Traversal is **depth-first in first-seen
|
||||
link order** and fully deterministic (the root index first, then each link followed in the
|
||||
order it appears, recursively). Repeated links are de-duplicated **on the resolved path**, so
|
||||
`./a.md` and `a.md` are one entry and cycles terminate. *(reference: the link pattern is
|
||||
`\]\(([^)]+\.md)\)`)*
|
||||
- A missing `index.md` **at the bundle root** is an error (a bundle has no entry point without
|
||||
it) — this binds the **root alone**: an intermediate directory reached by a link is
|
||||
navigated only through the links its own files carry, never by directory enumeration, so a
|
||||
nested directory without its own `index.md` is not an error (its unlinked content is simply
|
||||
unreachable). A broken, malformed, or bundle-escaping cross-link MUST be tolerated — skipped,
|
||||
never raised (OKF robustness rule): a target that fails to resolve for ANY reason (missing
|
||||
file, invalid path component, escape) is skipped, not raised. Path resolution MUST be
|
||||
boundary-checked against the bundle directory, fail-closed — this is the SOLE
|
||||
in-/out-of-bundle test.
|
||||
- Frontmatter is the leading `---`-delimited block, parsed line-oriented as `key: value`
|
||||
strings; the single required field is `type`; unknown fields MUST be preserved.
|
||||
- The rendered read-context is the index body (the summary) followed by each non-index
|
||||
concept file as a `## {type}: {title}` section; empty sections are dropped.
|
||||
- **`type: verdict` files MUST be excluded from the read-context.** Prior verdicts reach the
|
||||
hypothesis prompt ONLY via the gated experience fold below — never via context rendering,
|
||||
and never via a query-time retrieval tool pointed at the bundle (which would re-leak the
|
||||
verdict layer).
|
||||
concept file as a `## {type}: {title}` section; empty sections are dropped. Rendering is
|
||||
**flat regardless of nesting depth** — directory structure is navigation, not presentation,
|
||||
so a nested concept file renders as the same `## {type}: {title}` section a root file would;
|
||||
there is no level heading.
|
||||
- **`type: verdict` files MUST be excluded from the read-context.** The exclusion is a **type
|
||||
check on each file as it is reached** — applied recursively at every level, NEVER a property
|
||||
of the link graph: a file is excluded because it is `type: verdict`, not because of where it
|
||||
sits or how it was linked, so a mislabelled or injected navigation edge can never smuggle a
|
||||
verdict into the context. Prior verdicts reach the hypothesis prompt ONLY via the gated
|
||||
experience fold below — never via context rendering, and never via a query-time retrieval
|
||||
tool pointed at the bundle (which would re-leak the verdict layer).
|
||||
|
||||
**Experience fold (ExpeL-style, the learning seam):** before generation, the candidate's
|
||||
prior verdicts are retrieved from the store and folded into the hypothesis prompt:
|
||||
|
|
@ -399,7 +421,8 @@ cross-reference):
|
|||
| Seam | The test MUST fail when… | Reference test |
|
||||
|---|---|---|
|
||||
| Step-1 fold | a prior verdict no longer reaches the next hypothesis prompt; control: an empty store changes the outcome signal | `test_step1_expel_loadbearing.py` |
|
||||
| Verdict-layer exclusion | the realization signal appears in the rendered read-context | `test_okf.py` (bundle-context exclusion) |
|
||||
| Verdict-layer exclusion | the realization signal appears in the rendered read-context, at the root OR any nested level | `test_okf.py` (bundle-context exclusion) |
|
||||
| Navigation boundary | an escaping cross-link (`..`, a filesystem-absolute path, or a bundle-root `/` read as filesystem-absolute) is followed, a malformed target (e.g. an invalid path component) is raised instead of skipped, or a legitimate nested in-bundle link is skipped | `test_okf.py` (navigation boundary) |
|
||||
| Checker gate | the checker's surfaced output is detached OR its REJECT no longer overrides a validated outcome | `test_checker_gate_loadbearing.py` |
|
||||
| Informed refinement | the prior rejection reason no longer appears verbatim in the next prompt / the outcome never flips | `test_step5_refine_loadbearing.py` |
|
||||
| Async file loop | a verdict dropped after Run A fails to reach Run B's prompt via a FRESH store; control: an empty inbox | `test_step7_async_loop_loadbearing.py` |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue