"""Tests for ``render_plan`` and the runtime ``discourse_planner`` flag. Step 5 split into two slices: * The pure ``render_plan`` function — deterministic multi-clause surface from a :class:`DiscoursePlan`. * The runtime hook in ``chat/runtime.py`` — gated by ``RuntimeConfig.discourse_planner``, default False (flag off must be byte-identical to the existing single-sentence path; verified separately by the cognition eval). Flag-on integration is exercised on a known cognition-pack lemma so the assertions don't depend on private pack contents — only the shape and structural properties (multi-sentence count, no walk fragment, grounded source) are pinned. """ from __future__ import annotations from core.config import RuntimeConfig from chat.runtime import ChatRuntime from generate.discourse_planner import ( DialogueIntent, DiscourseMove, DiscourseMoveKind, DiscoursePlan, FactSource, GroundedFact, GroundingBundle, IntentTag, Relation, ResponseMode, plan_discourse, render_plan, ) def _intent() -> DialogueIntent: return DialogueIntent(tag=IntentTag.DEFINITION, subject="truth") def _full_bundle() -> GroundingBundle: return GroundingBundle( facts=( GroundedFact( subject="truth", predicate="is_defined_as", obj="that which corresponds to reality", source=FactSource.PACK, source_id="en_core_cognition_v1:truth#gloss", ), GroundedFact( subject="truth", predicate="belongs_to", obj="epistemic_domain", source=FactSource.PACK, source_id="en_core_cognition_v1:truth#domain:0", ), GroundedFact( subject="truth", predicate="reveals", obj="knowledge", source=FactSource.TEACHING, source_id="cognition_chains_v1#cause_truth_reveals_knowledge", ), GroundedFact( subject="knowledge", predicate="requires", obj="evidence", source=FactSource.TEACHING, source_id="cognition_chains_v1#cause_knowledge_requires_evidence", ), ) ) # --------------------------------------------------------------------------- # render_plan # --------------------------------------------------------------------------- class TestRenderPlan: def test_empty_plan_renders_empty(self) -> None: plan = DiscoursePlan(intent=_intent(), mode=ResponseMode.PARAGRAPH) assert render_plan(plan) == "" def test_brief_renders_single_sentence(self) -> None: plan = plan_discourse(_intent(), ResponseMode.BRIEF, _full_bundle()) rendered = render_plan(plan) assert rendered.count(".") == 1 assert rendered.endswith(".") def test_paragraph_renders_multi_sentence(self) -> None: plan = plan_discourse(_intent(), ResponseMode.PARAGRAPH, _full_bundle()) rendered = render_plan(plan) # PARAGRAPH plan has 5 moves but CLOSURE has no fact, so 4 clauses. assert rendered.count(".") >= 2 def test_paragraph_uses_canonical_connectives(self) -> None: plan = plan_discourse(_intent(), ResponseMode.PARAGRAPH, _full_bundle()) rendered = render_plan(plan) # SUPPORT and RELATION clauses use fixed connectives. assert "Furthermore," in rendered assert "In turn," in rendered def test_paragraph_transition_uses_consequently(self) -> None: plan = plan_discourse(_intent(), ResponseMode.PARAGRAPH, _full_bundle()) rendered = render_plan(plan) assert "Consequently," in rendered def test_render_is_deterministic(self) -> None: plan = plan_discourse(_intent(), ResponseMode.PARAGRAPH, _full_bundle()) a = render_plan(plan) b = render_plan(plan) assert a == b def test_clause_uses_verbatim_fact_object(self) -> None: # No synthesis: every fact's obj must appear verbatim in output. plan = plan_discourse(_intent(), ResponseMode.PARAGRAPH, _full_bundle()) rendered = render_plan(plan) for move in plan.moves: if move.fact is None: continue assert move.fact.obj in rendered def test_anchor_uses_is_for_is_defined_as(self) -> None: # is_defined_as collapses to natural "is" connective. plan = DiscoursePlan( intent=_intent(), mode=ResponseMode.BRIEF, moves=( DiscourseMove( kind=DiscourseMoveKind.ANCHOR, topic="truth", new=("truth",), fact=GroundedFact( subject="truth", predicate="is_defined_as", obj="reality-correspondence", source=FactSource.PACK, source_id="en_core_cognition_v1:truth#gloss", ), ), ), ) rendered = render_plan(plan) assert "Truth is reality-correspondence." == rendered def test_closure_without_fact_is_skipped(self) -> None: plan = DiscoursePlan( intent=_intent(), mode=ResponseMode.PARAGRAPH, moves=( DiscourseMove( kind=DiscourseMoveKind.ANCHOR, topic="truth", new=("truth",), fact=GroundedFact( subject="truth", predicate="is_defined_as", obj="reality", source=FactSource.PACK, source_id="en_core_cognition_v1:truth#gloss", ), ), DiscourseMove( kind=DiscourseMoveKind.CLOSURE, topic="truth", given=("truth",), relation_to_previous=Relation.ELABORATION, fact=None, ), ), ) rendered = render_plan(plan) assert rendered == "Truth is reality." # --------------------------------------------------------------------------- # Runtime flag — default off # --------------------------------------------------------------------------- class TestRuntimeFlagDefault: def test_default_runtime_config_has_flag_on(self) -> None: """Default flipped to True 2026-05-21 after the cognition eval (45 cases) was confirmed byte-identical OFF vs ON across both surface and trace_hash projections — single-fact prompts get the same output either way; the flag only differentiates NARRATIVE / EXAMPLE / PARAGRAPH / EXPLAIN / compound shapes. """ cfg = RuntimeConfig() assert cfg.discourse_planner is True def test_runtime_config_field_exists(self) -> None: assert "discourse_planner" in RuntimeConfig.__dataclass_fields__ # --------------------------------------------------------------------------- # Runtime flag — on path engages on pack-grounded EXPLAIN/PARAGRAPH # --------------------------------------------------------------------------- class TestRuntimeFlagOn: def test_flag_on_lifts_multi_sentence_on_known_pack_lemma(self) -> None: cfg = RuntimeConfig(discourse_planner=True) runtime = ChatRuntime(config=cfg) response = runtime.chat("Explain truth") # When the planner engages, the surface contains a connective # from the canonical table. When it doesn't (e.g. truth has no # qualifying teaching chain in the live corpus), the test # documents that fact rather than failing: lift is conditional # on substrate availability. if "Furthermore," in response.surface or "In turn," in response.surface: assert response.surface.count(".") >= 2 def test_flag_off_explicit_path_unchanged(self) -> None: """When the operator explicitly disables the planner the surface must remain in the existing single-sentence shape — no planner connectives. This pins the OFF path even though the default flipped to ON in 2026-05-21. """ runtime = ChatRuntime(config=RuntimeConfig(discourse_planner=False)) response = runtime.chat("Explain truth") assert "Furthermore," not in response.surface assert "Consequently," not in response.surface