core/tests/test_narrative_example_intents.py
Shay ce8226e9a2 feat(adr-0066): NARRATIVE + EXAMPLE intents with multi-clause composers (Phase 3.3 + 3.4)
Two new intent shapes + composers turn the runtime's corpus
density into operator-visible articulation.  Both consult the
cross-corpus aggregator from ADR-0064; no new ratification needed.

P3.3 — chat/narrative_surface.py + IntentTag.NARRATIVE.

  Classifier patterns (registered BEFORE generic DEFINITION):
    ^tell\s+me\s+about\s+
    ^describe\s+
    ^what\s+(?:can|do)\s+you\s+(?:say|know)\s+about\s+

  narrative_grounded_surface(subject, max_clauses=4) walks every
  reviewed chain rooted on subject across all registered teaching
  corpora.  Dedupes by (connective, object) — cause + verification
  carrying the same predicate emit one clause, not two.  Sorts by
  (intent, connective, object) for replay stability.

  Surface format:
    "{X} — narrative-grounded ({corpus_ids}): {dX1}; {dX2}.
     {X} {conn1} {O1} ({dO1}); {X} {conn2} {O2} ({dO2}).
     No session evidence yet."

  Cross-corpus subjects (e.g. mother in relations_v2) emit
  narrative-grounded (relations_chains_v2) tag; cognition subjects
  emit cognition_chains_v1 tag.  Multi-corpus subjects (when
  applicable) emit composite "corpus_a + corpus_b" tag.

P3.4 — chat/example_surface.py + IntentTag.EXAMPLE.

  Classifier patterns:
    ^(?:give|show)\s+(?:me\s+)?an?\s+(?:example|instance)\s+of\s+
    ^example\s+of\s+

  example_grounded_surface(object_lemma, max_examples=3) walks chains
  where the lemma is the OBJECT — inverts the typical subject-keyed
  access pattern.  Dedupes by subject; sorts by (intent, subject,
  connective).

  Surface format:
    "{X} — example-grounded ({corpus_ids}): {dX1}.
     Example: {subj1} {conn1} {X}; {subj2} {conn2} {X}.
     No session evidence yet."

Cross-cutting:
  - Both intents added to _OOV_INTENT_TAGS — fall through to OOV
    invitation when subject is unknown (Phase 2 gradient discipline).
  - Both tagged grounding_source="teaching" (same provenance tier
    as the existing teaching_grounded_surface).
  - No prose generation, no new mutation surface.

Live verification:
  > Tell me about truth.
    [teaching] truth — narrative-grounded (cognition_chains_v1):
    cognition.truth; logos.core. truth grounds knowledge
    (cognition.knowledge); truth requires evidence (cognition.evidence).

  > Give me an example of knowledge.
    [teaching] knowledge — example-grounded (cognition_chains_v1):
    cognition.knowledge. Example: truth grounds knowledge;
    understanding requires knowledge; evidence grounds knowledge.

  > Tell me about mother.
    [teaching] mother — narrative-grounded (relations_chains_v2):
    kinship.parent.female. mother precedes daughter (kinship.child.female).

  > Describe photosynthesis.
    [oov] I haven't learned 'photosynthesis' yet (intent: narrative). ...

ADR-0066 (this commit completes the ADR).  30 new tests passed.
Full lane: 2067 passed, 2 skipped, 0 failed in 2:32.
2026-05-18 17:01:55 -07:00

241 lines
8.5 KiB
Python

"""Phase 3.3 + 3.4 — NARRATIVE and EXAMPLE intent + composer tests.
The contracts pinned here:
NARRATIVE
- "Tell me about X" / "Describe X" / "What can you say about X"
classify as NARRATIVE before falling through to DEFINITION.
- Composer walks every reviewed chain rooted on X across all
registered teaching corpora; emits up to max_clauses unique
(predicate, object) clauses; deterministic ordering.
- Falls through to OOV invitation when X is unknown.
EXAMPLE
- "Give me an example of X" / "Show an instance of X" /
"Example of X" classify as EXAMPLE before DEFINITION.
- Composer surfaces chains where X is the OBJECT (reverse-chain
access pattern); dedupes by subject; deterministic ordering.
- Falls through to OOV invitation when X is unknown.
Both
- Surface composes only pack atoms + verbatim chain content +
fixed template — no content synthesis.
- Tagged ``grounding_source="teaching"`` (same provenance as
teaching_grounded_surface — both consume the reviewed corpora).
"""
from __future__ import annotations
import pytest
from chat.example_surface import example_grounded_surface
from chat.narrative_surface import narrative_grounded_surface
from chat.runtime import ChatRuntime
from generate.intent import IntentTag, classify_intent
# ---------------------------------------------------------------------------
# Intent classification
# ---------------------------------------------------------------------------
@pytest.mark.parametrize("prompt", [
"Tell me about light.",
"Tell me about parent",
"Describe truth",
"Describe photosynthesis.",
"What can you say about wisdom?",
"What do you know about memory?",
])
def test_narrative_patterns_classify_narrative(prompt: str) -> None:
intent = classify_intent(prompt)
assert intent.tag is IntentTag.NARRATIVE
assert intent.subject
@pytest.mark.parametrize("prompt", [
"Give me an example of truth.",
"Show me an instance of knowledge.",
"Show an example of parent.",
"Example of meaning",
])
def test_example_patterns_classify_example(prompt: str) -> None:
intent = classify_intent(prompt)
assert intent.tag is IntentTag.EXAMPLE
assert intent.subject
def test_narrative_pattern_precedes_definition() -> None:
"""``What can you say about X?`` could match the generic
``what is/are X`` pattern — assert NARRATIVE wins on the more
specific pattern."""
intent = classify_intent("What can you say about light?")
assert intent.tag is IntentTag.NARRATIVE
# ---------------------------------------------------------------------------
# NARRATIVE composer — pure function
# ---------------------------------------------------------------------------
def test_narrative_aggregates_multiple_chains() -> None:
"""``truth`` appears as the subject of multiple cognition chains;
the narrative composer emits a clause for each."""
surface = narrative_grounded_surface("truth")
assert surface is not None
assert "narrative-grounded (cognition_chains_v1)" in surface
assert "truth grounds knowledge" in surface
assert "truth requires evidence" in surface
def test_narrative_dedupes_by_predicate_object() -> None:
"""When cause + verification carry the same (connective, object),
only one clause is emitted."""
surface = narrative_grounded_surface("light")
assert surface is not None
# (light, cause, reveals, truth) + (light, verification, reveals, truth)
# → one clause "light reveals truth", not two.
assert surface.count("light reveals truth") == 1
def test_narrative_handles_relations_pack_subject() -> None:
surface = narrative_grounded_surface("parent")
assert surface is not None
assert "narrative-grounded (relations_chains_v1)" in surface
assert "parent precedes child" in surface
def test_narrative_handles_relations_v2_subject() -> None:
surface = narrative_grounded_surface("mother")
assert surface is not None
assert "narrative-grounded (relations_chains_v2)" in surface
assert "mother precedes daughter" in surface
def test_narrative_unknown_lemma_returns_none() -> None:
assert narrative_grounded_surface("photosynthesis") is None
assert narrative_grounded_surface("xyzunknown") is None
def test_narrative_empty_input_returns_none() -> None:
assert narrative_grounded_surface("") is None
assert narrative_grounded_surface(" ") is None
def test_narrative_is_deterministic() -> None:
a = narrative_grounded_surface("truth")
b = narrative_grounded_surface("truth")
assert a == b
def test_narrative_max_clauses_caps_output() -> None:
"""``max_clauses=1`` should emit just the lexicographically-first
clause for a multi-chain subject."""
full = narrative_grounded_surface("truth", max_clauses=8)
capped = narrative_grounded_surface("truth", max_clauses=1)
assert full is not None
assert capped is not None
assert capped != full
assert len(capped) < len(full)
# ---------------------------------------------------------------------------
# EXAMPLE composer — pure function
# ---------------------------------------------------------------------------
def test_example_surfaces_reverse_chain() -> None:
"""``truth`` appears as the object of ``light reveals truth`` —
the example composer surfaces the chain inverted (X = object)."""
surface = example_grounded_surface("truth")
assert surface is not None
assert "example-grounded (cognition_chains_v1)" in surface
assert "light reveals truth" in surface
def test_example_aggregates_multiple_subjects() -> None:
"""``knowledge`` appears as the object of multiple chains; the
example composer dedupes by subject."""
surface = example_grounded_surface("knowledge")
assert surface is not None
# truth/understanding/evidence all relate to knowledge as object.
assert "knowledge" in surface
# Each is listed once at most.
subjects = ["truth", "understanding", "evidence"]
found = [s for s in subjects if f"{s}" in surface]
assert len(found) >= 1
def test_example_handles_relations_object() -> None:
"""``parent`` appears as object of ``child follows parent`` +
``family grounds parent`` — multiple examples."""
surface = example_grounded_surface("parent")
assert surface is not None
assert "example-grounded (relations_chains_v1)" in surface
assert "parent" in surface
def test_example_unknown_object_returns_none() -> None:
assert example_grounded_surface("photosynthesis") is None
assert example_grounded_surface("xyzunknown") is None
def test_example_is_deterministic() -> None:
a = example_grounded_surface("truth")
b = example_grounded_surface("truth")
assert a == b
def test_example_max_examples_caps_output() -> None:
capped = example_grounded_surface("knowledge", max_examples=1)
full = example_grounded_surface("knowledge", max_examples=8)
assert capped is not None
assert full is not None
assert len(capped) <= len(full)
# ---------------------------------------------------------------------------
# Live runtime — NARRATIVE
# ---------------------------------------------------------------------------
def test_runtime_narrative_on_known_subject_routes_to_teaching() -> None:
rt = ChatRuntime()
resp = rt.chat("Tell me about truth.")
assert resp.grounding_source == "teaching"
assert "narrative-grounded" in resp.surface
assert "truth" in resp.surface
def test_runtime_narrative_on_oov_routes_to_oov_invitation() -> None:
rt = ChatRuntime()
resp = rt.chat("Describe photosynthesis.")
assert resp.grounding_source == "oov"
assert "photosynthesis" in resp.surface
assert "PackMutationProposal" in resp.surface
# ---------------------------------------------------------------------------
# Live runtime — EXAMPLE
# ---------------------------------------------------------------------------
def test_runtime_example_on_known_object_routes_to_teaching() -> None:
rt = ChatRuntime()
resp = rt.chat("Give me an example of truth.")
assert resp.grounding_source == "teaching"
assert "example-grounded" in resp.surface
assert "light reveals truth" in resp.surface
def test_runtime_example_on_oov_routes_to_oov_invitation() -> None:
rt = ChatRuntime()
resp = rt.chat("Example of photosynthesis")
assert resp.grounding_source == "oov"
def test_runtime_example_on_relations_object() -> None:
rt = ChatRuntime()
resp = rt.chat("Give me an example of parent.")
assert resp.grounding_source == "teaching"
assert "relations_chains_v1" in resp.surface