feat(adr-0206): response-governance bridge scaffold (STRICT-only, inert)

Adds the first consumer of CORE's two built epistemic substrates — the
decode-state taxonomy (core/epistemic_state.py) and the risk-reward
reliability gate (core/reliability_gate/, ADR-0175) — beginning to close
the LABEL-ONLY consumption gap they sat behind.

Ships the scaffold only:
- core/response_governance/policy.py — ReachLevel (STRICT < APPROXIMATE <
  EXTRAPOLATE < CREATIVE), ReachPolicy, govern_response (STRICT-only stub),
  shape_surface (STRICT = identity transform; higher levels real but
  unreachable in production), and the 9/5/1 EpistemicState partition.
- chat/runtime.py — cognition-path seam: the response surface now flows
  through shape_surface(govern_response(...)). STRICT = identity, so the
  path is byte-identical to pre-bridge. reach_level carried on ChatResponse.
- core/physics/identity.py — reach_level on TurnEvent (default "strict").

wrong==0 untouched: select_self_verified is NOT touched (ADR-0206 §5);
reach_level is NOT added to the telemetry JSONL dict, so pinned lane SHAs
stay byte-identical. Widening, SITUATE/FEED-BACK, the math-serving seam,
and the EVIDENCED reconcile are deferred to their own PRs.

Tests (tests/test_response_governance.py): STRICT-only contract over all
states x license x stakes, STRICT identity, live-wiring proof (forces
APPROXIMATE -> policy-sensitive surface), total+disjoint taxonomy
partition. Each fails loudly under the violation it guards.
This commit is contained in:
Shay 2026-06-03 10:39:12 -07:00
parent 4c095c3621
commit d5872a2e96
6 changed files with 615 additions and 3 deletions

View file

@ -37,6 +37,7 @@ from core.epistemic_state import (
epistemic_state_for_grounding_source,
normative_detail_from_verdicts,
)
from core.response_governance import govern_response, shape_surface
from chat.telemetry import (
TurnEventSink,
format_correction_event_jsonl,
@ -447,6 +448,13 @@ class ChatResponse:
# Comma-separated violated boundary/commitment IDs when normative
# clearance is VIOLATED or SUPPRESSED; empty string otherwise.
normative_detail: str = ""
# ADR-0206 — Response Governance Bridge reach level for this turn.
# Mirrors the TurnEvent field so callers (CLI, demos, tests) can read
# the governed reach level from ChatResponse. Scaffold contract:
# always "strict" (govern_response is STRICT-only until widening is
# built). ``"strict"`` default preserves byte-identity for callers
# that construct ChatResponse without this field.
reach_level: str = "strict"
# ADR-0072 (R5) — operator-visible register identity per turn.
# Mirrors the TurnEvent fields so callers (CLI, demos, tests) can
# read the register state from ChatResponse without re-parsing the
@ -2335,9 +2343,24 @@ class ChatRuntime:
refusal_emitted=refusal_emitted,
hedge_injected=hedge_injected,
)
main_epistemic_state = epistemic_state_for_grounding_source(
main_grounding_source
).value
main_epistemic = epistemic_state_for_grounding_source(main_grounding_source)
main_epistemic_state = main_epistemic.value
# ADR-0206 — Response Governance Bridge seam (cognition path).
# govern_response is STRICT-only (scaffold), so shape_surface is the
# identity transform and response_surface is returned verbatim —
# byte-identical to the pre-bridge path. This is live wiring, not
# dead code: the response surface now flows through the policy
# consumer, and the ONLY thing keeping it strict is the STRICT
# return value (proven by the live-wiring test). The risk-reward
# widening pathway and the math-serving seam are deferred to their
# own PRs (ADR-0206 §5); wrong==0 is untouched here.
main_reach_policy = govern_response(epistemic_state=main_epistemic)
main_reach_level = main_reach_policy.level.value
response_surface = shape_surface(
main_reach_policy,
committed_surface=response_surface,
decode_state=main_epistemic,
)
main_normative_clearance = clearance_from_verdicts(verdicts_bundle).value
main_normative_detail = normative_detail_from_verdicts(verdicts_bundle)
turn_event = TurnEvent(
@ -2371,6 +2394,7 @@ class ChatRuntime:
epistemic_state=main_epistemic_state,
normative_clearance=main_normative_clearance,
normative_detail=main_normative_detail,
reach_level=main_reach_level,
)
self.turn_log.append(turn_event)
self._emit_turn_event(turn_event)
@ -2421,6 +2445,7 @@ class ChatRuntime:
epistemic_state=main_epistemic_state,
normative_clearance=main_normative_clearance,
normative_detail=main_normative_detail,
reach_level=main_reach_level,
refusal_reason=refusal_surface if refusal_emitted else "",
dispatch_trace=dispatch_trace,
)

View file

@ -298,6 +298,15 @@ class TurnEvent:
epistemic_state: str = "undetermined"
normative_clearance: str = "unassessable"
normative_detail: str = ""
# ADR-0206 — Response Governance Bridge reach level for this turn.
# The reach policy that governed the response surface, as a
# lower_snake_case string mirroring core.response_governance.ReachLevel
# without importing that module here (preserving identity.py's
# low-coupling shared-value-type role). Scaffold contract: always
# "strict" — govern_response emits STRICT-only until the risk-reward
# widening loop is built (ADR-0206 §3). Default "strict" so callers
# that omit the field stay byte-identical and conservatively governed.
reach_level: str = "strict"
# ADR-0153 (W-020a) — canonical SHA-256 trace hash for this turn,
# back-stamped by ``CognitiveTurnPipeline.process`` after
# ``compute_trace_hash`` runs. Empty string on construction;

View file

@ -0,0 +1,47 @@
"""ADR-0206 — Response Governance Bridge (scaffold).
The consumption bridge that lets CORE's two *built* epistemic substrates —
the ratified decode-state taxonomy (:mod:`core.epistemic_state`) and the
deterministic risk-reward gate (:mod:`core.reliability_gate`) govern how a
response is shaped. Today that loop has **no consumer** (see ADR-0206 §1);
this package is its first, honestly-inert beginning.
Scaffold contract (ADR-0206 §3):
- :func:`govern_response` always returns :data:`STRICT_POLICY`. The
stakes-weighing / license-gated widening logic is *designed* in the ADR
and intentionally **not built**.
- :func:`shape_surface` is the IDENTITY transform at
:attr:`ReachLevel.STRICT` the only level emitted today so wiring it
into the response path is byte-identical to the pre-bridge path.
The seam is therefore **live wiring held strict by exactly one return
value** (the stub's STRICT), not dead code. ``wrong == 0`` is preserved
because nothing widens: the math serving chokepoint
(``select_self_verified``) is untouched by this PR (deferred to its own
PR per ADR-0206 §5).
"""
from __future__ import annotations
from core.response_governance.policy import (
ACTIVE_STATES,
RECONCILE_STATES,
RESERVED_STATES,
STRICT_POLICY,
ReachLevel,
ReachPolicy,
govern_response,
shape_surface,
)
__all__ = [
"ACTIVE_STATES",
"RECONCILE_STATES",
"RESERVED_STATES",
"STRICT_POLICY",
"ReachLevel",
"ReachPolicy",
"govern_response",
"shape_surface",
]

View file

@ -0,0 +1,186 @@
"""ADR-0206 §2-§3 — the reach policy and its STRICT-only scaffold.
A :class:`ReachPolicy` is the per-response decision about *how far into
uncertainty a response may reach* the thing CORE today does not compute.
The full bridge will derive it from the decode-state of the grounding and
the reliability gate's license (ADR-0206 §1). This module ships only the
**STRICT** end of that spectrum, wired so the seam is live but inert.
Two functions form the seam:
``govern_response``
The decision point. Scaffold: returns :data:`STRICT_POLICY` for every
input. This is the single return value the ``wrong == 0`` guarantee
rests on mutate it and the live-wiring tests diverge loudly.
``shape_surface``
The consumer. At :attr:`ReachLevel.STRICT` it is the IDENTITY
transform (returns the committed surface verbatim). The higher reach
levels are the future widening pathway; they are *unreachable in
production today* because ``govern_response`` never emits them, but
they are real, policy-sensitive code so the seam is not dead.
"""
from __future__ import annotations
from dataclasses import dataclass
from enum import Enum, unique
from core.epistemic_state import EpistemicState
@unique
class ReachLevel(Enum):
"""How far a governed response may reach beyond fully-grounded fact.
Ordered conceptually STRICT < APPROXIMATE < EXTRAPOLATE < CREATIVE.
Only STRICT is emitted by the scaffold; the rest name the spectrum the
bridge will earn the right to enter via the reliability gate.
"""
STRICT = "strict" # commit only fully-grounded; else refuse/disclose (TODAY)
APPROXIMATE = "approximate" # disclosed best-estimate from incomplete evidence
EXTRAPOLATE = "extrapolate" # disclosed inference past the evidence
CREATIVE = "creative" # disclosed imaginative / novel construction
@dataclass(frozen=True, slots=True)
class ReachPolicy:
"""The per-response governance decision.
``admissible_states`` is the set of decode-states whose surface may be
*committed as-is* at this level; states outside it are either refused
(STRICT) or surfaced as a disclosed alternative (higher levels).
``rationale`` and ``license_ratio`` keep the decision inspectable
the same discipline the reliability gate uses for ``LicenseDecision``.
"""
level: ReachLevel
admissible_states: frozenset[EpistemicState]
rationale: str
license_ratio: float = 0.0
# --- Taxonomy scoping (ADR-0206 §4) -----------------------------------------
# The 15 declared EpistemicState members, partitioned by their role in the
# governing loop. This is the "honestly scoped, not half-dead" contract:
# every state is either ACTIVE (produced today AND consumed by the loop),
# RESERVED (named here, not yet produced, each tied to a capability that
# must land first), or RECONCILE (a real drift, fixed in its own PR).
#
# Verified against the source tree on the ADR-0206 branch: exactly 9 states
# are ever produced (DECODED ×6, UNDETERMINED ×3, EPISTEMIC_STATE_NEEDED ×3,
# UNVERIFIED_POSSIBLE/UNVERIFIED_NOVEL/CONTRADICTED/AMBIGUOUS ×2,
# EVIDENCED_INCOMPLETE/INFERRED ×1) and 6 are never produced. Of those 6,
# five are RESERVED and one (EVIDENCED) is RECONCILE.
ACTIVE_STATES: frozenset[EpistemicState] = frozenset(
{
EpistemicState.DECODED, # fully grounded — strict-admissible
EpistemicState.EVIDENCED_INCOMPLETE, # partial grounding — approximate+
EpistemicState.INFERRED, # derived — extrapolate+
EpistemicState.UNVERIFIED_POSSIBLE, # possibility — extrapolate+ (disclosed)
EpistemicState.UNVERIFIED_NOVEL, # OOV — creative only (disclosed)
EpistemicState.CONTRADICTED, # never committed — force refuse
EpistemicState.AMBIGUOUS, # never committed — force refuse/clarify
EpistemicState.UNDETERMINED, # never committed — refuse
EpistemicState.EPISTEMIC_STATE_NEEDED, # sentinel — force situate step
}
)
# Never produced today; each unlocks only when its named capability lands.
RESERVED_STATES: frozenset[EpistemicState] = frozenset(
{
EpistemicState.VERIFIED, # needs canonical-comparison pass (soundness != correctness);
# the ONLY state that will license widening past gold.
EpistemicState.COMPUTATIONALLY_BOUNDED, # search-budget exhausted; search.py is the near-term emitter.
EpistemicState.SCOPE_BOUNDARY, # out-of-domain refusal; needs domain-scope detection.
EpistemicState.PERCEIVED, # raw ingest pre-comprehension; needs a perception lane.
EpistemicState.DECODED_UNARTICULATED, # decoded but no realizer surface; articulation-gap case.
}
)
# Declared here but never produced, while recognition/outcome.py defines its
# OWN "evidenced" constant. Real drift — reconciled in a dedicated PR
# (ADR-0206 §5), kept OUT of this purely-additive scaffold.
RECONCILE_STATES: frozenset[EpistemicState] = frozenset({EpistemicState.EVIDENCED})
# --- The scaffold's single governing decision -------------------------------
# STRICT admits only the fully-grounded decode-state; everything else is
# refused/disclosed by the existing response path. This is informational
# at STRICT (shape_surface short-circuits to identity) but records the
# contract the bridge will enforce once widening is built.
_STRICT_ADMISSIBLE: frozenset[EpistemicState] = frozenset({EpistemicState.DECODED})
STRICT_POLICY: ReachPolicy = ReachPolicy(
level=ReachLevel.STRICT,
admissible_states=_STRICT_ADMISSIBLE,
rationale="scaffold:strict-only (ADR-0206 §3 — risk-reward widening not yet built)",
license_ratio=0.0,
)
# Disclosure prefixes for the (currently unreachable) widening levels. Real
# code so the higher-level branch of shape_surface is genuinely
# policy-sensitive, exercised by the live-wiring test.
_DISCLOSURE_PREFIX: dict[ReachLevel, str] = {
ReachLevel.APPROXIMATE: "[approximate]",
ReachLevel.EXTRAPOLATE: "[extrapolated]",
ReachLevel.CREATIVE: "[exploratory]",
}
def govern_response(
*,
epistemic_state: EpistemicState | None = None,
license_decision: object | None = None,
stakes: object | None = None,
) -> ReachPolicy:
"""Decide the reach policy for a response.
SCAFFOLD (ADR-0206 §3): returns :data:`STRICT_POLICY` unconditionally.
The inputs are accepted now so the call site is the final shape, but
the stakes-weighing and license-gated widening they will drive is
*designed, not built*. Every response is therefore governed at STRICT
commit-only-when-grounded, else the existing refuse/disclose path
which is exactly the pre-bridge behavior.
This single return value is the load-bearing line for ``wrong == 0``:
the live-wiring tests prove that the *only* thing keeping the response
path strict is this STRICT return, not the absence of a consumer.
"""
return STRICT_POLICY
def shape_surface(
policy: ReachPolicy,
*,
committed_surface: str,
decode_state: EpistemicState,
disclosed_alternative: str | None = None,
) -> str:
"""Apply ``policy`` to a committed surface — the response-path consumer.
At :attr:`ReachLevel.STRICT` (the only level :func:`govern_response`
emits today) this is the IDENTITY transform: the committed surface is
returned verbatim, byte-identical to the pre-bridge path. This is why
wiring the seam into :mod:`chat.runtime` changes no behavior.
The higher reach levels are the future widening pathway. They are
real and policy-sensitive a non-admissible decode-state is surfaced
as a *disclosed alternative* rather than committed silently so the
seam is live wiring, not dead code. They are unreachable in production
today only because ``govern_response`` never emits them.
"""
if policy.level is ReachLevel.STRICT:
return committed_surface
# --- Future widening pathway (unreachable in production today) ---
alternative = (
committed_surface if disclosed_alternative is None else disclosed_alternative
)
if decode_state in policy.admissible_states:
return alternative
prefix = _DISCLOSURE_PREFIX.get(policy.level, "")
return f"{prefix} {alternative}".strip() if prefix else alternative

View file

@ -0,0 +1,167 @@
# ADR-0206 — Response Governance Bridge (scaffold)
- **Status:** Accepted (scaffold step; widening deferred)
- **Date:** 2026-06-03
- **Supersedes / relates to:** ADR-0175 (calibrated-learning reliability
gate), the Epistemic-State program (Phase 3, `core/epistemic_state.py`)
- **Scope of THIS PR:** purely additive — design on record + cognition-path
seam, STRICT-only, no behavior change.
## Context — the consumption gap
CORE has two **built** epistemic substrates:
1. **A ratified decode-state taxonomy**`core/epistemic_state.py`, a
`@unique` 15-member `EpistemicState` enum plus an orthogonal
`NormativeClearance` axis. Every chat turn is *labeled* with its
decode-state (derived from the grounding source) and the label is
stamped onto `TurnEvent` / `ChatResponse` and emitted to telemetry.
2. **A deterministic risk-reward gate**`core/reliability_gate/`
(ADR-0175). `license_for(class, action, ceilings)` decides whether a
class has *earned* an action via `measured_reliability / θ_required ≥ 1`.
A source audit (2026-06-03) established the load-bearing fact this ADR
addresses:
> **The decode-state taxonomy is LABEL-ONLY.** No consumer reads a
> decode-state to weigh stakes, modulate response risk, or gate
> approximation/creativity. Every read is a stamp, serialize, badge, or
> filter. The reliability gate is likewise *explicitly not serving-wired*
> (`core/reliability_gate/__init__.py`: "NOT wired into the serving/eval
> path"). The loop that would let these substrates **govern response
> behavior** has zero consumers.
This is the same shape as the ratify-vs-consume gap: the artifacts exist,
the runtime does not read them to act.
## Decision
Introduce the **Response Governance Bridge** — the missing consumer that
reads the two substrates and produces a per-response `ReachPolicy`
governing *how far into uncertainty a response may reach*. This ADR ships
only the **scaffold**: the design on record + a live but inert seam on the
cognition path. The risk-reward widening logic and the math-serving seam
are explicitly deferred (§5).
### §1 — The governing loop (designed; not all built)
```mermaid
flowchart LR
T[turn] --> S[SITUATE<br/>stakes/gravity]
S --> L[LICENSE<br/>reliability_gate.license_for]
L --> G[GOVERN<br/>govern_response → ReachPolicy]
G --> H[SHAPE<br/>shape_surface reads policy]
H --> D[DISCLOSE<br/>decode-state label]
D --> F[FEED BACK<br/>outcome → ledger]
F -. next turn .-> L
```
| Step | Status |
|------|--------|
| SITUATE (stakes/gravity from intent + domain + decode-state) | **designed** — not built |
| LICENSE (`reliability_gate.license_for`) | **built** — not yet called from serving |
| GOVERN (`govern_response → ReachPolicy`) | **this PR** — stubbed to STRICT |
| SHAPE (`shape_surface`, response path reads policy) | **this PR** — STRICT = identity |
| DISCLOSE (decode-state label) | **built** |
| FEED BACK (outcome → reliability ledger) | **designed** — not built |
Only **GOVERN + SHAPE** ship here. SITUATE and FEED-BACK are named and
deferred to follow-ups.
### §2 — The reach policy
`core/response_governance/policy.py` defines:
- `ReachLevel``STRICT < APPROXIMATE < EXTRAPOLATE < CREATIVE`. Only
`STRICT` is emitted today; the rest name the spectrum the bridge will
earn entry to via the reliability gate.
- `ReachPolicy(level, admissible_states, rationale, license_ratio)` — the
inspectable per-response decision (same discipline as
`reliability_gate.LicenseDecision`).
- `govern_response(*, epistemic_state, license_decision, stakes)` — the
decision point. **Scaffold: returns `STRICT_POLICY` for every input.**
- `shape_surface(policy, *, committed_surface, decode_state,
disclosed_alternative)` — the consumer. **At `STRICT` it is the IDENTITY
transform.** Higher levels surface a *disclosed alternative* for
non-admissible decode-states — real, policy-sensitive code so the seam is
live, but unreachable in production because `govern_response` never emits
them.
### §3 — Why STRICT-only scaffold
The scaffold makes the email's "in active development" claim **true the
moment it lands** while guaranteeing `wrong == 0` is untouched:
- `govern_response → STRICT` is the **single load-bearing return**. The
cognition response surface now flows through `shape_surface`, but STRICT
is identity, so the path is **byte-identical** to pre-bridge.
- The seam is **live wiring, not dead code**: `test_seam_is_live_wiring`
forces `APPROXIMATE` and proves the *same consumer the production path
calls* yields a different surface. The cognition path's strictness is
held by exactly the STRICT return — mutate it and the proofs diverge.
- The **math-serving chokepoint (`select_self_verified`) is not touched.**
It is the single most load-bearing line for `wrong == 0`; after the
ProofNode lesson (a merge silently killed a guard) it does not get its
first edit in a scaffold PR. The deferred math seam (§5) carries its own
widening test.
### §4 — Taxonomy scoping (honestly scoped, not half-dead)
Source-verified split of the 15 `EpistemicState` members (2026-06-03,
including the mapping functions in `epistemic_state.py`): **9 produced, 6
never produced.** (The earlier audit's "7 never produced" over-counted —
its grep excluded `epistemic_state.py`, where the partial→`EVIDENCED_INCOMPLETE`
mapping lives. `EVIDENCED_INCOMPLETE` *is* produced; corrected count is 6.)
| Role | Count | States | Consumed by loop as |
|------|-------|--------|---------------------|
| **ACTIVE** (produced + consumed) | 9 | `DECODED`, `EVIDENCED_INCOMPLETE`, `INFERRED`, `UNVERIFIED_POSSIBLE`, `UNVERIFIED_NOVEL`, `CONTRADICTED`, `AMBIGUOUS`, `UNDETERMINED`, `EPISTEMIC_STATE_NEEDED` | strict-admissible / approximate+ / extrapolate+ / creative / force-refuse / sentinel |
| **RESERVED** (named trigger, not produced) | 5 | `VERIFIED`, `COMPUTATIONALLY_BOUNDED`, `SCOPE_BOUNDARY`, `PERCEIVED`, `DECODED_UNARTICULATED` | each gated on a capability that must land first |
| **RECONCILE** (drift, own PR) | 1 | `EVIDENCED` | declared but never produced; `recognition/outcome.py` owns its own `"evidenced"` |
Reserved triggers:
- `VERIFIED` — needs a canonical-comparison pass (the soundness ≠
correctness gap); the **only** state that will license widening past gold.
- `COMPUTATIONALLY_BOUNDED` — search-budget exhausted; `generate/derivation/search.py`
is the near-term emitter.
- `SCOPE_BOUNDARY` — out-of-domain refusal with a reason; needs domain-scope
detection.
- `PERCEIVED` — raw ingest pre-comprehension; needs a perception lane.
- `DECODED_UNARTICULATED` — decoded but no realizer surface; articulation-gap.
The partition is enforced by `test_taxonomy_partition_is_total_and_disjoint`
(total + pairwise-disjoint over the live enum), so adding an enum member
without scoping it fails the build.
### §5 — Deferred (one kind of change per PR)
- **Math-serving seam** — parameterizing `select_self_verified` with a
`ReachPolicy` (STRICT branch byte-identical) + the full math-serving
widening test. Its own PR; smaller blast radius on the wrong==0 organ.
- **`EVIDENCED` reconcile** — map recognition's outcome into
`EpistemicState.EVIDENCED` at the boundary (or document the two axes). A
real corrective defect; kept out of this purely-additive scaffold.
- **JSONL emission of `reach_level`**`reach_level` is carried on
`TurnEvent` / `ChatResponse` but **not** added to the telemetry audit
dict here, so every pinned lane SHA stays byte-identical. Emission +
SHA re-pin is a follow-up (re-pinning a frozen gate is not "additive").
- **SITUATE / FEED-BACK / widening** — the risk-reward logic itself.
## Consequences
- **Honest claim unlocked:** decode-state taxonomy + risk-reward gate =
*built*; the bridge that lets them govern response reach = *in active
development* (committed seam, STRICT-only, design on record, widening
gated behind wrong==0 proofs). Never "live."
- **`wrong == 0` preserved:** STRICT identity + untouched
`select_self_verified`; serving metric stays `3/47/0`.
- **No frozen gate touched:** telemetry JSONL byte-identical → all pinned
lane SHAs green.
## Verification
- `tests/test_response_governance.py` — STRICT-only contract, STRICT
identity, **live-wiring proof (cognition path)**, taxonomy partition.
- Full-surface suite green (doctrine: not smoke).
- `scripts/verify_lane_shas.py` green; `3/47/0` preserved.

View file

@ -0,0 +1,178 @@
"""ADR-0206 — Response Governance Bridge scaffold tests.
These are the schema-defined proof obligations for the scaffold (CLAUDE.md
"Schema-Defined Proof Obligations"). Each test is written so it would
**meaningfully fail** if the property it guards were silently broken:
- ``test_govern_response_is_strict_only`` fails if the stub ever emits a
non-STRICT policy.
- ``test_strict_is_identity`` fails if STRICT stops being byte-identical.
- ``test_seam_is_live_wiring`` fails if ``shape_surface`` stops being
policy-sensitive i.e. if the seam became dead code that ignores its
policy. This is the proof that the cognition seam is live wiring held
strict by exactly the STRICT return, NOT dead code.
- ``test_taxonomy_partition_is_total_and_disjoint`` fails if the 9/5/1
scoping drifts from the actual EpistemicState enum.
No test here imports ``generate.derivation.verify`` the math-serving
chokepoint (``select_self_verified``) is untouched by this PR (ADR-0206 §5).
"""
from __future__ import annotations
import pytest
from core.epistemic_state import EpistemicState
from core.response_governance import (
ACTIVE_STATES,
RECONCILE_STATES,
RESERVED_STATES,
STRICT_POLICY,
ReachLevel,
ReachPolicy,
govern_response,
shape_surface,
)
# A small but representative cross-product of governance inputs. The
# scaffold must return STRICT for ALL of them.
_ALL_STATES = tuple(EpistemicState)
_LICENSE_STANDINS = (None, object(), {"licensed": True}, {"licensed": False})
_STAKES_STANDINS = (None, "high", "low", 0.0, 1.0)
# --- govern_response: STRICT-only contract ----------------------------------
@pytest.mark.parametrize("state", _ALL_STATES)
@pytest.mark.parametrize("license_decision", _LICENSE_STANDINS)
@pytest.mark.parametrize("stakes", _STAKES_STANDINS)
def test_govern_response_is_strict_only(state, license_decision, stakes):
"""The stub governs every input at STRICT — the wrong==0 load-bearing line.
If this fails, the scaffold has begun widening before the risk-reward
loop and its proofs exist; that is the exact regression the STRICT-only
contract forbids.
"""
policy = govern_response(
epistemic_state=state, license_decision=license_decision, stakes=stakes
)
assert policy.level is ReachLevel.STRICT
assert policy is STRICT_POLICY
def test_strict_policy_admits_only_decoded():
"""STRICT's admissible set is exactly {DECODED} — fully-grounded only."""
assert STRICT_POLICY.admissible_states == frozenset({EpistemicState.DECODED})
# --- shape_surface: STRICT identity + live wiring ---------------------------
@pytest.mark.parametrize("state", _ALL_STATES)
def test_strict_is_identity(state):
"""At STRICT, shape_surface returns the committed surface verbatim.
This is the byte-identity guarantee that makes the cognition seam a
no-op. A disclosed_alternative is supplied to prove STRICT ignores it
(it must NOT leak into the surface at STRICT).
"""
committed = f"grounded answer for {state.value}"
out = shape_surface(
STRICT_POLICY,
committed_surface=committed,
decode_state=state,
disclosed_alternative="A DIFFERENT, RISKIER STRING",
)
assert out == committed
def test_seam_is_live_wiring():
"""LIVE-WIRING PROOF (cognition path, no select_self_verified touched).
Force the governor's decision to APPROXIMATE and show the SAME consumer
the production path calls produces a DIFFERENT surface than at STRICT
for the same input. This proves shape_surface is genuinely
policy-sensitive the seam reads its policy and is not dead code and
therefore that the cognition path's strictness is held by exactly the
STRICT return value, nothing else.
"""
committed = "grounded answer"
alternative = "best-effort estimate"
decode_state = EpistemicState.UNVERIFIED_POSSIBLE # not strict-admissible
strict_out = shape_surface(
STRICT_POLICY,
committed_surface=committed,
decode_state=decode_state,
disclosed_alternative=alternative,
)
approximate_policy = ReachPolicy(
level=ReachLevel.APPROXIMATE,
admissible_states=frozenset({EpistemicState.DECODED}),
rationale="test:forced-approximate",
license_ratio=1.0,
)
approx_out = shape_surface(
approximate_policy,
committed_surface=committed,
decode_state=decode_state,
disclosed_alternative=alternative,
)
# The consumer read the policy: STRICT committed verbatim; APPROXIMATE
# surfaced the disclosed alternative with an epistemic prefix.
assert strict_out == committed
assert approx_out != strict_out
assert alternative in approx_out
assert approx_out.startswith("[approximate]")
def test_widening_admissible_state_commits_without_disclosure():
"""A widening level commits an admissible decode-state without a prefix.
Guards the branch that distinguishes "admissible at this level" (commit
the alternative as-is) from "inadmissible" (disclose). Keeps the
future-pathway logic honest and exercised.
"""
widening = ReachPolicy(
level=ReachLevel.APPROXIMATE,
admissible_states=frozenset({EpistemicState.EVIDENCED_INCOMPLETE}),
rationale="test:admissible",
)
out = shape_surface(
widening,
committed_surface="committed",
decode_state=EpistemicState.EVIDENCED_INCOMPLETE,
disclosed_alternative="alt",
)
assert out == "alt" # admitted: no disclosure prefix
# --- Taxonomy scoping (ADR-0206 §4) -----------------------------------------
def test_taxonomy_partition_is_total_and_disjoint():
"""The 9/5/1 partition covers every EpistemicState exactly once.
If a state is added to the enum without being placed in the governing
loop's partition, this fails — keeping the "honestly scoped, not
half-dead" contract enforced rather than asserted.
"""
assert len(ACTIVE_STATES) == 9
assert len(RESERVED_STATES) == 5
assert len(RECONCILE_STATES) == 1
union = ACTIVE_STATES | RESERVED_STATES | RECONCILE_STATES
assert union == set(EpistemicState)
# Pairwise disjoint — no state wears two roles.
assert not (ACTIVE_STATES & RESERVED_STATES)
assert not (ACTIVE_STATES & RECONCILE_STATES)
assert not (RESERVED_STATES & RECONCILE_STATES)
def test_evidenced_is_the_reconcile_state():
"""EVIDENCED is the orphaned drift (recognition owns its own copy)."""
assert RECONCILE_STATES == frozenset({EpistemicState.EVIDENCED})