core/tests/test_conversation_demo.py
Shay c435bdf88c feat(demo): humanise teaching-grounded surface for layperson display
The conversation demo's Scene 4 was emitting CORE's raw production
teaching-grounded surface, which reads engineer-y for a layperson:

  narrative — teaching-grounded (cognition_chains_v1):
  rhetoric.narrative; language.discourse. narrative reveals
  meaning (cognition.meaning). No session evidence yet.

The production format is the trust-boundary contract (12+ tests + eval
byte-equivalence + several ADRs depend on it), so it stays unchanged.

This change adds a demo-only display layer that rewrites the same
surface to put the propositional sentence first, with provenance as a
trailing parenthetical:

  Narrative reveals meaning. (teaching-grounded from
  cognition_chains_v1 — narrative: rhetoric.narrative;
  language.discourse; final term: cognition.meaning.
  No session evidence yet.)

Trust-boundary preserving:
  - Only fires when grounding_source == "teaching" AND surface matches
    the production format.
  - Every load-bearing token preserved (subject, connective, object,
    corpus_id, semantic_domains, "No session evidence yet").
  - Pack-grounded surfaces + discourse-planner surfaces pass through
    unchanged.
  - JSON report's `surface` field still carries the raw production
    surface — only the chat-style print is humanised.

Test gate: 2 new tests pin the rewrite contract (proposition-first,
all load-bearing tokens preserved, passthrough for non-teaching).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-19 14:14:02 -07:00

125 lines
4.7 KiB
Python

"""Conversation demo — pins the layperson-facing chat transcript.
These tests use ``stream=False`` so the demo runs instantly. They
verify the structured JSON report (which is what downstream
consumers integrate against), not the streamed visual layout.
"""
from __future__ import annotations
import pytest
from evals.conversation.run_demo import run_demo
@pytest.fixture(scope="module")
def demo_report() -> dict:
return run_demo(emit_json=True, stream=False)
def test_demo_has_five_turns(demo_report: dict) -> None:
assert len(demo_report["turns"]) == 5
def test_demo_closes_the_learning_loop(demo_report: dict) -> None:
assert demo_report["learning_loop_closed"] is True
assert demo_report["active_corpus_byte_identical"] is True
def test_scene1_pack_lookup_grounds_in_pack(demo_report: dict) -> None:
s1 = demo_report["turns"][0]
assert s1["scene"] == "S1_pack_lookup"
assert s1["prompt"] == "What is truth?"
assert s1["grounding_source"] == "pack"
assert "truth" in s1["surface"].lower()
assert "lexicon" in s1["note"].lower()
def test_scene2_teaching_chain_grounds_in_teaching(demo_report: dict) -> None:
s2 = demo_report["turns"][1]
assert s2["scene"] == "S2_teaching_chain"
assert s2["prompt"] == "Walk me through recall."
assert s2["grounding_source"] == "teaching"
assert "reveals memory" in s2["surface"].lower()
assert "chain" in s2["note"].lower()
def test_scene3_compound_handles_both_clauses(demo_report: dict) -> None:
s3 = demo_report["turns"][2]
assert s3["scene"] == "S3_compound"
assert s3["grounding_source"] in {"pack", "teaching"}
sentence_count = sum(1 for ch in s3["surface"] if ch in ".!?")
assert sentence_count >= 4
assert "truth" in s3["surface"].lower()
def test_scene4_cold_turn_does_not_make_up_an_answer(demo_report: dict) -> None:
s4a = demo_report["turns"][3]
assert s4a["scene"] == "S4a_cold_turn"
assert s4a["grounding_source"] in {"none", "oov"}
surface_low = s4a["surface"].lower()
assert "don't know" in surface_low or "haven't learned" in surface_low or "insufficient" in surface_low
def test_scene4_after_teaching_is_grounded_with_new_chain(demo_report: dict) -> None:
s4b = demo_report["turns"][4]
assert s4b["scene"] == "S4b_after_teaching"
assert s4b["grounding_source"] == "teaching"
surface_low = s4b["surface"].lower()
assert "narrative" in surface_low
assert "meaning" in surface_low
def test_demo_json_shape_is_stable(demo_report: dict) -> None:
assert set(demo_report.keys()) == {
"turns", "learning_loop_closed", "active_corpus_byte_identical",
}
for turn in demo_report["turns"]:
assert set(turn.keys()) == {
"scene", "prompt", "surface", "grounding_source", "note",
}
def test_humanize_surface_rewrites_teaching_grounded() -> None:
"""The display-only humaniser must put the propositional sentence
first and keep every load-bearing token from the production
teaching-grounded format."""
from evals.conversation.run_demo import _humanize_surface
raw = (
"narrative — teaching-grounded (cognition_chains_v1): "
"rhetoric.narrative; language.discourse. narrative reveals meaning "
"(cognition.meaning). No session evidence yet."
)
out = _humanize_surface(raw, grounding_source="teaching")
# Propositional sentence first, sentence-cased.
assert out.startswith("Narrative reveals meaning.")
# Every load-bearing token preserved (trust boundary).
assert "teaching-grounded" in out
assert "cognition_chains_v1" in out
assert "rhetoric.narrative" in out
assert "language.discourse" in out
assert "cognition.meaning" in out
assert "No session evidence yet." in out
def test_humanize_surface_is_passthrough_for_non_teaching() -> None:
from evals.conversation.run_demo import _humanize_surface
pack_surface = "Truth is a claim. pack-grounded (en_core_cognition_v1)."
assert _humanize_surface(pack_surface, grounding_source="pack") == pack_surface
discourse_surface = "Recall is to retrieve a stored state from memory. Recall reveals memory."
# Discourse-planner output doesn't match the raw teaching-grounded
# format — passes through unchanged.
assert _humanize_surface(discourse_surface, grounding_source="teaching") == discourse_surface
def test_demo_does_not_mutate_active_teaching_corpus() -> None:
"""The demo must be read-only against the live corpus."""
from chat import teaching_grounding as _tg
before = _tg._CORPUS_PATH.read_bytes() if _tg._CORPUS_PATH.exists() else b""
run_demo(emit_json=True, stream=False)
after = _tg._CORPUS_PATH.read_bytes() if _tg._CORPUS_PATH.exists() else b""
assert before == after