Replace the combined concept total, the distinct-token total and the per-level document count of earlier example bases with general wording in prose, comments and docstrings. No constant, assertion or test data changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
156 lines
7.2 KiB
Python
156 lines
7.2 KiB
Python
"""``evidence_for``'s ``key`` parameter works for keys other than ``verified``.
|
|
|
|
Reported at the close of B4 (session 76, PM addendum 5 to order
|
|
``20260902T151931Z-250257273``) and NOT fixed there, because it was outside that order:
|
|
|
|
evidence_for(path, key="sources") -> ValueError
|
|
|
|
``tier`` was derived UNCONDITIONALLY via ``trust_tier``, which refuses an entry that names no
|
|
``by`` actor — correctly, because deriving a trust level from an entry identifying nobody would
|
|
mint the provenance it claims to read. But ``by`` is required of a ``verified`` entry (SPEC §5.2),
|
|
not of every provenance key: the agreed segmented form carries ``segment_id``/``source_offset``,
|
|
and a ``sources`` list carries ``id``/``resource``. So the guard belonging to ONE key was applied
|
|
to ALL of them, and any other key whose value decoded cleanly crashed.
|
|
|
|
**It was never measured because it was never exercised:** all 11 call sites in the repo use the
|
|
default, so the parameter had no test of its own. A parameter no test drives is a parameter that
|
|
rots — the same class as a field no artefact carries.
|
|
|
|
The fix keeps the guard exactly where SPEC §5.3 puts it. ``trust_tier`` is UNCHANGED and still
|
|
refuses an actor-less ``verified`` entry; ``evidence_for`` derives a tier only for the key §5.3
|
|
tiers, and answers every other key with ``state`` / ``reason`` / ``items_seen`` / ``entries`` and
|
|
``tier=None``.
|
|
|
|
``admits_falsification`` had to move WITH it, and that is not scope creep — it is the same fact.
|
|
It read ``tier != "unverified"``, and ``None != "unverified"`` is true, so a present ``sources``
|
|
key would have admitted falsification: a document would have cleared the K5 threshold on evidence
|
|
that is not a verification at all. The threshold now names the tiers that clear it. For every
|
|
value reachable before this change the two spellings are identical, which is why the existing K5
|
|
arms stay green — the tightening is only for the state this change makes reachable.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from portfolio_optimiser import okf
|
|
|
|
_TWO_SOURCES = (
|
|
"---\n"
|
|
"type: concept\n"
|
|
"title: Serverromskjøling\n"
|
|
"sources: [{ id: d100, resource: driftskrav }, { id: ipmvp, resource: efficiency-valuation }]\n"
|
|
"verified: { by: human:kjell, at: 2026-09-02 }\n"
|
|
"---\n"
|
|
"\nInnhold.\n"
|
|
)
|
|
|
|
|
|
def _concept(tmp_path: Path, text: str = _TWO_SOURCES) -> Path:
|
|
path = tmp_path / "konsept.md"
|
|
path.write_text(text, encoding="utf-8")
|
|
return path
|
|
|
|
|
|
def test_a_non_verified_key_is_read_instead_of_raising(tmp_path: Path) -> None:
|
|
"""THE GOAL, in the reporter's own words: ``key="sources"`` on a concept with two sources
|
|
returns, and says it saw two.
|
|
|
|
Detach point: derive the tier unconditionally again → ``ValueError`` (RED).
|
|
"""
|
|
evidence = okf.evidence_for(_concept(tmp_path), key="sources")
|
|
|
|
assert evidence.state == "present"
|
|
assert evidence.items_seen == 2
|
|
assert [e["id"] for e in evidence.entries] == ["d100", "ipmvp"]
|
|
|
|
|
|
def test_a_non_verified_key_carries_no_tier(tmp_path: Path) -> None:
|
|
"""SPEC §5.3 tiers ``verified``. A tier on anything else would be a trust level derived from
|
|
a key that says nothing about trust — asserted, never inferred."""
|
|
assert okf.evidence_for(_concept(tmp_path), key="sources").tier is None
|
|
|
|
|
|
def test_a_non_verified_key_does_not_clear_the_k5_threshold(tmp_path: Path) -> None:
|
|
"""The consequence arm. ``tier != "unverified"`` is TRUE for ``None``, so without moving the
|
|
threshold with the fix, a present ``sources`` list would admit falsification — a document
|
|
clearing K5 on evidence that is not a verification.
|
|
|
|
Detach point: revert the threshold to ``!= "unverified"`` → RED here, green everywhere else.
|
|
"""
|
|
evidence = okf.evidence_for(_concept(tmp_path), key="sources")
|
|
|
|
assert evidence.state == "present", (
|
|
"the arm must run against a PRESENT key, or it proves nothing"
|
|
)
|
|
assert okf.admits_falsification(evidence) is False
|
|
|
|
|
|
def test_the_default_key_still_tiers_and_still_clears(tmp_path: Path) -> None:
|
|
"""The CONTROL. A fix that stopped tiering ``verified`` would pass all three arms above and
|
|
disable the whole falsification threshold."""
|
|
evidence = okf.evidence_for(_concept(tmp_path))
|
|
|
|
assert evidence.state == "present"
|
|
assert evidence.tier == "human-reviewed"
|
|
assert okf.admits_falsification(evidence) is True
|
|
|
|
|
|
def test_an_actorless_verified_entry_is_still_refused(tmp_path: Path) -> None:
|
|
"""The guard stays exactly where SPEC §5.2 puts it. Loosening ``trust_tier`` itself would have
|
|
fixed the crash by minting a trust tier for an entry that identifies nobody — the defect
|
|
``trust_tier``'s own docstring exists to refuse.
|
|
|
|
**A premise of this arm was wrong when first written, and the measurement corrected it:** an
|
|
actorless ``verified`` value never reaches ``trust_tier`` through ``evidence_for`` at all — the
|
|
decoder refuses that shape first, as ``unreadable`` / ``unsupported-flow`` (measured). So BOTH
|
|
guards are asserted where each actually lives, rather than one asserted where it is not.
|
|
"""
|
|
path = _concept(
|
|
tmp_path,
|
|
"---\ntype: concept\nverified: { at: 2026-09-02 }\n---\n\nInnhold.\n",
|
|
)
|
|
|
|
evidence = okf.evidence_for(path)
|
|
assert (evidence.state, evidence.reason, evidence.tier) == (
|
|
"unreadable",
|
|
"unsupported-flow",
|
|
None,
|
|
), "the decoder refuses an actorless verified value before any tier is derived"
|
|
assert okf.admits_falsification(evidence) is False
|
|
|
|
# And the public guard one layer down is untouched: called DIRECTLY with the shape the decoder
|
|
# would never hand it, ``trust_tier`` still refuses rather than tiering nobody.
|
|
with pytest.raises(ValueError):
|
|
okf.trust_tier(({"at": "2026-09-02"},))
|
|
|
|
|
|
def test_the_three_non_present_states_are_unchanged_for_any_key(tmp_path: Path) -> None:
|
|
"""Absent and unreadable never tiered anything, and must still not — the fix touches the
|
|
PRESENT arm alone."""
|
|
absent = okf.evidence_for(_concept(tmp_path), key="adjudication")
|
|
assert (absent.state, absent.tier, absent.items_seen) == ("absent", None, 0)
|
|
|
|
# P13b made the block sequence of mappings READABLE (measured: every concept in the
|
|
# four delivered bases write exactly that form). The specimen therefore moved to one that is
|
|
# still unreadable for a reason of its own — an item the flow decoder refuses — so this arm
|
|
# keeps asserting what it always asserted: an unreadable value is never tiered.
|
|
blocked = _concept(
|
|
tmp_path,
|
|
"---\ntype: concept\nsources:\n - { id d100, resource driftskrav }\n---\n\nInnhold.\n",
|
|
)
|
|
unreadable = okf.evidence_for(blocked, key="sources")
|
|
assert unreadable.state == "unreadable"
|
|
assert unreadable.tier is None
|
|
assert unreadable.reason == "block-sequence"
|
|
|
|
# The reverse direction, pinned in the same arm so the widening cannot regress silently.
|
|
readable = _concept(
|
|
tmp_path,
|
|
"---\ntype: concept\nsources:\n - id: d100\n resource: driftskrav\n---\n\nInnhold.\n",
|
|
)
|
|
present = okf.evidence_for(readable, key="sources")
|
|
assert present.state == "present"
|
|
assert present.tier is None # `sources` is not the key SPEC §5.3 tiers — the B4 rule stands
|