core/tests/test_served_disposition.py
Shay 178e3967ed feat(epistemic-disclosure): P0-3 — ServedDisposition mapping scaffold
The third axis of the bus: choose_served_disposition composes
EpistemicState × LimitationAssessment × DisclosureClaim → ServedDisposition.
Pure mapping — no rendering, no bus behaviour, no verify.py, no govern_response
mutation. Nothing consumes it yet.

core/epistemic_disclosure/disposition.py:
- ServedDisposition(str, Enum): commit | disclose | ask | propose | report |
  explain | refuse | step_aside.
- choose_served_disposition(*, epistemic_state, limitation, disclosure_claim=NONE):
  a blocking limitation dominates (its resolution_action -> ask/propose/report/
  explain/refuse/step_aside; scope_boundary -> EXPLAIN, NOT a hard REFUSE);
  otherwise the disclosure claim + state pick the serve mode.

The Phase-1 VERIFIED guard (load-bearing): a DisclosureClaim.VERIFIED discloses
ONLY when EpistemicState.VERIFIED backs it; an unbacked verified claim degrades to
a plain COMMIT — never served as verified before the producer exists.

30 tests: the load-bearing rules via REAL P0-1 assessments; the VERIFIED guard
parametrized across all 14 non-VERIFIED states; limitation-dominates-claim;
answer-action fall-through; totality over both axes; off-serving. Smoke 90/0.
2026-06-08 16:00:15 -07:00

189 lines
6.3 KiB
Python

"""P0-3 — tests for the ServedDisposition mapping scaffold.
Mapping-only: no rendering, no bus, no serving. Tests assert the load-bearing rules,
the Phase-1 VERIFIED guard (a verified claim never discloses without the verified
state), limitation-dominates-claim, the explicit scope_boundary -> EXPLAIN choice,
and totality over both axes.
"""
from __future__ import annotations
import ast
import pathlib
from enum import Enum
import pytest
import core.epistemic_disclosure.disposition as disposition_module
from core.comprehension_attempt.failure_family import REGISTRY, family_by_name
from core.epistemic_disclosure.disclosure_claim import DisclosureClaim
from core.epistemic_disclosure.disposition import (
ServedDisposition,
choose_served_disposition,
)
from core.epistemic_disclosure.limitation import LimitationAssessment, assess_from_family
from core.epistemic_state import EpistemicState
def _disp_for_family(name, *, state=EpistemicState.UNDETERMINED, claim=DisclosureClaim.NONE):
family = family_by_name(name)
assert family is not None, name
return choose_served_disposition(
epistemic_state=state, limitation=assess_from_family(family), disclosure_claim=claim
)
# --- serve branch (no blocking limitation) ----------------------------------------- #
def test_no_limitation_none_claim_commits():
assert (
choose_served_disposition(
epistemic_state=EpistemicState.DECODED,
limitation=None,
disclosure_claim=DisclosureClaim.NONE,
)
is ServedDisposition.COMMIT
)
def test_verified_state_and_claim_discloses():
assert (
choose_served_disposition(
epistemic_state=EpistemicState.VERIFIED,
limitation=None,
disclosure_claim=DisclosureClaim.VERIFIED,
)
is ServedDisposition.DISCLOSE
)
@pytest.mark.parametrize(
"state", [s for s in EpistemicState if s is not EpistemicState.VERIFIED]
)
def test_verified_claim_without_verified_state_never_discloses(state):
"""THE Phase-1 guard: a verified claim must not produce DISCLOSE unless the state
is actually VERIFIED — protects the lane from 'verified-looking' serving before the
producer exists."""
disp = choose_served_disposition(
epistemic_state=state, limitation=None, disclosure_claim=DisclosureClaim.VERIFIED
)
assert disp is not ServedDisposition.DISCLOSE
assert disp is ServedDisposition.COMMIT
def test_approximate_claim_discloses():
assert (
choose_served_disposition(
epistemic_state=EpistemicState.EVIDENCED_INCOMPLETE,
limitation=None,
disclosure_claim=DisclosureClaim.APPROXIMATE,
)
is ServedDisposition.DISCLOSE
)
def test_proposal_only_claim_proposes():
assert (
choose_served_disposition(
epistemic_state=EpistemicState.UNDETERMINED,
limitation=None,
disclosure_claim=DisclosureClaim.PROPOSAL_ONLY,
)
is ServedDisposition.PROPOSE
)
# --- limitation-driven dispositions (via real P0-1 assessments) -------------------- #
def test_ask_question_limitation_asks():
assert _disp_for_family("cmb_underdetermined") is ServedDisposition.ASK
def test_emit_proposal_limitation_proposes():
assert _disp_for_family("cmb_unsupported_reciprocal") is ServedDisposition.PROPOSE
def test_report_contradiction_limitation_reports():
assert _disp_for_family("answer_key_contradiction") is ServedDisposition.REPORT
def test_step_aside_limitation_steps_aside():
assert _disp_for_family("input_shape") is ServedDisposition.STEP_ASIDE
def test_hard_boundary_limitation_refuses():
assert _disp_for_family("cmb_non_positive_net") is ServedDisposition.REFUSE
def test_scope_boundary_limitation_explains_not_refuses():
"""The explicit P0-3 choice: scope is EXPLAIN, not a hard REFUSE."""
disp = _disp_for_family("unsupported_system_size")
assert disp is ServedDisposition.EXPLAIN
assert disp is not ServedDisposition.REFUSE
def test_limitation_dominates_disclosure_claim():
"""Even a fully-backed verified claim cannot serve an answer when a limitation blocks."""
family = family_by_name("cmb_underdetermined")
assert family is not None
disp = choose_served_disposition(
epistemic_state=EpistemicState.VERIFIED,
limitation=assess_from_family(family),
disclosure_claim=DisclosureClaim.VERIFIED,
)
assert disp is ServedDisposition.ASK
def test_answer_action_falls_through_to_serve():
"""A non-blocking limitation (resolution_action == 'answer') yields the serve decision."""
la = LimitationAssessment(
limitation_kind="missing_information",
resolution_action="answer",
epistemic_state=EpistemicState.UNDETERMINED,
owner_organ="r2",
blocking_reason="synthetic",
)
assert (
choose_served_disposition(
epistemic_state=EpistemicState.DECODED,
limitation=la,
disclosure_claim=DisclosureClaim.NONE,
)
is ServedDisposition.COMMIT
)
# --- totality over both axes ------------------------------------------------------- #
def test_every_family_maps_to_a_disposition():
for family in REGISTRY:
disp = choose_served_disposition(
epistemic_state=EpistemicState.UNDETERMINED, limitation=assess_from_family(family)
)
assert isinstance(disp, ServedDisposition), family.name
def test_every_disclosure_claim_serves_to_a_disposition():
for claim in DisclosureClaim:
disp = choose_served_disposition(
epistemic_state=EpistemicState.VERIFIED, limitation=None, disclosure_claim=claim
)
assert isinstance(disp, ServedDisposition), claim
# --- hygiene ----------------------------------------------------------------------- #
def test_is_str_enum():
assert issubclass(ServedDisposition, str)
assert issubclass(ServedDisposition, Enum)
def test_module_is_off_serving():
module_path = disposition_module.__file__
assert module_path is not None
src = pathlib.Path(module_path).read_text()
mods = [n.module or "" for n in ast.walk(ast.parse(src)) if isinstance(n, ast.ImportFrom)]
forbidden = [
m for m in mods if "generate.derivation" in m or "reliability_gate" in m or m.endswith("verify")
]
assert forbidden == []