"""DETERMINE (roadmap Step 4) — reason over realized structure → the honest gear. A question (a query-bearing ``Comprehension``) is answered ONLY from what the held self has already REALIZED (R0/R1 structural recall) — never from the field, never from an LLM, never from absence. The verdict is **as-told, never "verified"**: every realizable record is SPECULATIVE, and ``ADMISSIBLE_AS_EVIDENCE = {COHERENT}``, so a determination grounded in SPECULATIVE records carries ``basis="as_told"`` — "based on what I was told (unverified)". Until COHERENT promotion exists (out of scope), D0 produces only ``as_told`` assertions or ``Undetermined`` refusals. No estimation, no corpus mutation (teaching stays HITL proposal-only). wrong=0 / soundness (open-world): D0 asserts an answer ONLY when the asked relation is DIRECTLY entailed by a realized fact. Absence of a fact never refutes it (open-world), so D0 never asserts an answer from missing knowledge — it refuses (``Undetermined``). It asserts only ``answer=True`` on a direct hit; it never asserts False. Supported predicates are a CLOSED set for which DIRECT entailment is sound — a realized ground fact ``p(subject, target)`` answers the asked ``p(subject, target)``: the ``member`` relation (subsumption / "Is X a Y?") and the binary relational predicates of ``en_core_relational_predicates_v1`` (``parent_of``, ``less_than``, ``left_of``, ``before_event`` …; see ``generate.meaning_graph.relational``). The categorical (``subset``/``disjoint`` …) and propositional (``implies``/``or`` …) predicates are deliberately EXCLUDED — their truth is not a stored-pair lookup, so admitting them would be unsound. Negated questions, symmetric-converse questions, and any predicate outside the closed set are an honest ``Undetermined`` — D0 ships no entailment path it cannot exercise (no transitive/symmetric/rule inference). """ from __future__ import annotations from dataclasses import dataclass from generate.meaning_graph.reader import Comprehension, Refusal from generate.meaning_graph.relational import RELATIONAL_PREDICATES from generate.realize import RealizedRecord, recall_realized from session.context import SessionContext from teaching.epistemic import ADMISSIBLE_AS_EVIDENCE, EpistemicStatus #: The CLOSED set of query predicates D0 has a SOUND direct-entailment path for: #: ``member`` plus the ground binary relational predicates. Direct entailment is #: "a realized fact ``p(s, t)`` directly answers the asked ``p(s, t)``" — sound for #: these ground relations, unsound for categorical/propositional predicates (excluded). _SUPPORTED_PREDICATES = frozenset({"member"}) | RELATIONAL_PREDICATES @dataclass(frozen=True, slots=True) class Determined: """An answer reasoned from realized structure. ``basis`` is the epistemic standing of the grounding: ``"as_told"`` when the grounds are SPECULATIVE (candidate memory — the only case today), ``"verified"`` only if every ground is admissible-as-evidence (COHERENT — not yet reachable). ``answer`` is the truth of the asked (possibly negated) question. """ answer: bool basis: str predicate: str subject: str object: str grounds: tuple[RealizedRecord, ...] @dataclass(frozen=True, slots=True) class Undetermined: """No honest answer (refusal). ``reason`` is for audit, not control.""" reason: str def _basis(grounds: tuple[RealizedRecord, ...]) -> str: """Carry the grounds' epistemic standing forward — never overclaim "verified".""" statuses = {EpistemicStatus(g.epistemic_status) for g in grounds} return "verified" if statuses and statuses <= ADMISSIBLE_AS_EVIDENCE else "as_told" def determine( question: Comprehension | Refusal, ctx: SessionContext ) -> Determined | Undetermined: """Answer a membership-or-relational question from realized structure, or refuse. Eligibility: a query-bearing ``Comprehension`` with exactly one binary, non-negated query whose predicate is in the closed direct-entailment set (``member`` or a relational pack predicate). The answer is asserted ONLY on direct structural entailment by a realized fact of the SAME predicate; everything else is a typed ``Undetermined`` (open-world: absence never asserts a positive answer). """ if isinstance(question, Refusal): return Undetermined("refusal") if not isinstance(question, Comprehension): return Undetermined("not_a_comprehension") if len(question.queries) != 1: return Undetermined("not_single_query") # a determination answers one question query = question.queries[0] if query.predicate not in _SUPPORTED_PREDICATES: return Undetermined("unsupported_query") # honest: closed direct-entailment set if len(query.arguments) != 2: return Undetermined("malformed_query") # member is binary by construction if query.negated: # Realized facts are all positive (R0/R1 refuse negated relations), so a # negated question would only ever be answered from the positive's presence. # D0 declines it explicitly rather than ship an entailment path the reader # cannot exercise (it refuses negated membership questions upstream anyway). return Undetermined("negated_query_unsupported") predicate = query.predicate subject, target = query.arguments[0], query.arguments[1] # Structural recall: the realized facts of this predicate about this subject (R1a). # Exact, deterministic, versor-collision-irrelevant — never a metric call. facts = recall_realized(ctx, subject=subject, predicate=predicate) if not facts: return Undetermined("ungrounded") # nothing realized about the subject # Direct entailment: a realized p(subject, target) holds as-told. Open-world: # facts about the subject that do NOT match the asked target never refute it, so a # miss is a refusal (``not_entailed``), never an asserted False. Symmetric relations # (sibling_of, equal_to …) read only the stored direction here — the converse is a # sound-but-incomplete ``not_entailed``, never a fabricated assertion. grounding = next((f for f in facts if f.relation_arguments == (subject, target)), None) if grounding is None: return Undetermined("not_entailed") return Determined( answer=True, basis=_basis((grounding,)), predicate=predicate, subject=subject, object=target, grounds=(grounding,), )