portfolio-optimiser/docs/okf-konsum-kontrakter.md
Kjell Tore Guttormsen 9e35cfefbf docs(okf): grep-bart anker inn i SPEC-en, ikke et linjenummer
Et linjenummer inn i et ANNET repos fil raatner paa deres neste redigering, og
denne fila skal kunne leses utenfra. Samme regel som STATE-pekere: paragraf-
anker, verifisert med en grep som faktisk treffer.

Co-Authored-By: Claude <claude-opus-5>
2026-09-02 20:23:43 +02:00

6.5 KiB
Raw Blame History

OKF consumption contracts

This file is the source. Three facts below span a producer (llm-ingestion-okf) and this consumer, so neither side can hold the other to them from inside its own plan file. Docstrings, tests and the invariant ledger in CLAUDE.md point here; they do not restate the rule. A second copy of a contract is the drift this repository has a rule against.

The producer's half is recorded in that repository's docs/plan/office-intake.md §§ 45 and docs/consumption-contract.md. Ratified at programme level 2026-09-02. Written in English because the counterparty half is, and a contract split across two languages is a contract nobody reads whole.

Every number below comes from a command, and each command is written next to its number.

§ 1. The falsification threshold — present and not unverified

A falsification verdict may rest on a concept only when both hold:

evidence_for(path).state == "present"   AND   tier != "unverified"

Everything else is reported and explicitly discounted, never silently excluded. The report carries the triple (state, reason, items_seen): a concept that was dropped and a concept that was read-and-discounted are different facts, and only the second is honest about what was actually read.

The threshold is expressed in exactly one placeokf.admits_falsification(evidence) -> bool. A threshold spelled inline at each caller is a threshold nobody can find, and a second copy is free to drift from the first.

author, usage_count and last_modified are deliberately NOT required, and the denominator is written down rather than implied. SPEC § 5.1 names six keys on a sources entry — resource, id, title, author, usage_count, last_modified — and the producer writes two of them:

# Grep-able anchor, never a line number — a line anchor into another repository's file rots
# on that repository's next edit, and this one has to survive being read from outside.
grep -A11 'Each `sources` entry:' ~/repos/_okf-canonical/SPEC.md      # the six entry keys
grep -rh '^sources:' ~/repos/llm-ingestion-okf/examples/
#   -> sources: [{ id: golden-v0-2-sales, resource: fixture }]
#      (1 of the 26 markdown files under examples/ carries the key at all)

Measured 2026-09-02 against producer 62b6192. 2 of 6, not 2 of 5 — an earlier draft of this contract carried the smaller denominator, which would have understated how much of § 5.1 the producer leaves unwritten. Requiring the three credibility signals would make the threshold unreachable in practice while looking strict on paper: the worst of both. The threshold names only what is actually written.

usage_window is a sibling of sources, not an entry key, and is outside the six.

§ 2. The adjudication state — absence is unknown, never absent

A concept carries its adjudication state in the frontmatter key adjudication, whose value set is closed: proposed or adjudicated.

Observation State
adjudication: proposed proposed
adjudication: adjudicated adjudicated
the key is not present unknown
any other value refused by name — never mapped into the vocabulary

The third row is the whole contract. A concept that does not carry the key means we did not learn whether this was adjudicated (an older bundle), and collapsing that into absentit was not adjudicated — is a verdict on missing evidence presented as evidence of absence. It is the same defect as collapsing unreadable into absent one layer up, and the same defect the ungiven-verdict work removed from RunResult.verdict.

The fourth row is validation, never repair: an out-of-vocabulary value is an error, not an extension point, and mapping it to unknown would silently invent the very state this contract exists to keep honest.

Honesty limit, stated: this consumer's fixtures are hand-written to the contract above, not produced by llm-ingestion-okf. Integration against the producer's own golden is later work and is not claimed here.

§ 3. Identity across bundles — the pair (bundle_id, concept_id)

A concept ID is bundle-local and stable. Identity across bundles is the pair (bundle_id, concept_id) — never the concept ID alone.

bundle_id resolves in a fixed order, and the resolved value carries which source answered:

Order Source origin
1 the concept's own frontmatter declared-concept
2 the bundle root's index.md declared-index
3 neither declared it — the mount basename is used mount-derived

Three origins, not a boolean. A caller that cannot tell "the concept said so" from "we fell back twice" has been handed a stamp it cannot audit.

A base with no readable index.md is not "undeclared". The existing navigation fail-fast propagates unchanged; reading an unreadable base as "no declaration" is the tolerant-read defect that widens an answer instead of refusing it. Only a base whose index is readable and declares nothing falls through to the next row.

Measured today: every base resolves mount-derived. No shipped index.md declares the key:

find shared -name index.md | wc -l          # -> 7
grep -l 'bundle_id' $(find shared -name index.md) | wc -l   # -> 0

So this contract changes which sources are consulted, not what happens when none of them declares anything — which is why adopting it moves no existing call site.

There is no cross-bundle link form. A consumer holding several bundles partitions per bundle; the producer emits no syntax for pointing from inside one bundle into another, and adding one is out of scope until a consumer states the need.

§ 4. What these three do not settle

  • The wire form of sources is the producer's to fix. Flow-sequence-of-mappings is the normative emission form; block form remains conformant OKF but outside the accepted subset. A known defect stands on the producer side: llm-ingestion-guard 1.2.0 rejects a flow sequence of mappings for sources. That is a known producer-side gate defect, not a consumer error, and this consumer does not wait on it.
  • The trust tiers themselves (§ 5.3 of the SPEC) are inferred, never stored — no credibility score is written by either side.
  • Nothing here wires a library primitive into a run's stdout. The reader reads; the caller decides. Both halves of that split predate these contracts and are unchanged by them.