core/tests/test_explain_repl.py
Shay a1a085057e
feat(w013): wire explain_last_turn() into core chat /explain REPL command (#265)
Closes W-013 wiring debt. Per Phase 2 operator decision: wire
core.cognition.explain into the live core chat REPL.

Changes:
- core/cognition/explain.py: add explain_from_intent(intent, correction_text)
  companion to explain() — same dispatch table, skips the full
  CognitiveTurnResult round-trip. Callers with only a DialogueIntent can
  use this directly.
- chat/runtime.py: add _last_intent and _last_input_text instance fields;
  store intent on every classify_intent_from_input() call (pack-grounded
  path and stub/empty-vault path); add explain_last_turn() -> str method
  that calls explain_from_intent(_last_intent, correction_text=_last_input_text).
- core/cli.py: in cmd_chat REPL loop, handle "/explain" command — calls
  runtime.explain_last_turn() and prints the canonical prompt restatement
  (or a "no prior turn" message to stderr if no turn has run yet).
- tests/test_explain_repl.py: 11 tests pinning explain_from_intent dispatch
  for all intent tags and the ChatRuntime.explain_last_turn() contract.

Per ADR-0017 (Responsive-with-Axiology): introspection is per-turn and
operator-invoked, never autonomous — the /explain command is correct
placement for this feature.
2026-05-25 06:09:49 -07:00

88 lines
2.9 KiB
Python

"""Tests for W-013: explain_last_turn() wired into core chat REPL."""
from __future__ import annotations
from core.cognition.explain import explain_from_intent
from generate.intent import DialogueIntent, IntentTag
# ---------------------------------------------------------------------------
# explain_from_intent — unit tests (mirrors explain() dispatch)
# ---------------------------------------------------------------------------
def _intent(tag: IntentTag, subject: str = "truth") -> DialogueIntent:
return DialogueIntent(tag=tag, subject=subject)
def test_explain_from_intent_none_returns_empty() -> None:
assert explain_from_intent(None) == ""
def test_explain_from_intent_definition() -> None:
result = explain_from_intent(_intent(IntentTag.DEFINITION, "wisdom"))
assert result == "What is wisdom?"
def test_explain_from_intent_cause() -> None:
result = explain_from_intent(_intent(IntentTag.CAUSE, "light reveals"))
assert result == "Why light reveals?"
def test_explain_from_intent_procedure() -> None:
result = explain_from_intent(_intent(IntentTag.PROCEDURE, "ground a claim"))
assert result == "How do I ground a claim?"
def test_explain_from_intent_comparison() -> None:
result = explain_from_intent(
DialogueIntent(
tag=IntentTag.COMPARISON,
subject="knowledge",
secondary_subject="wisdom",
)
)
assert result == "Compare knowledge and wisdom."
def test_explain_from_intent_verification() -> None:
result = explain_from_intent(_intent(IntentTag.VERIFICATION, "truth is coherent"))
assert result == "Is truth is coherent?"
def test_explain_from_intent_recall() -> None:
result = explain_from_intent(_intent(IntentTag.RECALL, "truth"))
assert result == "Remember truth."
def test_explain_from_intent_correction_uses_correction_text() -> None:
result = explain_from_intent(
_intent(IntentTag.CORRECTION, "truth"),
correction_text="Actually truth is coherent.",
)
assert result == "Actually truth is coherent."
def test_explain_from_intent_correction_falls_back_to_subject() -> None:
result = explain_from_intent(_intent(IntentTag.CORRECTION, "truth"), correction_text="")
assert "truth" in result
# ---------------------------------------------------------------------------
# ChatRuntime.explain_last_turn — integration test
# ---------------------------------------------------------------------------
def test_explain_last_turn_no_prior_turn_returns_empty() -> None:
from chat.runtime import ChatRuntime
runtime = ChatRuntime()
assert runtime.explain_last_turn() == ""
def test_explain_last_turn_after_definition_turn() -> None:
from chat.runtime import ChatRuntime
runtime = ChatRuntime()
runtime.chat("What is truth?")
result = runtime.explain_last_turn()
# Should produce a definition form ("What is <subject>?")
assert result.startswith("What is ")