Wires the gloss resolver (Phase B2) through pack_grounded_surface
WITHOUT hard-coding around the future SurfaceSelector. Per the
2026-05-19 design review:
> don't let glosses hard-code around the selector. If they ship
> first, keep the integration deliberately narrow:
> pack_grounded_surface() may use glosses temporarily, but the
> data model should already look like future SurfaceCandidate
> input. That avoids a second migration.
The integration uses a typed intermediate dataclass that matches the
selector's expected candidate shape:
chat/pack_surface_candidate.py
@dataclass(frozen=True, slots=True)
class PackSurfaceCandidate:
surface: str # rendered final string
grounding_source: str # "pack" today
pack_id: str # provenance
gloss: str | None # reviewed natural-language form
semantic_domains: tuple # audit-trail content
lemma: str
pos: str
is_user_facing_safe: bool # honesty flag for selector
is_fluent_sentence: bool # gloss-backed vs. dotted-disclosure
When the SurfaceSelector lands:
- This type becomes one variant in the selector's typed candidate
union (alongside RefusalCandidate, TeachingCandidate, OOVCandidate).
- pack_grounded_surface() becomes a provider that emits the
candidate; the selector picks across providers' candidates by
ranked authority + is_fluent_sentence preference.
- No data migration — only the rendering step relocates.
Surface composition (chat/pack_grounding.py):
build_pack_surface_candidate(lemma) -> PackSurfaceCandidate
1. resolve_lemma(lemma) — required (None when OOV).
2. resolve_gloss(lemma) — when present AND same pack as lexicon,
compose POS-framed fluent sentence:
"Truth is a claim or state grounded by evidence and
coherent judgment. Pack-grounded (en_core_cognition_v1)."
sets is_fluent_sentence=True.
3. Else fallback to original ADR-0048 dotted-disclosure form:
"truth — pack-grounded (en_core_cognition_v1):
cognition.truth; logos.core; epistemic.ground.
No session evidence yet."
sets is_fluent_sentence=False.
pack_grounded_surface(lemma) -> str | None
Renders the candidate's surface field. Returns None for OOV.
Both fluent and disclosure surfaces carry the
"pack-grounded ({pack_id})" provenance marker so existing
substring-permissive tests continue to pass through the
transition.
POS-framed sentence templates (_frame_gloss):
NOUN -> "{Lemma} is {gloss}."
VERB -> "To {lemma} means {gloss}."
ADJ -> "Something is {lemma} when it {gloss}."
ADV -> "{Lemma} indicates {gloss}."
ADP -> "{Lemma} is a relation of {gloss}."
SCONJ -> "{Lemma} introduces {gloss}."
PRON -> "{Lemma} asks for {gloss}."
AUX -> "{Lemma} expresses {gloss}."
INTJ -> "{Lemma} is uttered to {gloss}."
DET -> "{Lemma} specifies {gloss}."
NUM -> "{Lemma} is the cardinal value {gloss}."
Phase C glosses (sitting in /tmp from the 5-subagent parallel
dispatch) are authored to fit these frames exactly.
NO GLOSSES SHIP IN THIS COMMIT. This is the wiring; the content
arrives in the Phase C commit. Today every pack still emits the
original dotted-disclosure form because no glosses.jsonl exists yet
on any pack.
Verification:
97/97 affected tests green (pack grounding, resolver, glosses,
procedure surface, correction topic, meta pack).
Cognition eval byte-identical on both splits.
Live probe with no glosses: surface format identical to pre-fix.