core/recognition/connector.py
Shay c1e723f185 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-06 09:01:43 -07:00

76 lines
2.4 KiB
Python

"""Connector: EpistemicNode → GraphNode — ADR-0144.
Maps an admitted EpistemicNode's FeatureBundle to a generation-side
GraphNode so the recognition path can feed the articulation planner.
The v1 mapping covers has-relation feature bundles (agent, relation,
count, unit). New proposition types extend the mapping here; unknown
feature layouts raise ValueError so gaps surface explicitly rather than
silently defaulting.
"""
from __future__ import annotations
from generate.graph_planner import GraphNode
from generate.intent import IntentTag
from recognition.carrier import EpistemicNode
from recognition.outcome import EVIDENCED
def epistemic_node_to_graph_node(
node: EpistemicNode,
*,
source_intent: IntentTag,
node_id: str | None = None,
language: str | None = None,
root: str | None = None,
morphology_id: str | None = None,
) -> GraphNode:
"""Derive a generation-side GraphNode from an admitted EpistemicNode.
Raises ``ValueError`` if ``node.recognition_outcome.state != EVIDENCED``.
Feature-bundle → GraphNode mapping (v1, has-relation propositions):
subject ← bundle["agent"].value
predicate ← bundle["relation"].value
obj ← "{count.value} {unit.value}"
Optional depth params allow 3-lang (Hebrew/Greek) morphology/root info
from recognition to flow into the shared PropositionGraph for
comprehension/articulation/reasoning.
"""
outcome = node.recognition_outcome
if outcome.state != EVIDENCED:
raise ValueError(
f"Cannot derive GraphNode from non-EVIDENCED EpistemicNode: "
f"state={outcome.state!r}"
)
bundle = outcome.proposition
assert bundle is not None # invariant: EVIDENCED → proposition not None
agent = bundle.get("agent")
relation = bundle.get("relation")
count = bundle.get("count")
unit = bundle.get("unit")
subject = str(agent.value) if agent is not None else "<unknown-agent>"
predicate = str(relation.value) if relation is not None else "has"
obj = (
f"{count.value} {unit.value}"
if count is not None and unit is not None
else "<pending>"
)
return GraphNode(
node_id=node_id or node.node_id,
subject=subject,
predicate=predicate,
obj=obj,
source_intent=source_intent,
language=language,
root=root,
morphology_id=morphology_id,
)
__all__ = ["epistemic_node_to_graph_node"]