"""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.cross_pack_grounding import cross_pack_chains_for_object from chat.pack_resolver import _pack_lexicon_for, resolve_lemma from packs.register.loader import RegisterPack, UNREGISTERED from chat.teaching_grounding import ( _all_chains_index, _pack_for_corpus, ) from generate.semantic_templates import humanize_predicate def _object_domains_for_chain(chain) -> tuple[str, ...]: """Resolve object domains for both in-pack and cross-pack chains.""" object_pack_id = getattr(chain, "object_pack_id", None) if object_pack_id: return _pack_lexicon_for(object_pack_id).get(chain.object, ()) return _pack_for_corpus(chain.corpus_id).get(chain.object, ()) def example_grounded_surface( object_lemma: str, *, max_examples: int = 3, register: RegisterPack = UNREGISTERED, ) -> 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: list = [chain for chain in index.values() if chain.object == key] # ADR-0067 — merge cross-pack chains whose object equals the lemma. matching.extend(cross_pack_chains_for_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 = _object_domains_for_chain(first) 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"]