core/generate/contemplation/findings.py
Shay f88e03e53a feat(epistemic): Q1-D — off-serving ASK delivery (QUESTION_NEEDED tenant)
The fourth and final Q1 rung: route the Q1-C rendered question onto the
contemplation bus as the QUESTION_NEEDED tenant (sibling of PROPOSAL_EMITTED),
off-serving. Consumes render_question VERBATIM — Q1-D constructs no prose, so the
Q1-C grounded-rendering wrong=0 guard is never bypassed by a second surface.

- generate/contemplation/findings.py: add Terminal.QUESTION_NEEDED.
- core/epistemic_disclosure/limitation.py: ask_question's home terminal is now
  QUESTION_NEEDED, making terminal_for_action total (all six actions map onto
  shipped terminals — the consolidation proof is now complete, not 'five of six').
- core/epistemic_questions/delivery.py: DeliveredQuestion (proposal-only artifact,
  cannot wrap an unrenderable question / cannot be served / answer_binding reserved
  None — illegal states unrepresentable), DeliveryOutcome, deliver_ask, the
  teaching/questions sink emitter, and AnswerBinding (reserved for Q2).

Rulings honored:
- D1: off-serving only — no served surface, no ask_serving_enabled, no chat wiring.
- D2: an unrenderable ASK falls back to the family's standing disposition
  (PROPOSAL_EMITTED if it still proposes, else NO_PROGRESS) — never a contentless
  QUESTION_NEEDED. Enforced twice: in deliver_ask and structurally in __post_init__.
- D3: Q1B_ASK_CARVE_OUT untouched — registry keeps proposal_allowed=True; both
  signals coexist off-serving so no operational signal is lost.
- D4: sink under teaching/questions/ (sibling of teaching/proposals/).
- D5: strictly single-slot — multi-slot is unrenderable, takes the D2 fallback.

Tests: 16 delivery tests (happy path verbatim, both D2 fallback branches,
structural guards, idempotent sink, off-serving AST) + updated the two limitation
consolidation tests for the now-total terminal map. smoke 90/0, affected suites
128/0.
2026-06-08 19:10:54 -07:00

40 lines
1.5 KiB
Python

"""Typed terminal states + per-pass findings for the contemplation pass manager (N6).
v0 is a single bounded pass chain — no loops, no daemon, no L10 runtime. Each pass appends a
``Finding`` (a traceable artifact, not hidden chain-of-thought), and the chain ends in exactly
one ``Terminal`` state.
"""
from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
class Terminal(str, Enum):
"""The terminal state of one bounded contemplation pass."""
SOLVED_VERIFIED = "SOLVED_VERIFIED"
REFUSED_KNOWN_BOUNDARY = "REFUSED_KNOWN_BOUNDARY"
REFUSED_UNSUPPORTED_FAMILY = "REFUSED_UNSUPPORTED_FAMILY"
CONTRADICTION_DETECTED = "CONTRADICTION_DETECTED"
PROPOSAL_EMITTED = "PROPOSAL_EMITTED"
# The ASK tenant (Q1-D): a solvable attempt blocked on missing/ambiguous *input*
# that a grounded question can intake. A SIBLING of PROPOSAL_EMITTED, never a
# subtype — a proposal offers a capability for review; a question requests a datum
# from the user. Off-serving: produced by the Q1-D delivery layer into the
# teaching/questions sink, never served until a future ask_serving_enabled gate.
QUESTION_NEEDED = "QUESTION_NEEDED"
AMBIGUOUS_ORGAN = "AMBIGUOUS_ORGAN"
NO_PROGRESS = "NO_PROGRESS"
@dataclass(frozen=True, slots=True)
class Finding:
"""One pass's typed result — name of the pass and a short summary of what it observed."""
pass_name: str
summary: str
__all__ = ["Finding", "Terminal"]