feat(okf): trust_tier derives three tiers from the actor prefix
Co-Authored-By: Claude <claude-opus-5>
This commit is contained in:
parent
7552be239b
commit
a00cde6444
2 changed files with 125 additions and 0 deletions
|
|
@ -371,6 +371,53 @@ class SkippedLink:
|
||||||
reason: SkipReason
|
reason: SkipReason
|
||||||
|
|
||||||
|
|
||||||
|
#: A concept's trust level, derived from its ``verified`` actors (SPEC §5.3), lowest to highest.
|
||||||
|
#: Derived, never stored: OKF records objective signals and refuses to persist a subjective score,
|
||||||
|
#: so this is a reading of the actors and not a field any document carries.
|
||||||
|
TrustTier = Literal["unverified", "machine-confirmed", "human-reviewed"]
|
||||||
|
|
||||||
|
#: The ONE actor prefix that raises a concept to the human tier (SPEC §7 actor convention).
|
||||||
|
_HUMAN_ACTOR_PREFIX = "human:"
|
||||||
|
|
||||||
|
|
||||||
|
def trust_tier(entries: tuple[dict[str, str], ...] | None) -> TrustTier:
|
||||||
|
"""Derive the trust tier from ``verified`` entries (SPEC §5.3).
|
||||||
|
|
||||||
|
``None`` or empty ⇒ ``unverified``; any actor whose ``by`` STARTS WITH ``human:`` ⇒
|
||||||
|
``human-reviewed``; otherwise ⇒ ``machine-confirmed``.
|
||||||
|
|
||||||
|
**Prefix, never substring.** ``by: bot/human:2`` is a machine actor whose identifier merely
|
||||||
|
contains the token, and a substring test would promote it — minting a human sign-off nobody
|
||||||
|
gave, which is the fabricated-provenance defect one level down.
|
||||||
|
|
||||||
|
**An entry that names NO actor is REFUSED, never tiered.** "Otherwise ⇒ machine-confirmed"
|
||||||
|
would derive a trust level from an entry that identifies nobody. ``decode_flow_value`` already
|
||||||
|
refuses that shape when it reads a ``verified`` value; this function is public, so it ASSERTS
|
||||||
|
the invariant at its own door instead of assuming its caller came through that one. No new
|
||||||
|
exception type: the named refusal for this condition belongs to the decoder, and a second class
|
||||||
|
here would imply a second rule.
|
||||||
|
|
||||||
|
**Entry count is not a tier.** SPEC §5.3 derives the tier from the actor prefix alone, so a
|
||||||
|
two-entry machine list stays ``machine-confirmed``.
|
||||||
|
|
||||||
|
Gated by ``tests/test_provenance_decoder_loadbearing.py``."""
|
||||||
|
if not entries:
|
||||||
|
return "unverified"
|
||||||
|
actors: list[str] = []
|
||||||
|
for entry in entries:
|
||||||
|
actor = entry.get("by", "").strip()
|
||||||
|
if not actor:
|
||||||
|
raise ValueError(
|
||||||
|
f"a verification entry {entry!r} names no `by` actor — SPEC §5.2 makes it "
|
||||||
|
"required, and deriving a trust tier from an entry that identifies nobody would "
|
||||||
|
"mint the provenance it claims to read"
|
||||||
|
)
|
||||||
|
actors.append(actor)
|
||||||
|
if any(actor.startswith(_HUMAN_ACTOR_PREFIX) for actor in actors):
|
||||||
|
return "human-reviewed"
|
||||||
|
return "machine-confirmed"
|
||||||
|
|
||||||
|
|
||||||
#: WHY a provenance value could not be read. The tokens name the SHAPE the value is written in
|
#: 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
|
#: 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
|
#: mapping are both CONFORMANT OKF (SPEC §5.2 writes ``verified`` in exactly those forms); they are
|
||||||
|
|
|
||||||
|
|
@ -431,3 +431,81 @@ def test_the_offending_text_is_carried_verbatim() -> None:
|
||||||
assert isinstance(result, okf.UnreadableProvenance)
|
assert isinstance(result, okf.UnreadableProvenance)
|
||||||
assert result.value == " - { by: human:a, at: 2026-01-01T00:00:00Z }"
|
assert result.value == " - { by: human:a, at: 2026-01-01T00:00:00Z }"
|
||||||
assert result.file == str(path)
|
assert result.file == str(path)
|
||||||
|
|
||||||
|
|
||||||
|
# --- Step 5: trust tier from the actor prefix -------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def test_no_entries_at_all_is_unverified() -> None:
|
||||||
|
"""SPEC §5.3: "No ``verified`` key ⇒ unverified". ``None`` and the empty tuple say the same
|
||||||
|
thing here — nothing was verified — and both must reach the same floor."""
|
||||||
|
assert okf.trust_tier(None) == "unverified"
|
||||||
|
assert okf.trust_tier(()) == "unverified"
|
||||||
|
|
||||||
|
|
||||||
|
def test_a_machine_actor_gives_machine_confirmed() -> None:
|
||||||
|
assert okf.trust_tier(({"by": "process:finance-nightly", "at": "2026-01-01T00:00:00Z"},)) == (
|
||||||
|
"machine-confirmed"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_a_human_actor_gives_human_reviewed() -> None:
|
||||||
|
assert okf.trust_tier(({"by": "human:ktg", "at": "2026-01-01T00:00:00Z"},)) == "human-reviewed"
|
||||||
|
|
||||||
|
|
||||||
|
def test_the_tier_keys_off_the_PREFIX_never_a_substring() -> None:
|
||||||
|
"""``bot/human:2`` is a MACHINE actor whose identifier happens to contain ``human:``.
|
||||||
|
|
||||||
|
A substring test would promote it to the human tier — inventing a human sign-off that never
|
||||||
|
happened, which is the fabricated-provenance defect this work removes, one level down.
|
||||||
|
"""
|
||||||
|
assert okf.trust_tier(({"by": "bot/human:2", "at": "2026-01-01T00:00:00Z"},)) == (
|
||||||
|
"machine-confirmed"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_an_entry_naming_no_actor_is_refused_never_tiered() -> None:
|
||||||
|
""" "Otherwise ⇒ machine-confirmed" would mint a tier out of an entry that names nobody.
|
||||||
|
|
||||||
|
Step 3 already refuses such an entry when it decodes a ``verified`` value; this door ASSERTS
|
||||||
|
that rather than assuming it, because ``trust_tier`` is public and its caller may not have come
|
||||||
|
through the decoder.
|
||||||
|
"""
|
||||||
|
with pytest.raises(ValueError) as excinfo:
|
||||||
|
okf.trust_tier(({"at": "2026-01-01T00:00:00Z"},))
|
||||||
|
assert "by" in str(excinfo.value)
|
||||||
|
|
||||||
|
|
||||||
|
def test_a_human_LAST_entry_still_resolves_to_human_reviewed() -> None:
|
||||||
|
"""AMENDMENT C — the ordering IS the arm.
|
||||||
|
|
||||||
|
``any(...)`` and "the last entry wins" agree on every single-entry case AND on a human-FIRST
|
||||||
|
list. They disagree on exactly one shape: process first, human last. That is the one fixture
|
||||||
|
that can tell the specified rule from the measured second-entry-wins defect, so it is the one
|
||||||
|
written here.
|
||||||
|
"""
|
||||||
|
process_first_human_last = (
|
||||||
|
{"by": "process:finance-nightly", "at": "2026-01-01T02:00:00Z"},
|
||||||
|
{"by": "human:ktg", "at": "2026-01-02T09:00:00Z"},
|
||||||
|
)
|
||||||
|
assert okf.trust_tier(process_first_human_last) == "human-reviewed"
|
||||||
|
|
||||||
|
# CONTROL — human FIRST, process LAST. Without it, a function that always answered
|
||||||
|
# `human-reviewed` would satisfy the arm above.
|
||||||
|
human_first_process_last = (
|
||||||
|
{"by": "human:ktg", "at": "2026-01-02T09:00:00Z"},
|
||||||
|
{"by": "process:finance-nightly", "at": "2026-01-01T02:00:00Z"},
|
||||||
|
)
|
||||||
|
assert okf.trust_tier(human_first_process_last) == "human-reviewed"
|
||||||
|
|
||||||
|
# CONTROL — two machine actors stay machine-confirmed, so the tier is not simply a function
|
||||||
|
# of the entry COUNT (SPEC §5.3 derives it from the actor prefix alone).
|
||||||
|
assert (
|
||||||
|
okf.trust_tier(
|
||||||
|
(
|
||||||
|
{"by": "process:a", "at": "2026-01-01T02:00:00Z"},
|
||||||
|
{"by": "process:b", "at": "2026-01-02T02:00:00Z"},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
== "machine-confirmed"
|
||||||
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue