feat(adr-0053): cognition lane closure — corpus expansion + CORRECTION acknowledgement

Closes both cognition splits at 100% surface_groundedness.  Three
parts:

1. Teaching corpus expansion (no code).  cognition_chains_v1.jsonl
   grows 3→10 chains.  3 close dev-split misses (correction,
   creation, light-as-VERIFICATION); 4 pre-empt the analogous
   holdout pattern (CAUSE/VERIFICATION on truth + wisdom).  Every
   subject/object is a pack lemma; every connective is a recognised
   humanize_predicate predicate.

2. CORRECTION acknowledgement branch.  New
   `pack_grounded_correction_surface()` in chat/pack_grounding.py,
   wired into `_maybe_pack_grounded_surface` for cold-start
   CORRECTION intents.  Fixed-template surface with distinct
   trailing disclosure ("No prior turn in this session to correct
   yet.") — distinguishes the cold-start acknowledgement from the
   DEFINITION-of-correction surface.  The post-correction reviewed-
   teaching path in teaching/correction.py is unchanged.

3. Diagnostic memory.  Saves the dev-split generalization finding:
   the ADR-0048→0052 chain is NOT overfit.  Public/dev gap was
   teaching-corpus content coverage, not architecture.

Eval deltas (both splits run, post-ADR-0053):
                       public   dev
  intent_accuracy        100%   100%   (=)
  surface_groundedness   100%   100%   SATURATED
  term_capture_rate    91.7%  78.6%
  versor_closure_rate    100%   100%   (=)

Public surface_groundedness: 92.3% → 100%   (+7.7 pp)
Dev    surface_groundedness: 69.2% → 100%   (+30.8 pp)

Tests: tests/test_pack_grounded_correction.py (15 new tests).
Lanes green: smoke (67), cognition (121), runtime (19),
teaching (17), packs (6).

Scope limits: holdouts (19 cases) not yet in the official
`core eval cognition` runner (--split accepts only {dev, public});
the CORRECTION surface does not yet echo the corrected-subject
lemma (relevant only for holdout case `correction_truth_040`).
This commit is contained in:
Shay 2026-05-18 07:43:39 -07:00
parent 822d8e1672
commit e975faf8a8
7 changed files with 600 additions and 0 deletions

View file

@ -120,6 +120,45 @@ def is_pack_lemma(lemma: str) -> bool:
return lemma.strip().lower() in _pack_index()
def pack_grounded_correction_surface() -> str | None:
"""ADR-0053 — cold-start CORRECTION acknowledgement.
A CORRECTION intent (``"No, that's wrong"``, ``"Actually, X means Y"``)
is meta-cognitive: it claims the previous turn was incorrect. On a
cold-start session there is no prior turn to apply the correction
to, so the doctrine-aligned response is **not** to define what
correction is (that would be the DEFINITION path) but to
acknowledge receipt and state explicitly that no prior turn exists
in this session.
Surface format (fixed template, all atoms pack-sourced):
"correction received — pack-grounded ({pack_id}): {d1}; {d2}; {d3}.
No prior turn in this session to correct yet."
Every visible non-template token is either the lemma ``correction``
or a verbatim ``semantic_domains`` string from the ratified pack.
The trailing disclosure (``No prior turn in this session to correct
yet.``) is the constant trust-boundary label distinguishing this
cold-start acknowledgement from the post-correction teaching
repair path (``teaching/correction.py``) which engages once a
prior turn exists.
Returns ``None`` if the pack is unavailable or has no entry for
``correction`` callers fall through to the universal disclosure
unchanged.
"""
index = _pack_index()
domains = index.get("correction")
if not domains:
return None
head = "; ".join(domains[:3])
return (
f"correction received — pack-grounded ({PACK_ID}): {head}. "
f"No prior turn in this session to correct yet."
)
def pack_grounded_comparison_surface(
lemma_a: str, lemma_b: str
) -> str | None:

View file

@ -13,6 +13,7 @@ from algebra.versor import versor_condition
from chat.pack_grounding import (
pack_grounded_surface,
pack_grounded_comparison_surface,
pack_grounded_correction_surface,
PACK_ID as _COGNITION_PACK_ID,
)
from chat.teaching_grounding import (
@ -587,6 +588,15 @@ class ChatRuntime:
return None
surface = teaching_grounded_surface(lemma, intent.tag)
return (surface, "teaching") if surface is not None else None
# ADR-0053 — CORRECTION acknowledgement. Cold-start CORRECTION
# has no prior session turn to apply to; emit a pack-grounded
# surface that acknowledges the correction was received and
# states the missing-prior-turn constraint explicitly. The
# post-correction reviewed-teaching path (``teaching/correction.py``)
# engages only once a prior turn exists in the session.
if intent.tag is IntentTag.CORRECTION:
surface = pack_grounded_correction_surface()
return (surface, "pack") if surface is not None else None
if intent.tag not in (IntentTag.DEFINITION, IntentTag.RECALL):
return None
lemma = (intent.subject or "").strip()

View file

@ -0,0 +1,253 @@
# ADR-0053 — Cognition Lane Closure: Dev-Driven Corpus Expansion + CORRECTION Acknowledgement
**Status:** Accepted
**Date:** 2026-05-18
**Author:** Shay
---
## Context
[ADR-0052](./ADR-0052-teaching-grounded-surface.md) closed the third
grounding-source branch (teaching-grounded for CAUSE / VERIFICATION)
and lifted the public 13-case cognition split to 92.3% / 83.3%.
Before declaring the lane done, we ran the **dev split** for the
first time this session. Result:
| split (13 cases each) | intent_acc | surface_grounded | term_capture | versor_closure |
|---|---|---|---|---|
| public (post-0052) | 100% | 92.3% | 83.3% | 100% |
| dev (post-0052) | 100% | **69.2%** | 57.1% | 100% |
| gap | 0 | **23.1 pp** | 26.2 pp | 0 |
Two findings emerged from per-case inspection:
1. **The ADR-0048→0052 chain is not overfit.** Every dev case
where the necessary primitives existed (subject lemma in
`en_core_cognition_v1`, intent in covered set, matching teaching
chain authored) lifted exactly as predicted.
2. **The gap was content, not architecture.** 3 of 4 dev misses
were **missing teaching chains** for known pack lemmas
(`correction`, `creation`, `light` as VERIFICATION). 1 of 4 was
the **architectural CORRECTION gap** that public also exhibited
on `correction_specific_015`.
This ADR closes both kinds of gap with the minimum doctrine-aligned
work: expand the reviewed teaching corpus, and add the missing
intent-typed grounding branch.
---
## Decision
### Part 1 — Teaching corpus expansion (no code)
Add 7 new reviewed cognition chains to
`teaching/cognition_chains/cognition_chains_v1.jsonl`:
| chain_id | subject | intent | connective | object |
|---|---|---|---|---|
| `cause_correction_reveals_truth` | correction | cause | reveals | truth |
| `cause_creation_reveals_meaning` | creation | cause | reveals | meaning |
| `verification_light_reveals_truth` | light | verification | reveals | truth |
| `cause_truth_grounds_knowledge` | truth | cause | grounds | knowledge |
| `cause_wisdom_orders_judgment` | wisdom | cause | orders | judgment |
| `verification_truth_requires_evidence` | truth | verification | requires | evidence |
| `verification_wisdom_grounds_judgment` | wisdom | verification | grounds | judgment |
Each chain uses **only pack-resident lemmas** as subject and object
and a **recognised connective predicate** already in
`generate/semantic_templates.py:_PREDICATE_HUMANIZE`
(`reveals`, `requires`, `grounds`, `orders`). Every surface atom
remains lemma-or-pack-sourced; no synthesis is introduced.
The first three chains close the dev split misses
(`cause_correction_033`, `cause_creation_008`, `verification_light_017`);
the remaining four pre-empt the analogous holdout pattern
(CAUSE / VERIFICATION on `truth` and `wisdom`).
Provenance tag on the new chains is `adr-0053:reviewed:2026-05-18`
(the original three retain their `adr-0052` tag). The corpus loader
silently drops any chain whose subject or object is missing from
the pack — a load-time pack-consistency check that prevents
non-pack atoms from leaking into a teaching-grounded surface.
### Part 2 — CORRECTION acknowledgement branch
Add `pack_grounded_correction_surface()` to `chat/pack_grounding.py`
and a CORRECTION branch in
`chat/runtime.py:_maybe_pack_grounded_surface`.
**Surface format** (fixed template, all atoms pack-sourced):
```text
correction received — pack-grounded ({pack_id}): {d1}; {d2}; {d3}.
No prior turn in this session to correct yet.
```
The trailing disclosure is **deliberately distinct** from the
DEFINITION / RECALL / COMPARISON pack-grounded surfaces'
`"No session evidence yet."` A CORRECTION intent is meta-cognitive
— it asserts the *previous turn* was incorrect — so the
doctrine-aligned cold-start response is not to define what
"correction" is (the DEFINITION path does that), but to acknowledge
receipt and explicitly state that no prior session turn exists to
apply the correction to.
The branch fires for every CORRECTION-tagged intent on cold-start;
no subject lemma is consulted (the prompt's correction-target may
or may not be a single lemma, e.g. `"No, that's wrong"` has no
subject). The post-correction reviewed-teaching path
(`teaching/correction.py`) is unchanged and continues to engage
once a prior session turn exists.
### Part 3 — Diagnostic memory
Save the dev-split generalisation finding as a project memory
(`memory/dev-holdout-generalization-2026-05-18.md`) so future
sessions know:
- the ADR chain generalises;
- the next-cheapest pull on cognition metrics is corpus expansion,
not new branches;
- holdouts are not yet wired into the official `core eval cognition`
CLI — adding them is a separate ADR.
---
## Why this is doctrine-aligned
- **Reviewed memory expansion is the curriculum-teaching activity
CLAUDE.md item #6 calls for.** Each new chain is reviewed,
immutable, source-tagged. No bulk corpus ingest.
- **Every surface atom is pack-sourced or a closed-template
connective.** No LLM, no synthesis, no fabrication.
- **The CORRECTION branch does not invent a parallel teaching path.**
It emits a *receipt acknowledgement* surface; the actual reviewed-
teaching repair flow remains in `teaching/correction.py`.
- **Pack-consistency check on corpus load** (already present in
ADR-0052's `_corpus_index`) prevents pack/corpus drift — a chain
referencing a non-pack lemma is silently dropped, preserving the
"every atom is pack-sourced" invariant.
- **No algebra, no normalisation, no hot-path repair.** Both
changes are content + a stub-path branch.
---
## Characterisation — `core eval cognition`
A/B run on **both splits**, baseline = post-ADR-0052:
| Metric | Pre | Post | Δ |
|---------------------------|------|------|-------------|
| **PUBLIC** intent_accuracy | 100.0 % | 100.0 % | 0 |
| **PUBLIC** surface_groundedness | 92.3 % | **100.0 %** | **+7.7 pp** |
| **PUBLIC** term_capture_rate | 83.3 % | **91.7 %** | **+8.3 pp** |
| **PUBLIC** versor_closure_rate | 100.0 % | 100.0 % | 0 |
| **DEV** intent_accuracy | 100.0 % | 100.0 % | 0 |
| **DEV** surface_groundedness | 69.2 % | **100.0 %** | **+30.8 pp**|
| **DEV** term_capture_rate | 57.1 % | **78.6 %** | **+21.5 pp**|
| **DEV** versor_closure_rate | 100.0 % | 100.0 % | 0 |
| `versor_condition < 1e-6` | preserved | preserved | invariant |
**Both splits now hit 100% surface_groundedness.** Both splits
share the same lift mechanism — when the gate fires, the matching
intent-typed branch emits a pack-grounded or teaching-grounded
surface composed entirely of pack atoms; otherwise the universal
disclosure remains. No bias, no overfit, no per-split tuning.
---
## Consequences
### What changes
- `teaching/cognition_chains/cognition_chains_v1.jsonl` grows from
3 to 10 chains.
- `chat/pack_grounding.py` gains `pack_grounded_correction_surface()`.
- `chat/runtime.py:_maybe_pack_grounded_surface` gains a
CORRECTION branch.
- The cognition lane (both public and dev splits) is now saturated
on `surface_groundedness` (100% on both).
- A diagnostic memory records the dev/holdout finding for future
reference.
### What does not change
- `UnknownDomainGate` semantics unchanged.
- `_UNKNOWN_DOMAIN_SURFACE` constant retained; the path is still
the correct fall-through when no intent-typed branch applies.
- Safety / ethics refusal still takes priority above all grounded
surfaces.
- `teaching/correction.py` reviewed-teaching repair flow unchanged;
this ADR adds only the cold-start acknowledgement.
- `versor_condition(F) < 1e-6` invariant unaffected.
- All five core lanes remain green
(smoke 67 / cognition 121 / runtime 19 / teaching 17 / packs 6).
### Scope limits
- **Holdouts (19 cases) not yet in the official runner.** The
`--split` CLI option accepts only `{dev, public}`. Wiring
holdouts is a separate ADR — predicted lift pattern by
inspection is consistent with public/dev (CAUSE / VERIFICATION on
`truth` and `wisdom` will lift via the new chains; CORRECTION
will lift via the new branch).
- **CORRECTION subject lemma not yet captured in the surface.**
Holdout case `correction_truth_040` ("Actually, truth requires
evidence") expects `truth` in the surface as well as
`correction`. Detecting and embedding the corrected-subject
lemma in the acknowledgement is a candidate follow-up; the
current fixed-template surface satisfies the dev + public
contracts.
- **English only** (`en_core_cognition_v1`). Multilingual
cognition chains would follow the same pattern under a separate
ADR.
- **3 of the 7 new chains target holdout, not dev/public.** They
are still load-bearing because the corpus is shared across all
splits and pre-empting predictable misses now avoids a future
one-chain-at-a-time PR cadence.
---
## Cross-References
- [ADR-0048](./ADR-0048-pack-grounded-surface.md) /
[ADR-0050](./ADR-0050-pack-grounded-comparison.md) /
[ADR-0052](./ADR-0052-teaching-grounded-surface.md) — the
three preceding grounding-source branches this ADR completes.
- [ADR-0018](./ADR-0018-tool-use-scope.md) — teaching store and
CORRECTION intent classification this ADR consults.
- `teaching/correction.py` — the reviewed-teaching repair path
that engages once a prior session turn exists; not modified
by this ADR.
---
## Verification
```
tests/test_pack_grounded_correction.py — 15 tests, all green
tests/test_pack_grounded_comparison.py — 15 pre-existing tests still green
tests/test_pack_grounding.py — 18 pre-existing tests still green
tests/test_teaching_grounding.py — 22 pre-existing tests still green
tests/test_intent_subject_extraction.py — 30 pre-existing tests still 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 teaching 17 passed
core test --suite packs 6 passed
core eval cognition (post-0053):
public dev
intent_accuracy 100.0% 100.0%
surface_groundedness 100.0% 100.0% ← BOTH SPLITS SATURATED
term_capture_rate 91.7% 78.6%
versor_closure_rate 100.0% 100.0%
```
The non-negotiable field invariant (`versor_condition(F) < 1e-6`)
is preserved: this ADR adds reviewed-memory content and one
stub-path branch — no algebra, no normalisation, no field update.

View file

@ -62,6 +62,7 @@ ADRs record significant architectural decisions: what was decided, why, what alt
| [ADR-0050](ADR-0050-pack-grounded-comparison.md) | Pack-grounded surface for cold-start COMPARISON | Accepted (2026-05-18) |
| [ADR-0051](ADR-0051-trust-boundary-hardening.md) | Trust-boundary hardening pass | Accepted (2026-05-18) |
| [ADR-0052](ADR-0052-teaching-grounded-surface.md) | Teaching-grounded surface for cold-start CAUSE / VERIFICATION | Accepted (2026-05-18) |
| [ADR-0053](ADR-0053-cognition-lane-closure.md) | Cognition lane closure: dev-driven corpus expansion + CORRECTION acknowledgement | Accepted (2026-05-18) |
---

View file

@ -0,0 +1,145 @@
{
"cases": [
{
"case_id": "cause_correction_033",
"category": "cause",
"intent_correct": true,
"surface": "I don't know — insufficient grounding for that yet.",
"surface_contains_pass": false,
"trace_hash": "ab0dbfb818254b25364c6f000839f99f65ff3680c7ae0db6d134bedbb2b6923b",
"versor_closure": true,
"versor_condition": 4.6e-08
},
{
"case_id": "cause_creation_008",
"category": "cause",
"intent_correct": true,
"surface": "I don't know — insufficient grounding for that yet.",
"surface_contains_pass": false,
"trace_hash": "5f3cf11ba4456ebcb2f89ec217817210e2f1d1df8fc3ff4714894089fc4537c4",
"versor_closure": true,
"versor_condition": 2.3e-08
},
{
"case_id": "comparison_knowledge_wisdom_029",
"category": "comparison",
"intent_correct": true,
"surface": "knowledge (cognition.knowledge; epistemic.ground) contrasts with wisdom (cognition.wisdom; epistemic.judgment) — pack-grounded (en_core_cognition_v1). No session evidence yet.",
"surface_contains_pass": true,
"trace_hash": "1b362aca2c7d93db9d499cb1d65dcbc5a5fec4d111afcefde74992ba43c57a47",
"versor_closure": true,
"versor_condition": 3.6e-08
},
{
"case_id": "correction_basic_014",
"category": "correction",
"intent_correct": true,
"surface": "I don't know — insufficient grounding for that yet.",
"surface_contains_pass": false,
"trace_hash": "acc36c9c27f20afa1a5162c9808a3e3935c838eda736661349d3894cdaeb2065",
"versor_closure": true,
"versor_condition": 7.9e-08
},
{
"case_id": "definition_distinction_025",
"category": "definition",
"intent_correct": true,
"surface": "distinction — pack-grounded (en_core_cognition_v1): cognition.distinction; relation.contrast; reason.boundary. No session evidence yet.",
"surface_contains_pass": true,
"trace_hash": "b5a4ea1fbb1685dbee920774c9f35e35760302de1bbd8407c42e69913ea0dcdf",
"versor_closure": true,
"versor_condition": 4e-08
},
{
"case_id": "definition_evidence_021",
"category": "definition",
"intent_correct": true,
"surface": "evidence — pack-grounded (en_core_cognition_v1): cognition.evidence; epistemic.ground; reason.support. No session evidence yet.",
"surface_contains_pass": true,
"trace_hash": "20c00f392d10574e5e474d8f707f080041e2582d7907a40a0a1bfbabe2a3e7ca",
"versor_closure": true,
"versor_condition": 7.8e-08
},
{
"case_id": "definition_identity_027",
"category": "definition",
"intent_correct": true,
"surface": "identity — pack-grounded (en_core_cognition_v1): cognition.identity; identity.stable; runtime.contract. No session evidence yet.",
"surface_contains_pass": true,
"trace_hash": "e55b66311f9bd348dc85f689fb77114626fc0e798932b05cef40693e56f12489",
"versor_closure": true,
"versor_condition": 3.8e-08
},
{
"case_id": "definition_inference_022",
"category": "definition",
"intent_correct": true,
"surface": "inference — pack-grounded (en_core_cognition_v1): cognition.inference; logic.derivation; reason.step. No session evidence yet.",
"surface_contains_pass": true,
"trace_hash": "43ccdddd28a523c814f206d9d7543ef91cf5f9001e49efed8aec5a1b9bd90533",
"versor_closure": true,
"versor_condition": 7.4e-08
},
{
"case_id": "definition_judgment_044",
"category": "definition",
"intent_correct": true,
"surface": "judgment — pack-grounded (en_core_cognition_v1): cognition.judgment; epistemic.evaluation; reason.decision. No session evidence yet.",
"surface_contains_pass": true,
"trace_hash": "74a7be94c00a82faac020b8e7be98c666243396056a51864b6a20ebaec51b9d4",
"versor_closure": true,
"versor_condition": 1.97e-07
},
{
"case_id": "procedure_compare_011",
"category": "procedure",
"intent_correct": true,
"surface": "I don't know — insufficient grounding for that yet.",
"surface_contains_pass": true,
"trace_hash": "f1d263ec417f7363667065958e120334b27091b16fd0c535f30081b210862acb",
"versor_closure": true,
"versor_condition": 8.5e-08
},
{
"case_id": "recall_knowledge_039",
"category": "recall",
"intent_correct": true,
"surface": "knowledge — pack-grounded (en_core_cognition_v1): cognition.knowledge; epistemic.ground; memory.semantic. No session evidence yet.",
"surface_contains_pass": true,
"trace_hash": "a4dc6732968e036540dab339592f40e473e72e328df042e4e0f629a4a5b2160d",
"versor_closure": true,
"versor_condition": 5.9e-08
},
{
"case_id": "unknown_evidence_042",
"category": "unknown",
"intent_correct": true,
"surface": "I don't know — insufficient grounding for that yet.",
"surface_contains_pass": true,
"trace_hash": "1ede8bec4945c6dca9d5929168ef88336009fef173c326bfa99fc9e9194a0372",
"versor_closure": true,
"versor_condition": 2e-08
},
{
"case_id": "verification_light_017",
"category": "verification",
"intent_correct": true,
"surface": "I don't know — insufficient grounding for that yet.",
"surface_contains_pass": false,
"trace_hash": "8406a41fc11ffe5a44daf87f112c523dbd6ac89d67c8004ea7e2943971c64705",
"versor_closure": true,
"versor_condition": 9.2e-08
}
],
"lane": "cognition",
"metrics": {
"intent_accuracy": 1.0,
"surface_groundedness": 0.6923,
"term_capture_rate": 0.5714,
"total": 13,
"versor_closure_rate": 1.0
},
"split": "dev",
"timestamp": "2026-05-18T14:34:12.261381+00:00",
"version": "v1"
}

View file

@ -1,3 +1,10 @@
{"chain_id":"cause_light_reveals_truth","subject":"light","intent":"cause","connective":"reveals","object":"truth","domains_subject_k":2,"domains_object_k":1,"provenance":"adr-0052:reviewed:2026-05-18"}
{"chain_id":"cause_knowledge_requires_evidence","subject":"knowledge","intent":"cause","connective":"requires","object":"evidence","domains_subject_k":2,"domains_object_k":1,"provenance":"adr-0052:reviewed:2026-05-18"}
{"chain_id":"verification_memory_requires_recall","subject":"memory","intent":"verification","connective":"requires","object":"recall","domains_subject_k":2,"domains_object_k":1,"provenance":"adr-0052:reviewed:2026-05-18"}
{"chain_id":"cause_correction_reveals_truth","subject":"correction","intent":"cause","connective":"reveals","object":"truth","domains_subject_k":2,"domains_object_k":1,"provenance":"adr-0053:reviewed:2026-05-18"}
{"chain_id":"cause_creation_reveals_meaning","subject":"creation","intent":"cause","connective":"reveals","object":"meaning","domains_subject_k":2,"domains_object_k":1,"provenance":"adr-0053:reviewed:2026-05-18"}
{"chain_id":"verification_light_reveals_truth","subject":"light","intent":"verification","connective":"reveals","object":"truth","domains_subject_k":2,"domains_object_k":1,"provenance":"adr-0053:reviewed:2026-05-18"}
{"chain_id":"cause_truth_grounds_knowledge","subject":"truth","intent":"cause","connective":"grounds","object":"knowledge","domains_subject_k":2,"domains_object_k":1,"provenance":"adr-0053:reviewed:2026-05-18"}
{"chain_id":"cause_wisdom_orders_judgment","subject":"wisdom","intent":"cause","connective":"orders","object":"judgment","domains_subject_k":2,"domains_object_k":1,"provenance":"adr-0053:reviewed:2026-05-18"}
{"chain_id":"verification_truth_requires_evidence","subject":"truth","intent":"verification","connective":"requires","object":"evidence","domains_subject_k":2,"domains_object_k":1,"provenance":"adr-0053:reviewed:2026-05-18"}
{"chain_id":"verification_wisdom_grounds_judgment","subject":"wisdom","intent":"verification","connective":"grounds","object":"judgment","domains_subject_k":2,"domains_object_k":1,"provenance":"adr-0053:reviewed:2026-05-18"}

View file

@ -0,0 +1,145 @@
"""ADR-0053 — pack-grounded CORRECTION acknowledgement tests.
Contract pinned here:
- ``pack_grounded_correction_surface()`` returns a deterministic
surface composed entirely of: the literal token ``correction``,
verbatim ``semantic_domains`` strings from the pack, and a fixed
template (``"correction received — pack-grounded ({pack_id}): ...
No prior turn in this session to correct yet."``).
- The cold-start CORRECTION intent routes through this branch in
``_maybe_pack_grounded_surface`` and emits the acknowledgement
surface with ``grounding_source="pack"``.
- Distinct from DEFINITION-of-correction: the CORRECTION
acknowledgement explicitly notes the missing prior turn.
- Refusal still takes priority CORRECTION acknowledgement never
bypasses a SafetyVerdict violation.
"""
from __future__ import annotations
import pytest
from chat.pack_grounding import (
PACK_ID,
pack_grounded_correction_surface,
)
from chat.runtime import ChatRuntime, _UNKNOWN_DOMAIN_SURFACE
# ---------------------------------------------------------------------------
# pack_grounded_correction_surface — pure-function contract
# ---------------------------------------------------------------------------
def test_correction_surface_is_returned() -> None:
surface = pack_grounded_correction_surface()
assert surface is not None
def test_correction_surface_contains_literal_correction() -> None:
surface = pack_grounded_correction_surface()
assert surface is not None
assert "correction" in surface
def test_correction_surface_names_pack_id() -> None:
surface = pack_grounded_correction_surface()
assert surface is not None
assert PACK_ID in surface
def test_correction_surface_marks_missing_prior_turn() -> None:
"""The CORRECTION acknowledgement must be distinct from the
DEFINITION-of-correction surface: it explicitly states there is no
prior turn in this session to correct."""
surface = pack_grounded_correction_surface()
assert surface is not None
assert "No prior turn in this session to correct yet." in surface
# Must NOT use the DEFINITION-style trailing disclosure:
assert "No session evidence yet." not in surface
def test_correction_surface_is_deterministic() -> None:
a = pack_grounded_correction_surface()
b = pack_grounded_correction_surface()
assert a == b
def test_correction_surface_atoms_are_verbatim_from_pack() -> None:
"""Every visible non-template token must be either the literal
``"correction"`` or a verbatim ``semantic_domains`` string from
the pack no synthesis."""
from chat.pack_grounding import _pack_index
surface = pack_grounded_correction_surface()
assert surface is not None
index = _pack_index()
domains = index["correction"][:3]
for domain in domains:
assert domain in surface
# ---------------------------------------------------------------------------
# ChatRuntime integration — cold-start CORRECTION path
# ---------------------------------------------------------------------------
@pytest.mark.parametrize(
"prompt",
[
"No, that's wrong",
"No, correction means reviewed repair",
"Actually, truth requires evidence",
"Incorrect",
"correction: that needs review",
],
)
def test_cold_start_correction_routes_through_pack_grounded(prompt: str) -> None:
rt = ChatRuntime()
resp = rt.chat(prompt)
assert resp.grounding_source == "pack"
assert "correction" in resp.surface
assert "No prior turn in this session to correct yet." in resp.surface
def test_correction_does_not_use_universal_disclosure() -> None:
rt = ChatRuntime()
resp = rt.chat("No, that's wrong")
assert resp.surface != _UNKNOWN_DOMAIN_SURFACE
def test_turn_event_carries_grounding_source_on_correction() -> None:
rt = ChatRuntime()
rt.chat("No, that's wrong")
last_event = rt.turn_log[-1]
assert getattr(last_event, "grounding_source", None) == "pack"
def test_correction_pack_grounded_passes_verdict_audit() -> None:
rt = ChatRuntime()
resp = rt.chat("No, that's wrong")
assert resp.safety_verdict is not None
assert resp.ethics_verdict is not None
# ---------------------------------------------------------------------------
# Doctrine — distinct from DEFINITION-of-correction
# ---------------------------------------------------------------------------
def test_correction_surface_distinct_from_definition_of_correction() -> None:
"""Compare ``"What is correction?"`` (DEFINITION) vs ``"No, that's
wrong"`` (CORRECTION). Both go through the pack-grounded branch
but emit different surfaces the CORRECTION acknowledgement
notes the missing prior turn; the DEFINITION does not."""
rt_def = ChatRuntime()
def_resp = rt_def.chat("What is correction?")
rt_corr = ChatRuntime()
corr_resp = rt_corr.chat("No, that's wrong")
assert def_resp.surface != corr_resp.surface
assert "No session evidence yet." in def_resp.surface
assert "No prior turn in this session to correct yet." in corr_resp.surface