core/tests/test_register_substantive_consumption.py
Shay 5a78b0e37b feat(register): ADR-0077 — substantive register knobs + layering boundary (R6)
R5 (ADR-0072) shipped the register *machinery*; ADR-0074's orthogonality
tour proved the axis was decoratively orthogonal to anchor-lens but
inspection of the cognition-eval surfaces revealed two structural gaps:

* On pack-grounded DEFINITION/RECALL/COMPARISON composers, the only
  realizer override any register consumed was `disclosure_domain_count`
  — which only fires on the no-gloss disclosure path.  Under terse_v1,
  every gloss-DEFINITION cell was byte-identical to default_neutral_v1.
* The register-tour's `surfaces_vary_at_least_once` gate could be
  satisfied by convivial's decorative wrapper alone, masking that
  regression in CI.

R6 closes both:

Layering separation (the load-bearing fix):
* New TurnEvent/ChatResponse field `register_canonical_surface` carries
  the composer output BEFORE any register transformation.  The pipeline
  hashes this field for `trace_hash`, preserving R5's invariant that
  per-prompt trace_hash is CONSTANT across registers even while
  substantive transforms produce visibly different surfaces.

Substantive transforms (`chat/register_substantive.py`):
* terse_v1 gains 3 bool knobs: `drop_provenance_tag`, `compress_gloss`,
  `drop_articles` — all pure regex transforms on the canonical surface.
* convivial_v1 gains `append_semantic_domain_clause` — appends a single
  bounded "Related: <atom>." clause using the lemma's pack atoms.
* default_neutral_v1 leaves overrides empty; substantive transform is
  byte-identical no-op (preserves `byte_identity_null_lift`).
* C1 (ADR-0075) safety preserved: drop_articles refuses to drop
  articles following `not` (avoids R3 violations); no knob combination
  trips R2/R3.

Strengthened tour gate (`evals/register_tour/run_tour.py`):
* Replaces `surfaces_vary_at_least_once` with two falsifiable claims:
  - `terse_substantively_differs_from_neutral_on_pack_grounded_definition`
  - `convivial_substantively_differs_from_neutral_on_pack_grounded_definition`
  Both restrict to DEFINITION+pack-grounded cells and require
  difference beyond whitespace/punctuation.
* New claim `register_canonical_surfaces_identical` directly proves
  the layering separation.
* Preserves R5's `all_grounding_sources_identical` +
  `all_trace_hashes_identical`.

Pack ratification:
* Loader widened to accept `bool` for closed-set R6 keys
  (drop_provenance_tag / compress_gloss / drop_articles /
  append_semantic_domain_clause).
* `_KNOWN_OVERRIDE_KEYS` ratify gate extended with same.
* terse_v1 + convivial_v1 reratified with new knobs; companion
  mastery reports re-sealed.  default_neutral_v1 unchanged.

Invariants pinned:
* `invariant_register_canonical_surface_constant_across_registers` (new)
* `invariant_terse_substantively_distinct_from_neutral` (new)
* `invariant_convivial_substantively_distinct_from_neutral` (new)
* `invariant_realizer_no_illegal_articulation` (C1, preserved)
* `invariant_realizer_guard_byte_identity_on_currently_passing_cases`
  (C1, preserved)

Verification:
* `core eval cognition`: 100.0% / 91.7% / 100.0% / 100.0% — byte-
  identical under default_neutral_v1.
* `core demo register-tour`: all 5 claims green, exit 0.
* `core demo anchor-lens-tour`: green (no anchor-lens code touched).
* `core demo orthogonality-tour`: green (5/5 claims).
* Full lane: 2858 passed, 1 pre-existing failure
  (test_all_preamble_explains_combined_run, carried forward
  unchanged from main).  56 new R6 tests across three files.
2026-05-19 23:39:11 -07:00

437 lines
14 KiB
Python

"""ADR-0077 (R6) — per-knob unit + end-to-end coverage of
``chat/register_substantive.py``.
Two test families:
* Pure-unit tests against synthetic canonical surfaces — assert each
knob is the deterministic string transform documented in the ADR,
with no dependency on the runtime.
* End-to-end tests run a real ChatRuntime under each ratified
register and assert the user-facing surface matches the post-
substantive expectation.
C1 (ADR-0075) preservation: every R6 knob combination must produce a
surface that the realizer guard accepts. This module pins that
contract by running the guard against every produced surface.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Mapping
import pytest
from chat.register_substantive import apply_substantive_register
from chat.runtime import ChatRuntime
from core.cognition.pipeline import CognitiveTurnPipeline
from core.config import RuntimeConfig
from generate.realizer_guard import check_surface
from packs.register.loader import RegisterPack, load_register_pack
# ---------- Fake register for synthetic unit coverage ----------
@dataclass(frozen=True)
class _FakeMarkers:
openings: tuple[str, ...] = ()
transitions: tuple[str, ...] = ()
closings: tuple[str, ...] = ()
def is_empty(self) -> bool:
return True
@dataclass(frozen=True)
class _FakeRegister:
"""Minimal duck-type that ``apply_substantive_register`` accepts.
Only ``realizer_overrides`` is read in the substantive path.
"""
realizer_overrides: Mapping[str, object] = field(default_factory=dict)
discourse_markers: _FakeMarkers = field(default_factory=_FakeMarkers)
def _r(overrides: Mapping[str, object]) -> _FakeRegister:
return _FakeRegister(realizer_overrides=dict(overrides))
_DEF_CANONICAL = (
"Light is a source of revelation that makes things knowable. "
"pack-grounded (en_core_cognition_v1)."
)
_DEF_CANONICAL_LENS = (
"Light is a source of revelation that makes things knowable. "
"pack-grounded (en_core_cognition_v1) [lens(grc_logos_v1):systematic]."
)
_CMP_CANONICAL = (
"light (cognition.illumination; logos.core) contrasts with "
"truth (cognition.truth; logos.core) — pack-grounded "
"(en_core_cognition_v1). No session evidence yet."
)
_DOT_CANONICAL = (
"light — pack-grounded (en_core_cognition_v1): "
"cognition.illumination; logos.core; cognition.knowledge. "
"No session evidence yet."
)
# ---------- No-op contract ----------
def test_empty_overrides_is_byte_identical():
"""The default register (empty overrides) must be a no-op."""
out = apply_substantive_register(_DEF_CANONICAL, _r({}))
assert out == _DEF_CANONICAL
def test_all_knobs_false_is_byte_identical():
"""Explicit False values must also be a no-op — the ``.get(key)``
lookup returns ``False`` which is falsy."""
out = apply_substantive_register(
_DEF_CANONICAL,
_r({
"drop_provenance_tag": False,
"compress_gloss": False,
"drop_articles": False,
"append_semantic_domain_clause": False,
}),
)
assert out == _DEF_CANONICAL
def test_empty_canonical_is_byte_identical():
out = apply_substantive_register("", _r({"drop_provenance_tag": True}))
assert out == ""
# ---------- drop_provenance_tag ----------
def test_drop_provenance_strips_trailing_gloss_clause():
out = apply_substantive_register(
_DEF_CANONICAL, _r({"drop_provenance_tag": True}),
)
assert "pack-grounded" not in out
assert out.endswith("knowable.")
assert "..," not in out and ".." not in out # no double-period bug
def test_drop_provenance_preserves_lens_annotation():
out = apply_substantive_register(
_DEF_CANONICAL_LENS, _r({"drop_provenance_tag": True}),
)
assert "pack-grounded" not in out
assert "[lens(grc_logos_v1):systematic]" in out
def test_drop_provenance_strips_infix_em_dash_form():
out = apply_substantive_register(
_CMP_CANONICAL, _r({"drop_provenance_tag": True}),
)
assert "pack-grounded" not in out
# Em-dash before the provenance is consumed by the infix regex.
assert " — pack-grounded" not in out
assert "contrasts with truth" in out
assert "No session evidence yet" in out
def test_drop_provenance_strips_dotted_disclosure_form():
out = apply_substantive_register(
_DOT_CANONICAL, _r({"drop_provenance_tag": True}),
)
assert "pack-grounded" not in out
assert out.startswith("light:") # em-dash + provenance gone; colon kept
# ---------- compress_gloss ----------
def test_compress_gloss_replaces_is_a():
out = apply_substantive_register(
"Light is a source of revelation.", _r({"compress_gloss": True}),
)
assert out == "Light: source of revelation."
def test_compress_gloss_replaces_is_an():
out = apply_substantive_register(
"Answer is an example of speech.", _r({"compress_gloss": True}),
)
assert out == "Answer: example of speech."
def test_compress_gloss_replaces_is_the():
out = apply_substantive_register(
"Beginning is the origin point.", _r({"compress_gloss": True}),
)
assert out == "Beginning: origin point."
def test_compress_gloss_replaces_bare_is():
out = apply_substantive_register(
"Cause is that which produces effect.",
_r({"compress_gloss": True}),
)
assert out == "Cause: that which produces effect."
def test_compress_gloss_only_first_occurrence():
"""Multiple ``is`` tokens must not all collapse — only the gloss
opener. Mangling a subordinate clause's ``is`` would be a bug."""
out = apply_substantive_register(
"X is a Y. The reason is clear.", _r({"compress_gloss": True}),
)
assert out == "X: Y. The reason is clear."
def test_compress_gloss_noop_on_comparison():
"""COMPARISON form has no ``is`` — must be byte-identical."""
out = apply_substantive_register(
_CMP_CANONICAL, _r({"compress_gloss": True}),
)
assert out == _CMP_CANONICAL
# ---------- drop_articles ----------
def test_drop_articles_strips_mid_sentence_a():
out = apply_substantive_register(
"Light reveals a truth in this case.", _r({"drop_articles": True}),
)
assert out == "Light reveals truth in this case."
def test_drop_articles_strips_mid_sentence_the():
out = apply_substantive_register(
"Light reveals the truth.", _r({"drop_articles": True}),
)
assert out == "Light reveals truth."
def test_drop_articles_preserves_article_after_not():
"""C1 R3 safety: must not collapse ``is not a claim`` to ``is not
claim`` because R3 fires when a be-negation is followed by a VERB.
``claim`` is a NOUN so R3 wouldn't fire anyway, but the lookbehind
guards us against a more dangerous case in the future."""
out = apply_substantive_register(
"Light is not a claim.", _r({"drop_articles": True}),
)
assert out == "Light is not a claim."
def test_drop_articles_collapses_double_space_artifact():
"""Adjacent removals must not leave a double space in the output."""
out = apply_substantive_register(
"Walks the path of a sage.", _r({"drop_articles": True}),
)
assert " " not in out
assert out == "Walks path of sage."
# ---------- append_semantic_domain_clause ----------
def test_append_picks_lex_first_unused_atom():
out = apply_substantive_register(
"Light is the source.",
_r({"append_semantic_domain_clause": True}),
semantic_domains=("logos.core", "cognition.illumination"),
)
# Sorted atoms: ("cognition.illumination", "logos.core").
# Both are absent from canonical, so the first lex-sorted wins.
assert out.endswith(" Related: cognition.illumination.")
def test_append_skips_atom_already_in_canonical():
out = apply_substantive_register(
"Light is a source in cognition.illumination.",
_r({"append_semantic_domain_clause": True}),
semantic_domains=("cognition.illumination", "logos.core"),
)
assert out.endswith(" Related: logos.core.")
def test_append_noop_when_all_atoms_already_used():
canonical = "Light: covers logos.core cognition.illumination."
out = apply_substantive_register(
canonical,
_r({"append_semantic_domain_clause": True}),
semantic_domains=("cognition.illumination", "logos.core"),
)
assert out == canonical
def test_append_noop_when_no_atoms_supplied():
out = apply_substantive_register(
_DEF_CANONICAL,
_r({"append_semantic_domain_clause": True}),
semantic_domains=(),
)
assert out == _DEF_CANONICAL
# ---------- Knob composition (terse_v1 combined) ----------
def test_terse_full_combo_def_form():
"""compress_gloss + drop_provenance_tag + drop_articles together
on the canonical DEFINITION form."""
out = apply_substantive_register(
_DEF_CANONICAL,
_r({
"compress_gloss": True,
"drop_provenance_tag": True,
"drop_articles": True,
}),
)
# compress: "Light is a source ... knowable." -> "Light: source ... knowable."
# drop_articles: removes leftover "a/the" articles (none in this gloss).
# drop_provenance_tag: trailing pack-grounded clause removed.
assert out == "Light: source of revelation that makes things knowable."
def test_terse_full_combo_with_lens():
"""Lens annotation must survive the terse combo."""
out = apply_substantive_register(
_DEF_CANONICAL_LENS,
_r({
"compress_gloss": True,
"drop_provenance_tag": True,
"drop_articles": True,
}),
)
assert "[lens(grc_logos_v1):systematic]" in out
assert "pack-grounded" not in out
# ---------- Guard safety (C1 preservation) ----------
def _trivial_pos_lookup(_token: str) -> None:
"""Pack-less POS lookup — returns None for every token. The C1
guard then fails-open on every unknown token, which is what we
want for synthetic unit assertions.
"""
return None
@pytest.mark.parametrize("knobs", [
{"compress_gloss": True},
{"drop_provenance_tag": True},
{"drop_articles": True},
{"append_semantic_domain_clause": True},
{
"compress_gloss": True,
"drop_provenance_tag": True,
"drop_articles": True,
},
])
def test_every_knob_combination_passes_guard(knobs):
"""invariant_realizer_no_illegal_articulation preservation —
no R6 knob combination produces a surface that trips R2/R3."""
domains = ("cognition.illumination", "logos.core")
for canonical in (_DEF_CANONICAL, _DEF_CANONICAL_LENS, _CMP_CANONICAL, _DOT_CANONICAL):
out = apply_substantive_register(
canonical, _r(knobs), semantic_domains=domains,
)
v = check_surface(out, pos_lookup=_trivial_pos_lookup)
assert v.status == "ok", (
f"knobs={knobs} produced guard-rejected surface "
f"{out!r} from canonical {canonical!r}: {v}"
)
# ---------- End-to-end against real packs + runtime ----------
@pytest.fixture(scope="module")
def neutral_def_surface() -> str:
rt = ChatRuntime(config=RuntimeConfig(register_pack_id="default_neutral_v1"))
pipe = CognitiveTurnPipeline(runtime=rt)
pipe.run("What is light?")
return rt.turn_log[-1].surface
def test_e2e_terse_drops_provenance_tag():
rt = ChatRuntime(config=RuntimeConfig(register_pack_id="terse_v1"))
pipe = CognitiveTurnPipeline(runtime=rt)
pipe.run("What is light?")
te = rt.turn_log[-1]
assert "pack-grounded" not in te.surface
assert te.register_canonical_surface != ""
assert "pack-grounded" in te.register_canonical_surface
def test_e2e_terse_compresses_gloss():
rt = ChatRuntime(config=RuntimeConfig(register_pack_id="terse_v1"))
pipe = CognitiveTurnPipeline(runtime=rt)
pipe.run("What is light?")
te = rt.turn_log[-1]
assert "Light: " in te.surface
# Gloss compression strips " is a "; raw " is a " must NOT survive
# in the user-facing surface under terse.
assert " is a " not in te.surface
def test_e2e_terse_drops_articles():
rt = ChatRuntime(config=RuntimeConfig(register_pack_id="terse_v1"))
pipe = CognitiveTurnPipeline(runtime=rt)
pipe.run("What is light?")
te = rt.turn_log[-1]
# Should not contain standalone mid-sentence articles in the
# compressed surface. ``" a "`` and ``" the "`` are mid-sentence
# markers we expect to be absent in the compressed gloss.
assert " a " not in te.surface
assert " the " not in te.surface
def test_e2e_convivial_appends_related_clause(neutral_def_surface: str):
rt = ChatRuntime(config=RuntimeConfig(register_pack_id="convivial_v1"))
pipe = CognitiveTurnPipeline(runtime=rt)
pipe.run("What is light?")
te = rt.turn_log[-1]
assert "Related: " in te.surface
# The Related clause must be derived from a pack atom — atoms in
# the cognition pack are dotted ``domain.subdomain`` form (e.g.
# ``cognition.illumination``, ``logos.core``). Match a dotted
# atom inside the Related clause specifically.
import re
m = re.search(r"Related: ([\w]+\.[\w.]+)\.", te.surface)
assert m is not None, f"no dotted atom in Related clause: {te.surface!r}"
assert "." in m.group(1)
def test_e2e_neutral_unchanged_by_r6(neutral_def_surface: str):
"""default_neutral_v1 has no R6 knobs → surface must be identical
to the pre-R6 byte-for-byte expected gloss."""
expected = (
"Light is a source of revelation that makes things knowable. "
"pack-grounded (en_core_cognition_v1)."
)
assert neutral_def_surface == expected
# ---------- Pack-loaded knob schema ----------
@pytest.mark.parametrize("register_id,expected_keys", [
("default_neutral_v1", set()),
(
"terse_v1",
{"compress_gloss", "disclosure_domain_count", "drop_articles",
"drop_provenance_tag"},
),
("convivial_v1", {"append_semantic_domain_clause"}),
])
def test_register_pack_carries_expected_r6_keys(
register_id: str, expected_keys: set[str],
):
"""Pin the on-disk pack contents so future re-ratification can't
silently drop the R6 knobs."""
pack = load_register_pack(register_id)
actual_keys = set(pack.realizer_overrides.keys())
assert actual_keys == expected_keys