feat(okf): evidence_for distinguishes present, absent and unreadable with a reason

Co-Authored-By: Claude <claude-opus-5>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-02 20:31:18 +02:00
commit d62e89935c
2 changed files with 232 additions and 1 deletions

View file

@ -455,6 +455,101 @@ def trust_tier(entries: tuple[dict[str, str], ...] | None) -> TrustTier:
return "machine-confirmed"
#: What a document says about ONE provenance key. Three states, and the third is the whole point:
#: collapsing ``unreadable`` into ``absent`` turns a verdict on missing evidence into evidence of
#: absence.
EvidenceState = Literal["present", "absent", "unreadable"]
@dataclass(frozen=True)
class FalsificationEvidence:
"""What one document offers a falsification verdict, and how much of it could be read.
Each field asserts only what its state can honestly carry: ``tier`` is ``None`` unless the state
is ``present`` (an unread value tiers nothing), and ``reason`` is ``None`` when it IS present
(there is nothing to explain). ``items_seen`` is its own field rather than folded into a token,
the ``BudgetExceeded`` -(y) rule: "which shape" and "how many" are two operative questions."""
#: Path of the document the evidence was read from.
file: str
state: EvidenceState
#: The derived trust tier — ``None`` unless ``state`` is ``present``.
tier: TrustTier | None
#: WHY the value could not be read — ``None`` unless ``state`` is ``unreadable``.
reason: ProvenanceReason | None
#: The decoded entries, empty unless ``state`` is ``present``.
entries: tuple[dict[str, str], ...]
#: How many entries were seen, INCLUDING ones that could not be decoded. Part of the
#: ``(state, reason, items_seen)`` triple a discounted concept is reported with.
items_seen: int
def evidence_for(path: str | Path, key: str = "verified") -> FalsificationEvidence:
"""Read one document's provenance into the three-state answer a falsification verdict needs.
**A library primitive, deliberately NOT wired into ``run_project`` or ``explore``**, mirroring
``promote_verdict`` and ``write_verdict``: the system reads, the caller decides. Wiring it into
the run surface would move the byte-pinned demo transcript, which nothing asks for.
Gated by ``tests/test_falsification_verdict_loadbearing.py``."""
result = read_provenance(path, key)
if result is None:
return FalsificationEvidence(
file=str(path), state="absent", tier=None, reason=None, entries=(), items_seen=0
)
if isinstance(result, UnreadableProvenance):
return FalsificationEvidence(
file=str(path),
state="unreadable",
tier=None,
reason=result.reason,
entries=(),
items_seen=result.items_seen,
)
return FalsificationEvidence(
file=str(path),
state="present",
tier=trust_tier(result),
reason=None,
entries=result,
items_seen=len(result),
)
def evidence_notice(evidence: FalsificationEvidence) -> str | None:
"""One line about evidence that could not be used, or ``None`` when there is nothing to say.
Omission, never an empty row (the ``cost_baseline_notice`` precedent). The reason TOKEN is
printed raw rather than translated into prose, so no second display vocabulary exists to drift
from ``ProvenanceReason``."""
if evidence.state == "present":
return None
detail = "" if evidence.reason is None else f"; reason {evidence.reason}"
return (
f"provenance {evidence.state} in {evidence.file}{detail}; items_seen={evidence.items_seen}"
)
def admits_falsification(evidence: FalsificationEvidence) -> bool:
"""The K5 threshold, expressed in ONE place: ``present`` AND a tier above ``unverified``.
An operator decision, not a default. A second copy of a threshold drifts (-(p)), and a
threshold spelled inline at each caller is a threshold nobody can find.
Everything it refuses is meant to be REPORTED with ``(state, reason, items_seen)`` and
explicitly discounted never silently excluded, because a dropped concept and a discounted one
are different facts and only one of them is honest about what was read.
**``author`` / ``usage_count`` / ``last_modified`` are deliberately NOT required, and the
denominator is written down rather than implied:** SPEC §5.1 names SIX entry keys and the
producer writes TWO of them. Requiring keys the producer does not emit would make the threshold
unreachable in practice while looking strict on paper. The threshold names only what is
actually written. See ``docs/okf-konsum-kontrakter.md`` § 1, which is the source for this rule.
Gated by ``tests/test_falsification_verdict_loadbearing.py``."""
return evidence.state == "present" and evidence.tier != "unverified"
#: WHY a provenance value could not be read. The tokens name the SHAPE the value is written in
#: and NOTHING else — the same discipline ``SkipReason`` carries. A block sequence and a block
#: mapping are both CONFORMANT OKF (SPEC §5.2 writes ``verified`` in exactly those forms); they are

View file

@ -17,9 +17,10 @@ reusing it would mean editing a frozen module to reach a repo-owned fixture.
from __future__ import annotations
import shutil
from pathlib import Path
from portfolio_optimiser import okf
from portfolio_optimiser import okf, verdicts
_GOLDEN_DIR = Path(__file__).resolve().parents[1] / "tests" / "golden" / "block-form-provenance"
@ -52,3 +53,138 @@ def test_block_form_bundle_renders_the_captured_bytes() -> None:
assert [f.name for f in bundle.files] == ["index.md", "attested.md", "multi-verified.md"]
assert okf.bundle_context(bundle) == expected
# --- Step 7: the falsification verdict, with the third state carrying its reason ---------------
_ENERGI_BUNDLE = Path(__file__).resolve().parents[1] / "shared" / "examples" / "bygg-energi-mikro"
def _present_document(tmp_path: Path) -> Path:
"""A REAL flow-form document, produced by our own writer — never a mock.
All three arms below read committed or freshly-written files, because the property under test
is what the reader does with bytes on disk, and a stubbed reader would test the stub.
"""
dst = tmp_path / "bundle"
shutil.copytree(_ENERGI_BUNDLE, dst)
return verdicts.promote_verdict(
str(dst),
verdicts.Verdict(
id="EVIDENCE-PRESENT",
proposal_features=verdicts.bundle_candidate_features(str(dst)),
decision="approved",
rationale="godkjent",
),
approver="process:fixture-check",
experiment="exp-B",
timestamp="2026-06-30T09:00:00Z",
)
def test_a_flow_form_document_is_present_and_tiered(tmp_path: Path) -> None:
"""CONTROL — without it, every arm below is satisfied by a reader that always says "absent"."""
evidence = okf.evidence_for(_present_document(tmp_path))
assert evidence.state == "present"
assert evidence.tier == "machine-confirmed"
assert evidence.reason is None, (
"a present state has no reason to carry, and must not invent one"
)
assert evidence.items_seen == 1
def test_a_document_without_the_key_is_absent_and_carries_no_tier() -> None:
"""``absent`` is what a document that never claimed verification says — and it is NOT the same
fact as ``unreadable``."""
evidence = okf.evidence_for(_GOLDEN_DIR / "bundle" / "index.md")
assert evidence.state == "absent"
assert evidence.tier is None
assert evidence.reason is None
assert evidence.items_seen == 0
def test_a_block_form_document_is_unreadable_and_says_WHY() -> None:
"""The third state, and the arm asserts the REASON, not merely the state.
A collapsed ``unreadable`` ``absent`` is a verdict on missing evidence presented as evidence
of absence. Asserting only ``state != "present"`` would stay green against exactly that
collapse, so the reason token is what this arm pins.
"""
evidence = okf.evidence_for(_GOLDEN_DIR / "bundle" / "attested.md")
assert evidence.state == "unreadable"
assert evidence.reason == "block-sequence"
assert evidence.items_seen == 1
def test_a_two_entry_block_document_yields_NO_tier() -> None:
"""A tier here would be the measured second-entry-wins defect surfacing.
``multi-verified.md`` carries a human sign-off FIRST and a process entry SECOND. A reader that
limped past the block form and kept the last entry it saw would report ``machine-confirmed``
for a concept a human signed downgrading the tier with nothing failing. The honest answer to
an unreadable value is no tier at all.
"""
evidence = okf.evidence_for(_GOLDEN_DIR / "bundle" / "multi-verified.md")
assert evidence.state == "unreadable"
assert evidence.tier is None
assert evidence.items_seen == 2
def test_evidence_notice_is_None_when_there_is_nothing_to_say(tmp_path: Path) -> None:
"""Omission, never an empty row — the ``cost_baseline_notice`` precedent."""
assert okf.evidence_notice(okf.evidence_for(_present_document(tmp_path))) is None
def test_evidence_notice_prints_the_reason_TOKEN_itself() -> None:
"""No second display vocabulary. A prose translation here would be free to drift from
``ProvenanceReason``, and the drifted copy is the one the operator would read."""
notice = okf.evidence_notice(okf.evidence_for(_GOLDEN_DIR / "bundle" / "attested.md"))
assert notice is not None
assert "block-sequence" in notice
assert "items_seen=1" in notice
# --- Amendment A: the K5 threshold ------------------------------------------------------------
def test_admits_falsification_refuses_a_state_that_is_not_present() -> None:
"""AMENDMENT A, first conjunct — a verdict may not rest on evidence that was never read."""
assert not okf.admits_falsification(okf.evidence_for(_GOLDEN_DIR / "bundle" / "attested.md"))
assert not okf.admits_falsification(okf.evidence_for(_GOLDEN_DIR / "bundle" / "index.md"))
def test_admits_falsification_refuses_an_unverified_tier() -> None:
"""AMENDMENT A, second conjunct — its OWN arm, because one arm cannot separate two conjuncts.
**This state is not reachable through ``evidence_for`` today, and that is measured, not
assumed:** ``decode_flow_value`` refuses an empty flow sequence, so a ``present`` value always
carries at least one entry naming an actor, and ``trust_tier`` therefore never answers
``unverified`` for it. The conjunct is DEFENSIVE, which is precisely why it needs an arm of its
own a single arm over a reachable document would leave it untested, and an untested conjunct
is one a later simplification deletes without anything going red.
"""
unverified = okf.FalsificationEvidence(
file="crafted.md", state="present", tier="unverified", reason=None, entries=(), items_seen=0
)
assert not okf.admits_falsification(unverified)
def test_admits_falsification_ADMITS_the_positive_case(tmp_path: Path) -> None:
"""CONTROL — an always-refusing threshold passes both negative arms and is worthless."""
assert okf.admits_falsification(okf.evidence_for(_present_document(tmp_path)))
def test_a_discounted_concept_reports_the_TRIPLE_not_merely_the_refusal() -> None:
"""AMENDMENT A — "why it was discounted" is the operative fact.
A dropped concept and a discounted one are different facts, and only the second is honest about
what was read. Asserting that admission was denied says nothing about which of them happened;
the triple ``(state, reason, items_seen)`` is what makes the difference legible.
"""
evidence = okf.evidence_for(_GOLDEN_DIR / "bundle" / "multi-verified.md")
assert not okf.admits_falsification(evidence)
assert (evidence.state, evidence.reason, evidence.items_seen) == (
"unreadable",
"block-sequence",
2,
)