core/chat/example_surface.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

123 lines
4.4 KiB
Python

"""chat/example_surface.py — Phase 3.4: EXAMPLE intent composer.
When a prompt classifies as EXAMPLE — "Give me an example of X",
"Show me an instance of X", "Example of X" — the composer surfaces
a reviewed chain where X appears as the **object**, inverting the
typical "X is the subject" chain access pattern.
For "Give me an example of truth":
(light, cause, reveals, truth) exists in the cognition corpus
"Example of truth: light reveals truth."
This is the *converse* of NARRATIVE. Where NARRATIVE walks every
chain rooted on X as subject ("X reveals A; X grounds B"), EXAMPLE
walks chains where X is the object ("A reveals X; B grounds X").
Both consult the same aggregated teaching index — no new corpus
ratification required.
Design constraints (matching ADR-0052..0065 doctrine):
- **No content synthesis.** Every visible non-template token is
pack-sourced or a verbatim chain atom.
- **Deterministic ordering.** Examples sort by (intent, subject,
connective) so identical corpus state yields identical surfaces.
- **Dedup by subject.** Multiple chains can have the same object X
with the same subject Y (e.g. cause/verification both
``Y reveals X``). Emit one example per distinct subject.
- **Bounded count.** Default ``max_examples=3`` keeps the surface
readable.
Returns ``None`` when no chain references X as object — caller
falls through to pack-grounded DEFINITION (if X is pack-resident)
or to OOV invitation (if X is unknown).
"""
from __future__ import annotations
from chat.pack_resolver import resolve_lemma
from chat.teaching_grounding import (
_all_chains_index,
_pack_for_corpus,
)
from generate.semantic_templates import humanize_predicate
def example_grounded_surface(
object_lemma: str,
*,
max_examples: int = 3,
) -> str | None:
"""Return a deterministic EXAMPLE-tier surface, or ``None``.
Aggregates every reviewed chain whose **object** equals
*object_lemma* across all registered teaching corpora. Dedups
by subject (the same subject acting under both cause + verification
on the same object produces one example, not two). Sorts
lexicographically for replay stability.
Returns ``None`` when no chain references *object_lemma* as
object — caller routes through pack-grounded DEFINITION (if
the lemma is pack-resident) or to OOV invitation.
"""
if not object_lemma or not isinstance(object_lemma, str):
return None
key = object_lemma.strip().lower()
if not key:
return None
if max_examples < 1:
return None
index = _all_chains_index()
matching = [chain for chain in index.values() if chain.object == key]
if not matching:
return None
# Dedup by subject — same subject acting twice (cause +
# verification) on this object is one example. Stable sort
# by (intent, subject, connective).
seen_subjects: set[str] = set()
deduped: list = []
for chain in sorted(
matching, key=lambda c: (c.intent, c.subject, c.connective),
):
if chain.subject in seen_subjects:
continue
seen_subjects.add(chain.subject)
deduped.append(chain)
if len(deduped) >= max_examples:
break
first = deduped[0]
# Object domains come from the first chain's bound pack; falls
# back to the cross-pack resolver if the chain's corpus is bound
# to a pack that does not carry the object (defensive — strict
# pack-residency in ADR-0064 prevents this).
object_pack = _pack_for_corpus(first.corpus_id)
object_domains = object_pack.get(first.object, ())
if not object_domains:
resolved = resolve_lemma(first.object)
if resolved is None:
return None
object_domains = resolved[1]
head_object = "; ".join(
object_domains[: max(1, first.domains_object_k)]
)
corpora = tuple(sorted({c.corpus_id for c in deduped}))
corpora_tag = corpora[0] if len(corpora) == 1 else " + ".join(corpora)
clauses: list[str] = []
for chain in deduped:
connective = humanize_predicate(chain.connective)
clauses.append(f"{chain.subject} {connective} {chain.object}")
examples_text = "; ".join(clauses)
return (
f"{first.object} — example-grounded ({corpora_tag}): "
f"{head_object}. Example: {examples_text}. "
f"No session evidence yet."
)
__all__ = ["example_grounded_surface"]