From 3cdc4faa5f4b35dabaa7fa2a434402340cf527f6 Mon Sep 17 00:00:00 2001 From: Shay Date: Sat, 6 Jun 2026 10:40:20 -0700 Subject: [PATCH] =?UTF-8?q?feat(runtime):=20inline=20realization=20?= =?UTF-8?q?=E2=80=94=20the=20conversation=20accrues=20knowledge=20(Step=20?= =?UTF-8?q?B-1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wires comprehend->realize/determine into the live turn loop: a declarative turn REALIZES a fact into the held self (session vault, SPECULATIVE/as-told); a question turn is DETERMINED over realized knowledge (answered as-told, or refused open-world). The first time a CORE conversation actually accumulates knowledge it can recall and reason over — the 'one continuous life' telos made real, including across reboot (the accrued fact is in the Shape B+ snapshot, so it survives the process ending). Seam: all chat() returns funnel through _checkpointed_response; accrual runs there BEFORE the checkpoint (so the fact persists this turn), gated by a new accrue_realized_knowledge flag (default OFF — one-shot/eval runtimes don't accrue; the production L10 process enables it with persist_session_state). Discipline: SESSION memory, not ratified learning — proposes nothing, HITL untouched. Realization writes go through generate.realize (INV-21 allowed writer). Additive: a failure is a clean no-op, never crashes a turn. Slice B-1 RECORDS the outcome (last_turn_accrual(), introspectable) and leaves the ChatResponse/surface contract UNCHANGED; surfacing the determination is the B-2 follow-up (needs runtime_contracts + contract-test updates). Tests: declarative accrues; question determines as_told; untold refuses open-world; idempotent retell not recreated; flag-off no-ops with identical surface; and the lived-spine proof — accrued knowledge survives reboot (determined as_told after a fresh runtime over the same checkpoint). --- chat/runtime.py | 83 +++++++++++++++++++ core/config.py | 11 +++ tests/test_inline_realization.py | 134 +++++++++++++++++++++++++++++++ 3 files changed, 228 insertions(+) create mode 100644 tests/test_inline_realization.py diff --git a/chat/runtime.py b/chat/runtime.py index 2f098a25..86e261a8 100644 --- a/chat/runtime.py +++ b/chat/runtime.py @@ -388,6 +388,22 @@ def _make_trajectory_from_result(result, turn: int): return operator.build(frames, trajectory_id=f"turn_{turn}") +@dataclass(frozen=True, slots=True) +class TurnAccrual: + """The inline-realization outcome of one turn (Step B). + + ``kind`` is ``"realized"`` (a declarative fact was accrued into the held self), + ``"determined"`` (a question was answered over realized knowledge), or ``"none"`` + (nothing comprehensible to accrue/determine). The payload carries the typed + realize/determine result for introspection. This is recorded, not surfaced — + slice B-1 leaves the ChatResponse/surface contract unchanged. + """ + + kind: str + realized: Any = None # generate.realize.Realized | NotRealized | None + determination: Any = None # generate.determine.Determined | Undetermined | None + + @dataclass(frozen=True, slots=True) class ChatResponse: surface: str @@ -672,6 +688,11 @@ class ChatRuntime: # W-013 — last classified intent, updated each turn for /explain REPL use. self._last_intent: Any | None = None self._last_input_text: str = "" + # Step B (inline realization) — the last turn's accrual outcome (what the turn + # realized into the held self, or determined over it). Introspectable; the + # surface contract is unchanged (slice B-1 records, does not surface). + self._last_turn_accrual: TurnAccrual | None = None + self._relational_pack_lemmas: frozenset[str] | None = None self._engine_state_store: EngineStateStore | None = ( None if no_load_state else EngineStateStore(engine_state_path) ) @@ -923,9 +944,71 @@ class ChatRuntime: def _checkpointed_response(self, response: ChatResponse) -> ChatResponse: self._turn_count += 1 + # Step B — inline realization, BEFORE the checkpoint so an accrued fact is in + # the snapshot this turn (survives reboot with persist_session_state). Gated; + # off by default. The surface contract is unchanged (the outcome is recorded, + # not surfaced). + if self.config.accrue_realized_knowledge: + self._accrue_in_turn(self._last_input_text) self.checkpoint_engine_state() return response + def last_turn_accrual(self) -> TurnAccrual | None: + """The most recent turn's inline-realization outcome (Step B), or None when + accrual is off or did not complete. Introspection only — never surfaced.""" + return self._last_turn_accrual + + def _accrue_in_turn(self, text: str) -> None: + """Inline realization (Step B): a comprehensible declarative turn accrues a + realized fact into the held self (session vault, SPECULATIVE / as-told); a + comprehensible question turn is determined over realized knowledge. Records the + typed outcome on ``self._last_turn_accrual``. + + Never raises into the turn — accrual is ADDITIVE, so any failure is a clean + no-op (the turn's response is untouched). This is SESSION memory (immediate), + NOT ratified learning: it proposes nothing and leaves the teaching/review HITL + path untouched. Realization writes go through ``generate.realize`` (the INV-21 + allowed vault writer); DETERMINE and the readers are total (typed results, no + raises), so the broad guard is a defensive backstop, not expected control flow. + """ + self._last_turn_accrual = None + if self._context is None or not text or not text.strip(): + return + try: + from generate.determine import determine + from generate.meaning_graph.reader import Comprehension, comprehend + from generate.meaning_graph.relational import comprehend_relational + from generate.realize import realize_comprehension + + if self._relational_pack_lemmas is None: + from generate.meaning_graph.relational import load_relational_pack_lemmas + + self._relational_pack_lemmas = load_relational_pack_lemmas() + + readings = ( + comprehend(text), + comprehend_relational(text, self._relational_pack_lemmas), + ) + comprehensions = [c for c in readings if isinstance(c, Comprehension)] + + # A question turn (query-bearing) is DETERMINED over realized knowledge. + for c in comprehensions: + if c.queries: + self._last_turn_accrual = TurnAccrual( + kind="determined", determination=determine(c, self._context) + ) + return + # A declarative turn (a single told fact) is REALIZED into the held self. + for c in comprehensions: + if not c.queries and c.meaning_graph.relations: + self._last_turn_accrual = TurnAccrual( + kind="realized", realized=realize_comprehension(c, self._context) + ) + return + self._last_turn_accrual = TurnAccrual(kind="none") + except Exception: # additive: accrual must never crash a turn # noqa: BLE001 + self._last_turn_accrual = None + @property def session(self) -> SessionContext: return self._context diff --git a/core/config.py b/core/config.py index 1d191967..18dfda1f 100644 --- a/core/config.py +++ b/core/config.py @@ -292,6 +292,17 @@ class RuntimeConfig: # wanting a hard identity-continuity guarantee opt in. strict_identity_continuity: bool = False + # Step B (inline realization) — when on, each turn ACCRUES knowledge into the + # held self: a comprehensible declarative turn is realized into the session vault + # (SPECULATIVE, as-told), and a comprehensible question turn is determined over + # realized knowledge. This is the "one continuous life" telos made real — a + # conversation accumulates knowledge it can recall and reason over. OFF by default + # (one-shot/eval runtimes don't accrue); the production L10 process enables it + # alongside persist_session_state so accrued facts survive reboot. Realization is + # SESSION memory (immediate), NOT ratified learning — it proposes nothing; the + # teaching/review HITL path is untouched. + accrue_realized_knowledge: bool = False + DEFAULT_IDENTITY_PACK: str = "default_general_v1" DEFAULT_ETHICS_PACK: str = "default_general_ethics_v1" diff --git a/tests/test_inline_realization.py b/tests/test_inline_realization.py new file mode 100644 index 00000000..6a98e673 --- /dev/null +++ b/tests/test_inline_realization.py @@ -0,0 +1,134 @@ +"""Step B — inline realization in the live turn loop. + +A conversation ACCRUES knowledge: a declarative turn realizes a fact into the held +self; a question turn is determined over realized knowledge — answered as-told, or +refused (open-world). Gated by ``accrue_realized_knowledge``; off by default leaves the +surface contract and behavior unchanged. With persistence on, the accrued fact survives +reboot — the "one continuous life" made real across an interruption. +""" + +from __future__ import annotations + +from dataclasses import replace + +import pytest + +from chat.runtime import ChatRuntime +from core.config import RuntimeConfig + + +def _rt(*, accrue: bool, persist: bool = False, engine_state_path=None) -> ChatRuntime: + config = replace( + RuntimeConfig(), + accrue_realized_knowledge=accrue, + persist_session_state=persist, + ) + return ChatRuntime(config=config, engine_state_path=engine_state_path) + + +# --------------------------------------------------------------------------- # +# Accrual: the conversation accumulates knowledge it can reason over +# --------------------------------------------------------------------------- # + + +def test_declarative_turn_accrues_a_realized_fact() -> None: + rt = _rt(accrue=True) + rt.chat("Truth is a concept.") + accrual = rt.last_turn_accrual() + assert accrual is not None and accrual.kind == "realized" + assert accrual.realized.created is True + + +def test_question_turn_is_determined_from_accrued_knowledge() -> None: + rt = _rt(accrue=True) + rt.chat("Truth is a concept.") + rt.chat("Is truth a concept?") + accrual = rt.last_turn_accrual() + assert accrual is not None and accrual.kind == "determined" + det = accrual.determination + assert det.answer is True + assert det.basis == "as_told" # SPECULATIVE session memory, never "verified" + + +def test_untold_question_refuses_open_world() -> None: + rt = _rt(accrue=True) + rt.chat("Truth is a concept.") + rt.chat("Is truth a virtue?") # never told + det = rt.last_turn_accrual().determination + # Undetermined, never a fabricated answer or an asserted False (open-world). + assert type(det).__name__ == "Undetermined" + assert det.reason in {"not_entailed", "ungrounded"} + + +def test_idempotent_retell_is_not_recreated() -> None: + rt = _rt(accrue=True) + rt.chat("Truth is a concept.") + assert rt.last_turn_accrual().realized.created is True + rt.chat("Truth is a concept.") # same fact again + assert rt.last_turn_accrual().realized.created is False # structural dedup + + +# --------------------------------------------------------------------------- # +# Off by default: no behavior change, surface contract intact +# --------------------------------------------------------------------------- # + + +def test_flag_off_does_not_accrue_and_keeps_surface() -> None: + rt = _rt(accrue=False) + response = rt.chat("Truth is a concept.") + assert rt.last_turn_accrual() is None + # the turn still returns a real surface (no behavior change when the flag is off) + assert isinstance(response.surface, str) and response.surface + + +def test_accrual_does_not_alter_the_surface() -> None: + # The same input yields the same surface with accrual on vs off (B-1 records, + # does not surface — the surface contract is untouched). + off = _rt(accrue=False).chat("Truth is a concept.") + on = _rt(accrue=True).chat("Truth is a concept.") + assert on.surface == off.surface + assert on.articulation_surface == off.articulation_surface + + +# --------------------------------------------------------------------------- # +# One continuous life: accrued knowledge survives reboot +# --------------------------------------------------------------------------- # + + +def test_accrued_knowledge_survives_reboot(tmp_path) -> None: + esp = tmp_path / "engine_state" + # life 1: tell a fact, then the process ends + rt1 = _rt(accrue=True, persist=True, engine_state_path=esp) + rt1.chat("Truth is a concept.") + assert rt1.last_turn_accrual().kind == "realized" + + # reboot: a NEW runtime over the SAME checkpoint resumes the SAME life + rt2 = _rt(accrue=True, persist=True, engine_state_path=esp) + rt2.chat("Is truth a concept?") + det = rt2.last_turn_accrual().determination + assert det.answer is True and det.basis == "as_told" # remembered across the reboot + + +def test_no_accrual_persistence_without_persist_flag(tmp_path) -> None: + esp = tmp_path / "engine_state" + rt1 = _rt(accrue=True, persist=False, engine_state_path=esp) + rt1.chat("Truth is a concept.") + # without persistence the fact accrues only in-session; a reboot does not recall it + rt2 = _rt(accrue=True, persist=False, engine_state_path=esp) + rt2.chat("Is truth a concept?") + det = rt2.last_turn_accrual().determination + assert type(det).__name__ == "Undetermined" + + +# --------------------------------------------------------------------------- # +# Robustness: accrual is additive, never crashes a turn +# --------------------------------------------------------------------------- # + + +def test_uncomprehended_turn_is_a_clean_noop() -> None: + rt = _rt(accrue=True) + response = rt.chat("Hello there friend.") # no fact/question to accrue + assert response.surface # the turn still returns normally + accrual = rt.last_turn_accrual() + # either nothing comprehensible (kind="none") or a refusal that realized nothing + assert accrual is None or accrual.kind in {"none", "realized", "determined"}