core/core/cognition/result.py
Shay 0c75be7c15 feat: integrate 3-core-language depth into PropositionGraph spine for bidirectional unification
- Add LexicalResolution dataclass + resolve_entry() in chat/pack_resolver.py
  that returns language, root, morphology_id, gloss, semantic_domains from
  he/grc/en packs (lru-cached, first-match, full depth support).

- Extend GraphNode (generate/graph_planner.py) with optional language/root/
  morphology_id fields (defaults preserve all call sites). Update as_dict()
  to include them conditionally. ground_graph() now propagates depth.

- Generalize enrichment in core/cognition/pipeline.py:
  - Per-subject resolution map using depth packs.
  - Enrich all matching nodes before ground (subject→node map).
  - Pass depth alongside recalled_words to ground_graph().

- Consume depth on articulation side:
  - realize_semantic() and render_semantic() now accept/use language+root
    for etymological/Logos framing on Hebrew/Greek nodes (e.g. "אמת (Hebrew
    root: א-מ-ן) is defined as..."). English unchanged.

- Enrich oov_geometric_context with node_depths for future geometric
  anti-unification using roots.

- Extend recognition/connector.py to forward depth from EpistemicNode
  paths into GraphNode.

- Add full Hebrew turn test under realizer_grounded_authority flag.
- Update related tests (semantic realizer, OOV context, surface resolution).
- Cleaned legacy type() hack immediately on discovery (hard-stop rule).

All targeted tests green (52+ in slices), broad relevant suite 581 passed.
Invariants preserved: versor only at owned boundaries, exact recall,
immutable updates, no new legacy parsers. 3 pillars upheld.

Work continues tomorrow from this checkpoint.
2026-07-05 20:19:02 -07:00

191 lines
8.8 KiB
Python

"""
CognitiveTurnResult — the complete record of one cognitive turn.
This is the canonical output of CognitiveTurnPipeline.run(). It is
frozen and slot-based so it can be passed safely across module boundaries
without mutation risk.
"""
from __future__ import annotations
from dataclasses import dataclass
from core.cognition.leeway import LeewayRecord
from field.state import FieldState
from generate.articulation import ArticulationPlan
from generate.dialogue import DialogueRole
from generate.graph_planner import ArticulationTarget, PropositionGraph
from generate.intent import DialogueIntent
from generate.proposition import Proposition
from core.physics.identity import IdentityScore
from recognition.carrier import EpistemicGraph
from teaching.correction import CorrectionCandidate
from teaching.review import ReviewedTeachingExample
from teaching.store import PackMutationProposal
from chat.dispatch_trace import DispatchTrace
@dataclass(frozen=True, slots=True)
class CognitiveTurnResult:
"""Full observability record for a single pipeline turn.
Includes the Shadow Coherence Gate evidence (authority_source +
substrate_hazard) so that the migration from hybrid legacy spine to
the unified PropositionGraph substrate is completely inspectable and
replay-diagnosable without ever breaking determinism or the 74 invariants.
"""
# --- input layer ---
input_text: str
input_tokens: tuple[str, ...]
filtered_tokens: tuple[str, ...]
# --- field layer ---
field_state_before: FieldState | None # None on the very first turn
field_state_after: FieldState
# --- understanding / recall layer ---
proposition: Proposition
articulation: ArticulationPlan
# --- output surfaces ---
surface: str # final voiced surface (what the user sees)
walk_surface: str # sentence-assembled walk surface
articulation_surface: str # bare articulation surface before assembly
# --- dialogue ---
dialogue_role: DialogueRole
# --- identity telemetry ---
identity_score: IdentityScore | None
# --- vault / memory ---
vault_hits: int
recall_energy_class: str | None = None
# --- intent / graph telemetry ---
intent: DialogueIntent | None = None
proposition_graph: PropositionGraph | None = None
articulation_target: ArticulationTarget | None = None
# --- teaching loop ---
teaching_candidate: CorrectionCandidate | None = None
reviewed_teaching_example: ReviewedTeachingExample | None = None
pack_mutation_proposal: PackMutationProposal | None = None
# --- inference operators (ADR-0018) ---
# Deterministic serialisation of any typed operator invoked during the
# turn (e.g. transitive_walk over the teaching-store typed-relation
# graph). Empty string when no operator ran. Folded into trace_hash
# so operator invocation is a load-bearing part of replay equality.
operator_invocation: str = ""
# --- forward semantic control evidence (ADR-0023) ---
# ``admissibility_trace`` is the per-transition record produced by
# ``generate()`` (empty tuple when no admissibility ran).
# ``admissibility_trace_hash`` is its canonical SHA-256, folded
# into ``trace_hash`` only when non-empty so pre-ADR-0023 turn
# hashes are byte-preserved.
# ``ratification_outcome`` is the enum value ("ratified" /
# "demoted" / "passthrough") from the field ratifier; empty
# string when no ratification ran.
# ``region_was_unconstrained`` records whether forward semantic
# control was active on this turn — observation only, no
# production fail-closed yet (see ADR-0023 §Out of scope).
admissibility_trace: tuple = ()
admissibility_trace_hash: str = ""
ratification_outcome: str = ""
region_was_unconstrained: bool = True
# --- inner-loop refusal evidence (ADR-0024 Phase 2) ---
# ``refusal_reason`` is the stable string value of a
# ``generate.exhaustion.RefusalReason`` when the walk refused this
# turn, or the empty string otherwise. Empty-string default is
# the contract for "no refusal materialised"; folding into
# trace_hash is gated on non-emptiness so non-refused turns keep
# byte-identical hashes relative to pre-Phase-2 (CLAUDE.md
# determinism invariant). Phase 2 leaves the materialisation site
# in chat/runtime.py untouched per the ADR-0024 Phase 2 scope
# decision — this field exists so the trace contract is already
# in place when a future ADR wires the materialisation path.
refusal_reason: str = ""
# --- recognition / epistemic carrier (ADR-0144) ---
# None when no DerivedRecognizer is attached, when recognition refused,
# or on the very first turn before any recognizer is configured.
# Non-None only when recognition admitted (state == EVIDENCED).
# NOT folded into trace_hash in Phase 1 (observability only).
epistemic_graph: EpistemicGraph | None = None
# --- grounding dispatcher trace (ADR-0142) ---
# None when no dispatch trace is collected or on pre-dispatch-trace turns.
# NOT folded into trace_hash (observability only).
dispatch_trace: DispatchTrace | None = None
# --- compound intent observability (ADR-0089 Phase C1) ---
# Finding 4 (audit 2026-05-20). ``classify_compound_intent`` returns
# multiple parts for inputs like "What is X and how does it relate
# to Y?" but the pipeline still routes only the dominant clause
# through the existing single-intent path. Pre-fix the secondary
# clauses were silently dropped — no observability, no telemetry,
# no trace evidence.
#
# Phase C1 surfaces the dropped clauses here so operators can see
# the lost signal without changing any current behaviour. Phase
# C2 (opt-in, flag-gated) will route the secondary clauses through
# a multi-node graph; that wiring is deliberately scoped to a
# separate PR per ADR-0089 because it widens
# ``compute_trace_hash`` and the surface resolver contract.
#
# Empty tuple == this turn was single-clause OR the compound
# classifier was not consulted; ``len > 0`` == this turn dropped
# secondary clauses that were classified but not routed.
dropped_compound_clauses: tuple[DialogueIntent, ...] = ()
# --- invariant bookkeeping ---
versor_condition: float = 0.0 # must be < 1e-6
trace_hash: str = "" # SHA-256 over deterministic key fields
# --- response-governance leeway evidence (B4; observational, not in trace_hash) ---
leeway: LeewayRecord | None = None
# --- Shadow Coherence Gate / substrate authority (Phase A) ---
# ``authority_source`` is the value from SurfaceResolution.authority:
# "runtime_canonical" | "runtime_pre_decoration" | "runtime" | "realizer" | "substrate_realizer".
# It is the single source of truth for which spine actually spoke.
#
# ``substrate_hazard`` is the machine-readable list of reasons the
# geometric substrate was *not* granted authority on this turn even
# though a PropositionGraph was produced. Populated only on bypass
# paths. Observational (not folded into trace_hash in Phase A) so that
# every existing turn keeps byte-identical hashes while the hazard
# ledger illuminates the exact work remaining for Layers 1-3.
#
# These two fields turn the "Authority Flip Cliff" into a controlled,
# data-driven strangler migration.
authority_source: str = ""
substrate_hazard: tuple[str, ...] = ()
# --- Phase C instrumentation: Geometric Anti-Unification hook for OOV (read-only telemetry) ---
# When an OOV subject is encountered in the context of a PropositionGraph
# (i.e. a "hole" in S-P-[OOV] or similar), this carries the discrete
# structural context (unresolved topology + intent) plus a placeholder
# for exact CGA neighbor probe results (via vault.recall + cga_inner on
# surrounding realized facts).
#
# Today: purely structural (from effective_graph.get_unresolved_topology()
# when grounding_source indicates oov or pending slots on OOV-shaped
# intents). No vault call yet (keeps change atomic + zero side effects).
#
# Future: perform *exact* geometric anti-unification here (sub-graph
# match on conformal space) to propose SPECULATIVE algebraic variable
# or relation type for the hole, without ever affecting user surface,
# trace_hash (observational), or durable state. Must emit SPECULATIVE,
# respect teaching boundary for any promotion.
#
# Pillars: Mechanical Sympathy (cheap structural + optional exact recall),
# Semantic Rigor (exact CGA only, no approx), Third Door (graph structure
# as first-class for inference instead of lexical substring).
#
# Never folded into trace_hash in this phase. Never mutates field/vault.
oov_geometric_context: dict | None = None