diff --git a/chat/runtime.py b/chat/runtime.py
index 20bea64c..4071bcb6 100644
--- a/chat/runtime.py
+++ b/chat/runtime.py
@@ -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,
)
diff --git a/core/physics/identity.py b/core/physics/identity.py
index bc261814..d2968be5 100644
--- a/core/physics/identity.py
+++ b/core/physics/identity.py
@@ -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;
diff --git a/core/response_governance/__init__.py b/core/response_governance/__init__.py
new file mode 100644
index 00000000..260ba58b
--- /dev/null
+++ b/core/response_governance/__init__.py
@@ -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",
+]
diff --git a/core/response_governance/policy.py b/core/response_governance/policy.py
new file mode 100644
index 00000000..2b267d8c
--- /dev/null
+++ b/core/response_governance/policy.py
@@ -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
diff --git a/docs/decisions/ADR-0206-response-governance-bridge.md b/docs/decisions/ADR-0206-response-governance-bridge.md
new file mode 100644
index 00000000..2856f5f0
--- /dev/null
+++ b/docs/decisions/ADR-0206-response-governance-bridge.md
@@ -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
stakes/gravity]
+ S --> L[LICENSE
reliability_gate.license_for]
+ L --> G[GOVERN
govern_response → ReachPolicy]
+ G --> H[SHAPE
shape_surface reads policy]
+ H --> D[DISCLOSE
decode-state label]
+ D --> F[FEED BACK
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.
diff --git a/tests/test_response_governance.py b/tests/test_response_governance.py
new file mode 100644
index 00000000..c6817819
--- /dev/null
+++ b/tests/test_response_governance.py
@@ -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})