"""L10 — the always-on heartbeat: one continuous life that lives + learns when idle and survives interruption as the SAME life. These exercise the T-experience heartbeat (``chat/always_on.run_continuous``) directly: the engine ticks ``idle_tick`` over uptime with no user turn, the closure invariant holds by construction (read as evidence, never repaired), and a reboot resumes the SAME life with its accumulated heartbeat learning intact (enforced by the strict identity guard). Wrong=0 / no-hot-path-repair are preserved by composition — the heartbeat only proposes (HITL untouched) + consolidates sound session memory. """ from __future__ import annotations import json from chat.always_on import CLOSURE_CEILING, LIVED_LIFE_FILENAME, run_continuous from chat.runtime import ChatRuntime, RuntimeConfig from core.cognition.pipeline import CognitiveTurnPipeline from generate.meaning_graph.reader import comprehend from generate.realize import realize_comprehension, recall_realized from session.context import SessionContext _RESUME_CONFIG = RuntimeConfig( persist_session_state=True, # Shape B+ — the lived field/vault survive reboot consolidate_determinations=True, # Step D — the loop learns from determined facts strict_identity_continuity=True, # a reboot must be the SAME life or refuse (load guard) ) def _stored_members(ctx: SessionContext, subject: str) -> set[str]: """The set of ``b`` for which ``member(subject, b)`` is a STORED realized record (recall, not on-the-fly re-derivation).""" return { f.relation_arguments[1] for f in recall_realized(ctx, subject=subject, predicate="member") } def _seed_continuous_life(runtime: ChatRuntime) -> None: """Seed a consolidatable held self — member(socrates, man) + subset(man, mortal), from which the idle heartbeat DERIVES member(socrates, mortal) — and excite the field so closure is observed, not vacuous.""" ctx = runtime._context realize_comprehension(comprehend("Socrates is a man."), ctx) realize_comprehension(comprehend("All men are mortals."), ctx) # A real cognitive turn excites the field so ``versor_condition`` is observable. CognitiveTurnPipeline(runtime=runtime).run("Socrates is a man.") def test_heartbeat_lives_learns_and_converges(tmp_path) -> None: runtime = ChatRuntime(config=_RESUME_CONFIG, engine_state_path=tmp_path / "engine_state") _seed_continuous_life(runtime) # The derived membership is NOT stored before the heartbeat runs. assert "mortal" not in _stored_members(runtime._context, "socrates") report = run_continuous(runtime, heartbeats=5) assert report.heartbeats == 5 assert report.final_checkpoint_ok assert report.identity # the life carries a content identity (telemetry) # LIVES: closure holds by construction across the uptime, and it is OBSERVED (a real # field exists), not vacuously true. assert report.closure_observed assert report.closure_held observed = [r.versor_condition for r in report.records if r.versor_condition is not None] assert observed and all(vc < CLOSURE_CEILING for vc in observed) # LEARNS while idle: Step-D consolidation derived AND STORED member(socrates, mortal) # during the heartbeat (non-vacuous — it was absent before run_continuous). assert report.total_facts_consolidated >= 1 assert "mortal" in _stored_members(runtime._context, "socrates") # CONVERGES: a saturated life stops churning — the final beat does no work. assert report.records[-1].did_work is False def test_life_survives_interruption_as_the_same_life(tmp_path) -> None: state_dir = tmp_path / "engine_state" runtime = ChatRuntime(config=_RESUME_CONFIG, engine_state_path=state_dir) _seed_continuous_life(runtime) report = run_continuous(runtime, heartbeats=3) # The heartbeat's ACCUMULATED learning before the interruption. assert report.total_facts_consolidated >= 1 assert "mortal" in _stored_members(runtime._context, "socrates") # Interruption: the live runtime is dropped. The next runtime over the SAME engine-state # dir is the reboot. Under strict_identity_continuity, constructing it ENFORCES the # same-life identity guard — a clean construction means the stamped checkpoint identity # matched the recomputed one (the SAME life, not a new one). del runtime resumed = ChatRuntime(config=_RESUME_CONFIG, engine_state_path=state_dir) # The heartbeat's accumulated learning survived the reboot as a STORED record — not # merely re-derivable from the seeds: the consolidated member(socrates, mortal) is # recalled directly. (With consolidation off, this would NOT be stored — non-vacuous.) assert "mortal" in _stored_members(resumed._context, "socrates") def test_persisted_run_is_a_readable_lived_life_surface(tmp_path) -> None: """run_continuous(report_path=...) leaves the workbench a readable, validated surface — the persist seam the Lived Life route reads (engine_state/lived_life.json).""" from workbench.lived_life import lived_life_from_payload state_dir = tmp_path / "engine_state" runtime = ChatRuntime(config=_RESUME_CONFIG, engine_state_path=state_dir) _seed_continuous_life(runtime) report_path = state_dir / LIVED_LIFE_FILENAME report = run_continuous(runtime, heartbeats=4, report_path=report_path) # The artifact exists where the workbench reads it, and round-trips through the # read-model honesty gate (closure_held / totals consistent with the per-beat bytes). assert report_path.exists() raw = json.loads(report_path.read_text(encoding="utf-8")) surface = lived_life_from_payload(raw) assert surface.status == "recorded" assert surface.heartbeats == report.heartbeats == 4 assert surface.closure_observed and surface.closure_held assert surface.total_facts_consolidated == report.total_facts_consolidated >= 1 assert surface.converged is True # the saturated life is at rest by the final beat def test_heartbeat_never_repairs_closure(tmp_path) -> None: """The heartbeat READS versor_condition as evidence; it must never repair the field. A no-op idle life (nothing to learn) keeps closure stable without any mutation.""" runtime = ChatRuntime(config=_RESUME_CONFIG, engine_state_path=tmp_path / "engine_state") _seed_continuous_life(runtime) assert runtime._context.state is not None field_before = runtime._context.state.F.copy() report = run_continuous(runtime, heartbeats=4) # The field is byte-unchanged by the idle heartbeat (no propagation, no repair). assert runtime._context.state is not None assert (runtime._context.state.F == field_before).all() assert report.closure_observed and report.closure_held