Closes the surface-grounding gap isolated by ADR-0047's
characterisation. Adds the ratified cognition pack as a second
grounding source alongside the session vault.
== chat/pack_grounding.py (new) ==
Loads en_core_cognition_v1's lexicon once (cached; immutable pack)
and exposes:
pack_grounded_surface(lemma) -> str | None
Returns a deterministic, fully pack-sourced surface:
"{lemma} — pack-grounded ({pack_id}): {d1}; {d2}; {d3}. No session evidence yet."
Every visible atom is the lemma or a verbatim semantic_domains
string from the pack. No rewording, no synthesis, no LLM.
== chat/runtime.py ==
_stub_response gains optional pack_grounded_surface= parameter.
_maybe_pack_grounded_surface routes to the pack only when all four
hold: gate_source=="empty_vault", output_language=="en",
intent.tag in {DEFINITION, RECALL}, and intent.subject is a pack
lemma. Safety/ethics refusal still takes priority above this branch.
ChatResponse and TurnEvent gain grounding_source ∈ {vault,pack,none}.
Main walk path tags responses "vault".
== core/cognition/pipeline.py ==
gate_fired detection moved from string equality on the universal
disclosure to provenance:
gate_fired = response.vault_hits == 0 and response.grounding_source != "vault"
Same intent (suppress realizer template on gate-fired turns),
broader stub-path surface set.
== Characterisation (core eval cognition, 13-case public split) ==
Metric Pre Post Δ
intent_accuracy 100.0% 100.0% 0
surface_groundedness 15.4% 46.2% +30.8 pp
term_capture_rate 0.0% 33.3% +33.3 pp
versor_closure_rate 100.0% 100.0% 0
Lift is non-uniform by design: only single-lemma DEFINITION/RECALL
on pack-known English subjects engage. CAUSE/COMPARISON/VERIFICATION
and multi-word OOV subjects still return the universal disclosure —
fabricating those would violate the no-LLM-fallback doctrine.
== Tests ==
tests/test_pack_grounding.py 18 passed
tests/test_semantic_realizer_integration.py (updated) 1 stub-path test
pinned to the broader contract: surface is either universal
disclosure or pack-grounded; never the realizer template.
== Lanes ==
smoke 67 cognition 121 runtime 19 algebra 132
teaching 17 packs 6
versor_condition(F) < 1e-6 invariant unaffected (no algebra changes).
257 lines
12 KiB
Markdown
257 lines
12 KiB
Markdown
# ADR-0048 — Pack-Grounded Surface for Cold-Start DEFINITION / RECALL
|
|
|
|
**Status:** Accepted
|
|
**Date:** 2026-05-18
|
|
**Author:** Shay
|
|
|
|
---
|
|
|
|
## Context
|
|
|
|
[ADR-0047](./ADR-0047-wire-forward-graph-constraint.md) isolated the
|
|
load-bearing finding from the cognition lane: with the forward graph
|
|
constraint engaged (6/13 cases), `surface_groundedness` and
|
|
`term_capture_rate` did **not** move. The candidate-set restriction
|
|
upstream of `generate()` was working — the gap lived **downstream of
|
|
propagation**.
|
|
|
|
Investigation traced every cognition case to a single failure mode.
|
|
`ChatRuntime.chat()` consults `UnknownDomainGate` on each turn; the
|
|
gate measures whether anything in the **session vault** is similar to
|
|
the input. A freshly instantiated runtime starts with `len(vault) == 0`,
|
|
so the gate fires immediately with `source="empty_vault"` and routes
|
|
through `_stub_response`, emitting the universal disclosure:
|
|
|
|
"I don't know — insufficient grounding for that yet."
|
|
|
|
This is doctrine-correct refusal — no fabrication. But it elides the
|
|
fact that CORE *does* have grounded evidence for `light`, `knowledge`,
|
|
`meaning`, `memory`, `truth`, `procedure`, `relation`, and many other
|
|
cognition-core lemmas: they are compiled into the **ratified
|
|
`en_core_cognition_v1` pack**, each carrying curated `semantic_domains`
|
|
(`["cognition.illumination", "logos.core", "perception.clarity",
|
|
"meaning.revelation"]` for `light`).
|
|
|
|
CLAUDE.md is explicit:
|
|
|
|
> *Semantic Pack Discipline — Prefer compact, curated packs … `en_core_cognition_v1`
|
|
> supplies thought vocabulary, operations, and relation predicates.*
|
|
|
|
Pack evidence is **reviewed/curated memory**, the strongest form of
|
|
grounding short of session vault evidence. The gate consulted only
|
|
session memory and treated cold-start as universally ungrounded.
|
|
|
|
---
|
|
|
|
## Decision
|
|
|
|
Add a narrow second-source-of-grounding alongside the session vault:
|
|
|
|
1. **`chat/pack_grounding.py`** (new) — loads `en_core_cognition_v1`'s
|
|
lexicon once (cached; ratified packs are immutable) and exposes
|
|
`pack_grounded_surface(lemma) -> str | None`. The surface format is
|
|
fixed and entirely composed of pack-sourced atoms:
|
|
|
|
```text
|
|
{lemma} — pack-grounded ({pack_id}): {d1}; {d2}; {d3}. No session evidence yet.
|
|
```
|
|
|
|
Every visible token is either the lemma itself or a verbatim
|
|
`semantic_domains` string from the pack. No rewording, no LLM,
|
|
no synthesis. The trailing disclosure (`No session evidence yet.`)
|
|
is a constant trust-boundary label distinguishing pack-grounded
|
|
surfaces from vault-grounded surfaces.
|
|
|
|
2. **`chat/runtime.py`** — extends `_stub_response` with an optional
|
|
`pack_grounded_surface: str | None = None` parameter. When the
|
|
`UnknownDomainGate` fires, the runtime calls a narrow helper
|
|
`_maybe_pack_grounded_surface(text, gate_source)` that returns a
|
|
non-`None` surface **only** when:
|
|
|
|
- `gate_source == "empty_vault"` (not any other gate-firing source),
|
|
- `config.output_language == "en"` (pack is English-specific),
|
|
- `intent.tag in {DEFINITION, RECALL}` (narrow intent scope),
|
|
- `intent.subject` is a known pack lemma.
|
|
|
|
When all four hold, the stub-path response carries the
|
|
pack-grounded surface instead of `_UNKNOWN_DOMAIN_SURFACE`. Safety
|
|
and ethics refusal still take priority above this branch (refusal is
|
|
a remediation tier, not a grounding source).
|
|
|
|
3. **`ChatResponse.grounding_source`** and **`TurnEvent.grounding_source`** —
|
|
one new field on each, valued in `{"vault", "pack", "none"}`. The
|
|
tag is preserved verbatim through the telemetry stream so downstream
|
|
audit consumers can distinguish session-evidence answers, pack-
|
|
evidence answers, and disclosures.
|
|
|
|
4. **`core/cognition/pipeline.py`** — `gate_fired` detection is moved
|
|
from string equality on `_UNKNOWN_DOMAIN_SURFACE` to provenance:
|
|
|
|
```python
|
|
gate_fired = response.vault_hits == 0 and response.grounding_source != "vault"
|
|
```
|
|
|
|
The realizer's template fallback (`"Truth is defined as ..."`) is
|
|
suppressed identically on both stub-path surfaces (universal
|
|
disclosure and pack-grounded), preserving the
|
|
"calibration gaps Finding 2" contract while accepting the broader
|
|
stub-path surface set.
|
|
|
|
---
|
|
|
|
## Why this is doctrine-aligned, not a fabrication
|
|
|
|
CLAUDE.md prohibits *opaque LLM fallbacks, stochastic sampling, hidden
|
|
normalisation, hot-path repair, and approximate recall*. Pack-grounded
|
|
surfaces are:
|
|
|
|
- **Not opaque.** Every visible atom is the lemma or a verbatim pack
|
|
`semantic_domains` string; the source pack is named in the surface.
|
|
- **Not stochastic.** Deterministic JSONL read; identical input
|
|
produces byte-identical output (`test_surface_is_deterministic`).
|
|
- **Not hidden normalisation.** The pack lookup is a separate source
|
|
of grounding, not a normalisation step inside an existing operator.
|
|
- **Not hot-path repair.** `UnknownDomainGate` remains correct — the
|
|
gate still fires; the change is what the runtime emits *after* the
|
|
gate fires, in a narrow, intent-typed, audit-tagged branch.
|
|
- **Not approximate recall.** Exact dictionary lookup on the pack
|
|
lexicon by lemma. No metric, no neighbourhood, no threshold.
|
|
|
|
The fundamental architectural move is recognising that **the system has
|
|
two grounding sources, not one**: session vault and reviewed pack. The
|
|
old code consulted only session vault; this ADR extends the recall step
|
|
to also consult the reviewed pack, with provenance preserved end-to-end.
|
|
|
|
This matches the same trust-boundary discipline ADR-0029 (safety packs)
|
|
and ADR-0033 (ethics packs) established for the verdict surfaces:
|
|
multiple ratified packs compose into the runtime's evidence basis, each
|
|
with its own provenance tag. Identity / safety / ethics packs already
|
|
contribute to the manifold and the verdict bundle; this ADR adds the
|
|
cognition pack as a corresponding contributor to the *surface*.
|
|
|
|
---
|
|
|
|
## Characterisation — `core eval cognition`
|
|
|
|
A/B run on the 13-case public cognition split, identical
|
|
`RuntimeConfig` except for the merge of this ADR:
|
|
|
|
| Metric | Pre-ADR-0048 | Post-ADR-0048 | Δ |
|
|
|---------------------------|--------------|---------------|------------|
|
|
| `intent_accuracy` | 100.0 % | 100.0 % | 0 |
|
|
| `surface_groundedness` | 15.4 % | **46.2 %** | **+30.8** |
|
|
| `term_capture_rate` | 0.0 % | **33.3 %** | **+33.3** |
|
|
| `versor_closure_rate` | 100.0 % | 100.0 % | 0 |
|
|
| `versor_condition < 1e-6` | preserved | preserved | invariant |
|
|
|
|
The lift is **not** uniform across cases. ADR-0048 only engages on
|
|
single-lemma DEFINITION / RECALL where the subject is in the pack. The
|
|
remaining cases (CAUSE / COMPARISON / VERIFICATION / multi-word OOV
|
|
subjects) still return the universal disclosure, which is the correct
|
|
behaviour — the cognition pack doesn't carry causal explanations or
|
|
verification logic, and fabricating those would violate the no-LLM-
|
|
fallback doctrine.
|
|
|
|
Surface examples (post-ADR-0048):
|
|
|
|
| Prompt | Surface |
|
|
|--------|---------|
|
|
| `What is light?` | `"light — pack-grounded (en_core_cognition_v1): cognition.illumination; logos.core; perception.clarity. No session evidence yet."` |
|
|
| `What is knowledge?` | `"knowledge — pack-grounded (en_core_cognition_v1): cognition.knowledge; epistemic.ground; memory.semantic. No session evidence yet."` |
|
|
| `Remember light` | `"light — pack-grounded (en_core_cognition_v1): cognition.illumination; logos.core; perception.clarity. No session evidence yet."` |
|
|
| `Why does light exist?` | `"I don't know — insufficient grounding for that yet."` (CAUSE intent — no pack path) |
|
|
| `What is a procedure?` | `"I don't know — insufficient grounding for that yet."` (multi-word subject "a procedure" not in pack index) |
|
|
|
|
---
|
|
|
|
## Consequences
|
|
|
|
### What changes
|
|
|
|
- `ChatRuntime` cold-start DEFINITION / RECALL on a pack-known English
|
|
lemma emits a pack-grounded surface instead of the universal
|
|
disclosure.
|
|
- `ChatResponse` and `TurnEvent` carry a new `grounding_source` field;
|
|
downstream audit consumers can filter on `"vault" | "pack" | "none"`.
|
|
- `CognitiveTurnPipeline.run` no longer string-matches on the universal
|
|
disclosure to detect a gate-fired turn — it uses
|
|
`response.grounding_source != "vault"`. Same intent, broader surface
|
|
set.
|
|
- One test (`test_pipeline_honours_safety_stub_when_gate_fires`)
|
|
updated to assert the broader stub-path contract: surface is either
|
|
the universal disclosure or a pack-grounded surface, and never the
|
|
realizer's `"X is defined as ..."` template.
|
|
|
|
### What does not change
|
|
|
|
- `UnknownDomainGate` semantics are unchanged. It still fires on
|
|
every empty-vault cold-start turn. Provenance signal preserved
|
|
(`gate_decision.source == "empty_vault"`).
|
|
- Safety / ethics refusal still takes priority above pack grounding
|
|
— refusal is a remediation tier, not a grounding source.
|
|
- `_UNKNOWN_DOMAIN_SURFACE` constant retained; non-English, non-pack-
|
|
lemma, non-DEFINITION/RECALL paths still return it unchanged.
|
|
- `versor_condition(F) < 1e-6` invariant unaffected (no algebra
|
|
changes).
|
|
- Main walk path's `ChatResponse` carries `grounding_source="vault"`.
|
|
|
|
### Scope limits
|
|
|
|
- English path only (`en_core_cognition_v1`). Multilingual cognition
|
|
packs would follow the same pattern under a separate ADR.
|
|
- DEFINITION + RECALL only. CAUSE / COMPARISON / VERIFICATION /
|
|
PROCEDURE intents are out of scope — they need either teaching-store
|
|
chains (ADR-0018) or operator-driven inference, not pack lookup.
|
|
- Single-lemma subjects only. Multi-word subjects produced by the
|
|
intent classifier (`"does light exist"`, `"a procedure"`) bypass the
|
|
pack lookup; tightening the intent classifier's subject extraction
|
|
is a separate concern and a candidate follow-up ADR.
|
|
- Top-3 `semantic_domains` rendered. The pack's deterministic order
|
|
in the JSONL determines which three; pack authors can re-order if
|
|
needed (a one-line edit to the lexicon).
|
|
|
|
---
|
|
|
|
## Cross-References
|
|
|
|
- [ADR-0018](./ADR-0018-tool-use-scope.md) — `intent_bridge` /
|
|
`classify_intent` whose `DialogueIntent.tag` and `subject` this
|
|
ADR consults.
|
|
- [ADR-0029](./ADR-0029-safety-packs.md) and
|
|
[ADR-0033](./ADR-0033-ethics-packs.md) — sibling pack-grounded
|
|
contributors to the runtime: this ADR extends that pattern from
|
|
*verdict* surfaces to the *answer* surface.
|
|
- [ADR-0035](./ADR-0035-turn-loop-verdict-surfacing.md) — stub-path
|
|
TurnEvent emission that pack-grounded surfaces preserve.
|
|
- [ADR-0046](./ADR-0046-forward-graph-constraint.md) /
|
|
[ADR-0047](./ADR-0047-wire-forward-graph-constraint.md) — the
|
|
Pillar 1→2→3 wiring whose A/B characterisation isolated the
|
|
surface-grounding gap addressed here.
|
|
|
|
---
|
|
|
|
## Verification
|
|
|
|
```
|
|
tests/test_pack_grounding.py — 18 tests, all green
|
|
tests/test_semantic_realizer_integration.py — gate-fired test updated, all green
|
|
|
|
Lanes (all green on this branch):
|
|
core test --suite smoke 67 passed
|
|
core test --suite cognition 121 passed
|
|
core test --suite runtime 19 passed
|
|
core test --suite algebra 132 passed
|
|
core test --suite teaching 17 passed
|
|
core test --suite packs 6 passed
|
|
|
|
core eval cognition (pre → post):
|
|
intent_accuracy 100.0% → 100.0% (=)
|
|
surface_groundedness 15.4% → 46.2% (+30.8 pp)
|
|
term_capture_rate 0.0% → 33.3% (+33.3 pp)
|
|
versor_closure_rate 100.0% → 100.0% (=)
|
|
```
|
|
|
|
The non-negotiable field invariant (`versor_condition(F) < 1e-6`) is
|
|
preserved: this ADR adds a surface-construction branch on the
|
|
existing stub path — no algebra changes, no rotor construction,
|
|
no field update.
|