ADR-0063 closes the ADR-0048/0050/0053/0061 hardcoded-cognition-pack asymmetry. New chat/pack_resolver.py provides resolve_lemma(lemma, pack_ids) → (resolving_pack_id, semantic_domains) across an ordered tuple of mounted lexicon packs (first-match-wins, lru_cache per-pack). Surface composers in chat/pack_grounding.py now consult the resolver instead of a hardcoded en_core_cognition_v1. en_core_relations_v1 joins RuntimeConfig.input_packs defaults; kinship lemmas now ground on the live path: > What is a parent? parent — pack-grounded (en_core_relations_v1): kinship.ascendant.direct; kinship.parent; biology.progenitor. No session evidence yet. Cross-pack comparison (knowledge × parent) renders composite tag (en_core_cognition_v1 × en_core_relations_v1). Cognition lane remains byte-identical: cognition is resolved first and the surface format for cognition lemmas is unchanged. Cognition eval (byte-identical to pre-ADR baseline): public → intent 100% / surface 100% / term 91.7% / closure 100% holdout → intent 100% / surface 100% / term 83.3% / closure 100% Curated lanes green: smoke 67 / cognition 121 / teaching 17 / packs 6 / runtime 19 / algebra 132. New tests: test_pack_resolver.py (28) + test_cross_pack_grounding.py (17). test_en_core_relations_v1_pack.py: default-input-packs guard inverted. test_pack_grounding.py: two stale ADR-0048 tests rewritten (premises invalidated by ADR-0052/0061; now use fully-out-of-pack prompts). chat/teaching_grounding.py UNCHANGED — cognition_chains_v1 corpus stays cognition-only. Cross-pack teaching corpora are the natural ADR-0064.
9.9 KiB
ADR-0063 — Cross-pack surface resolver
Status: Accepted
Date: 2026-05-18
Author: Shay
Supersedes: none (extends ADR-0048 / ADR-0050 / ADR-0052 / ADR-0053 / ADR-0060 / ADR-0061 / ADR-0062)
Builds on: the kinship starter pack en_core_relations_v1 (commit f0c57eb).
Context
ADR-0048 introduced the first pack-grounded surface composer — a
deterministic cold-start surface for DEFINITION / RECALL intents
whose subject lemma is present in the ratified cognition pack. ADR-0050
extended that to COMPARISON. ADR-0053 + ADR-0060 added a CORRECTION
acknowledgement composer (cold-start branch). ADR-0061 added a
PROCEDURE composer. All four composers shared a hardcoded reference
to a single lexicon pack:
# chat/pack_grounding.py — pre-ADR-0063
PACK_ID: str = "en_core_cognition_v1"
That asymmetry was the rate-limiter on domain expansion. Mounting the
en_core_relations_v1 kinship starter pack (already ratified —
checksum 1a013b46…) would silently widen the runtime's mounted
manifold (vault recall + intent classification) without a corresponding
surface composer for kinship lemmas. The cold-start
What is a parent? prompt would fall through to the universal
"insufficient grounding" disclosure even though parent is in a
ratified, immutable pack on disk.
The previous ADR's memory recorded this explicitly:
Engagement status: OPT-IN ONLY. Pack is NOT in
RuntimeConfig.input_packsdefaults… until cross-pack teaching-grounded composition exists, mounting silently exposes lemmas to vault recall and intent classification without a corresponding ratified surface composer.
ADR-0063 is the one-PR architectural unlock that closes that gap: the surface composers consult an abstract resolver across all mounted lexicon packs instead of one hardcoded pack, and the relations pack joins the default mount.
Decision
1. New abstraction: chat/pack_resolver.py
A small (~140 lines) pure module that maps a lemma to a
(resolving_pack_id, semantic_domains) pair across an ordered tuple of
mounted lexicon packs:
DEFAULT_RESOLVABLE_PACK_IDS: tuple[str, ...] = (
"en_core_cognition_v1",
"en_core_relations_v1",
)
def resolve_lemma(
lemma: str,
pack_ids: tuple[str, ...] = DEFAULT_RESOLVABLE_PACK_IDS,
) -> tuple[str, tuple[str, ...]] | None: ...
def is_resolvable(lemma, pack_ids=...) -> bool: ...
def mounted_lemmas(pack_ids=...) -> frozenset[str]: ...
Properties:
- First-match-wins. Order matters: cognition is listed first so that any future cross-pack collision resolves cognition-side, preserving cognition-lane byte-identity.
- lru_cache per-pack. Each pack's
lexicon.jsonlis loaded at most once per process (ratified packs are immutable). - No mutation. Returns tuples, frozensets, plain strings.
- Pure. No I/O outside the cached read; no network; no LLM; no approximation.
2. Surface composers consult the resolver
chat/pack_grounding.py:
pack_grounded_surface(lemma)— surface tag follows the resolving pack id. Cognition lemmas keep emittingpack-grounded (en_core_cognition_v1)byte-identically; kinship lemmas emitpack-grounded (en_core_relations_v1).pack_grounded_comparison_surface(a, b)— each side resolved independently. When both resolve to the same pack, the tag stays single (pack-grounded (en_core_cognition_v1)). When the two resolve to different packs, the tag becomes composite (pack-grounded (en_core_cognition_v1 × en_core_relations_v1)).pack_grounded_correction_surface(text)— anchor pack stays cognition (correctionis a cognition lemma); topic domains come from whichever pack resolves the topic lemma. Topic extraction scansmounted_lemmas()instead of the cognition-only index.pack_grounded_procedure_surface(subject_text)— topic extraction scansmounted_lemmas(); resolving pack id drives the surface tag.
chat/teaching_grounding.py is unchanged in this ADR. The
reviewed teaching corpus (cognition_chains_v1.jsonl) still
references cognition-pack lemmas only. Cross-pack teaching chains
(e.g. a relations_chains_v1.jsonl) are the natural next ADR.
3. Backward compatibility
chat/pack_grounding.py:PACK_ID = "en_core_cognition_v1"and_pack_index()(cognition-only) are retained for back-compat with consumers inteaching/contemplation.py,teaching/discovery.py,chat/teaching_grounding.py, and various tests whose semantics are scoped to the cognition pack. They are NOT removed — only the composers' internal lookups switch to the resolver.is_pack_lemma(lemma)keeps its cognition-only semantics; cross-pack residency ischat.pack_resolver.is_resolvable.
4. Default mount adds the relations pack
core/config.py:
input_packs: tuple[str, ...] = (
"en_minimal_v1",
"en_core_cognition_v1",
"en_core_relations_v1", # added in ADR-0063
"he_logos_micro_v1",
"grc_logos_micro_v1",
)
Mounting is now safe because the composers ground kinship lemmas deterministically.
5. The relations-pack default-input test inverts
tests/test_en_core_relations_v1_pack.py:
- Before:
test_pack_is_not_in_default_input_packs - After:
test_pack_is_in_default_input_packs
The earlier test was an explicit guard against premature mounting. It guarded the asymmetry that ADR-0063 closes; its inverse is now the correct invariant.
Consequences
Capability unlocked
| Surface composer | Cognition lemmas | Kinship lemmas |
|---|---|---|
pack_grounded_surface (DEFINITION / RECALL) |
byte-identical | now grounds |
pack_grounded_comparison_surface (COMPARISON) |
byte-identical when both cognition | now grounds kinship × kinship; composite tag for cross-pack |
pack_grounded_correction_surface (CORRECTION) |
byte-identical when topic ∈ cognition | now anchors topic on kinship lemmas |
pack_grounded_procedure_surface (PROCEDURE) |
byte-identical when topic ∈ cognition | now grounds kinship topics |
Cognition lane: byte-identical
The cognition lane's prompts use only cognition lemmas. The resolver
picks cognition first, the surface tag stays en_core_cognition_v1,
the surface bytes are identical:
core eval cognition → 100/100/91.7/100 (public)
core eval cognition --split holdout → 100/100/83.3/100 (holdout)
Both byte-identical to the pre-ADR baseline.
Live-path grounding on kinship lemmas
$ core chat
> What is a parent?
parent — pack-grounded (en_core_relations_v1): kinship.ascendant.direct;
kinship.parent; biology.progenitor. No session evidence yet.
grounding_source = pack
Future ADRs unlocked
- ADR-0064 — Cross-pack teaching chains. Add
relations_chains_v1.jsonl(reviewed kinship chains) and extendchat/teaching_grounding.pyto consult both corpora. The natural next ADR. - Pronoun + role-filler v2.
mother,father,son, etc. as specializations of the v1 kinship primitives — extendsen_core_relations_v1itself. - Cross-domain triples.
family causes belonging,parent grounds identity— opens after cognition + relations both have ratified internal DAGs of reviewed chains.
Trust boundaries
- The resolver does NOT execute pack metadata, validators, or scripts.
It reads
lexicon.jsonlonly. - A pack that fails to load produces an empty index — callers see
None. No exception leaks into the runtime. - No mutation site introduced. The resolver is read-only over immutable, ratified pack data.
- Tag tokens emitted in surfaces are always one of:
en_core_cognition_v1,en_core_relations_v1, or the composite{a} × {b}form derived from mounted-pack ids — never from user input. pack_resolver.clear_resolver_cache()is a test-only escape hatch; production paths never call it.
Verification
tests/test_pack_resolver.py 28 passed
tests/test_cross_pack_grounding.py 17 passed
tests/test_pack_grounding.py 13 passed
tests/test_pack_grounded_comparison.py passed
tests/test_pack_grounded_correction.py passed
tests/test_correction_topic_lemma.py passed
tests/test_procedure_surface.py passed
tests/test_teaching_grounding.py passed
tests/test_composed_surface.py 11 passed
tests/test_en_core_relations_v1_pack.py 6 passed (inverted)
Lanes:
core test --suite smoke 67 passed
core test --suite cognition 121 passed
core test --suite teaching 17 passed
core test --suite packs 6 passed
core test --suite runtime 19 passed
core test --suite algebra 132 passed
core test --suite full <pending — see PR>
Cognition eval (byte-identical to pre-ADR baseline):
public split → intent 100% / surface 100% / term 91.7% / closure 100%
holdout split → intent 100% / surface 100% / term 83.3% / closure 100%
The non-negotiable field invariant versor_condition(F) < 1e-6 is
unaffected — this ADR is a routing/dispatch change over immutable
pack data; no algebra, no field operators, no normalization sites
were touched.
Files changed
chat/pack_resolver.py NEW (~140 lines)
chat/pack_grounding.py composers use resolver
core/config.py relations pack joins defaults
tests/test_pack_resolver.py NEW (28 tests)
tests/test_cross_pack_grounding.py NEW (17 tests)
tests/test_pack_grounding.py 2 stale tests rewritten
tests/test_en_core_relations_v1_pack.py default-input test inverted
docs/decisions/ADR-0063-cross-pack-surface-resolver.md NEW (this file)
docs/decisions/README.md ADR-0063 index entry
Lines of net change: small. The architectural unlock is in
chat/pack_resolver.py; the rest of the diff is wiring + tests.