feat(anchor_lens): ADR-0073c — L1.3 first lenses + composer wiring
L1.3 of the anchor-lens inside-out rollout — first substantive
surface lift on the substantive axis. Two ratified non-trivial
lenses engage on cognition-pack lemmas via the alignment graph,
appending [lens(<id>):<mode>] annotations to the existing
pack-grounded surface.
Two ratified lenses
grc_logos_v1 (Greek substrate)
primary_substrate : "grc"
semantic_domain_preferences: ["logos.episteme.systematic_knowledge"]
cognitive_mode_label : "systematic"
Engages on en "knowledge" via grc-core-cog-021 (ἐπιστήμη) →
en-core-cog-007 alignment edge.
he_logos_v1 (Hebrew substrate)
primary_substrate : "he"
semantic_domain_preferences: ["logos.aletheia.verity"]
cognitive_mode_label : "covenant-verity"
Engages on en "truth" via he-core-cog-002 (אמת) →
en-core-cog-002 alignment edge.
Both ratified under method anchor_lens_lifts_proposition.
Engagement rule (single)
1. Resolve en_lemma → entry_id (cognition pack).
2. For each substrate pack matching lens.primary_substrate, load
alignment.jsonl; find edges where target_id == entry_id.
3. For each such substrate lemma, if any atom in its
semantic_domains ∈ lens.semantic_domain_preferences → engage.
4. No match → None (no annotation; byte-identical surface).
The pivot is shared semantic_domain atoms surfaced via the
alignment graph — exactly the language-neutral commitment from
ADR-0073. Engagement never touches non-English surface text;
entry_ids and atom strings only.
Surface lift
no-lens : "Knowledge is X. pack-grounded (en_core_cognition_v1)."
lens-on : "Knowledge is X. pack-grounded (en_core_cognition_v1) [lens(grc_logos_v1):systematic]."
Annotation between existing provenance and trailing period.
Both metadata fields are ASCII-bounded ≤64 chars at the loader
level, so the annotation can never carry non-ASCII.
Scope deliberately narrow
L1.3 wiring restricted to pack_grounded_surface /
build_pack_surface_candidate (DEFINITION/RECALL only). Other
composers (COMPARISON / CORRECTION / PROCEDURE / NARRATIVE /
EXAMPLE / CAUSE / VERIFICATION) accept the anchor_lens kwarg via
forward-compat default UNANCHORED but do not yet consume it.
L1.3b or later broadens to those intent shapes.
Ratify gate widening
Non-null lenses must:
- have primary_substrate ∈ {grc, he, en}
- have a non-empty cognitive_mode_label
- every preferred atom must exist in at least one lemma of the
named substrate (trust boundary: operators cannot ship a lens
pointing at atoms not on disk).
Method: anchor_lens_lifts_proposition. Null lenses still ratify
under byte_identity_null_lift (L1.2 method).
Seam allow-list widening
Truth-path modules (cognition / trace / pipeline / intent /
propagation / vault / algebra) still refused. Composer-side
imports from chat/pack_grounding.py now permitted — the same way
ADR-0069's R2 widened the register seam.
New invariants pinned (3)
tests/test_anchor_lens_engagement_unit.py (14 tests) — resolver
returns mode label only on intended substrate × en lemma pair;
case-insensitive; engagement None under null lens; synthetic
lens with unmatched atom returns None; annotation is pure ASCII.
tests/test_anchor_lens_lifts_proposition.py (17 tests) — grc
engages on knowledge only, he engages on truth only,
cross-lens isolation, three-way distinctness, replay determinism
per (lens × prompt), register-tour seam holds within each lens
scope (orthogonality CI-pinned, parametrized over 4 lens
choices).
tests/test_anchor_lens_no_glyph_leak.py (5 tests) — hard
block-scoped gate: Greek (U+0370..03FF, U+1F00..1FFF), Hebrew
(U+0590..05FF), Syriac, Arabic. Stylistic punctuation
(em-dash etc.) explicitly allowed; em-dash predates L1.3 by a
wide margin and is not a substrate-leak risk. Tested per-lens
across every cognition case + direct lens-metadata ASCII check.
Lane evidence
74 anchor-lens tests pass (37 from L1.2 + 37 new).
python -m core.cli eval cognition → public 100/100/91.7/100
byte-identical (lens=None / default_unanchored_v1).
core demo register-tour --json → all_claims_supported: True
(R5 seam still holds; L1.3 doesn't perturb presentation axis).
Full lane: 2706 passed / 4 skipped / 1 pre-existing failure
(+37 over L1.2's 2669; the one failure remains
test_all_preamble_explains_combined_run, unrelated).
Files
packs/anchor_lens/grc_logos_v1.json NEW
packs/anchor_lens/grc_logos_v1.mastery_report.json NEW
packs/anchor_lens/he_logos_v1.json NEW
packs/anchor_lens/he_logos_v1.mastery_report.json NEW
scripts/ratify_anchor_lens_packs.py EDIT
LENS_IDS adds grc_logos_v1 / he_logos_v1; gate widened.
chat/pack_grounding.py EDIT
_resolve_anchor_lens_mode, _maybe_append_anchor_lens_annotation,
_substrate_lexicon_by_entry_id, _en_lemma_to_entry_id.
build_pack_surface_candidate + pack_grounded_surface gain
anchor_lens kwarg (default UNANCHORED).
chat/runtime.py EDIT
Thread self.anchor_lens into pack_grounded_surface() call.
tests/test_anchor_lens_pack_seam.py EDIT
Doc-comment updated for L1.3 allow-list.
tests/test_anchor_lens_* NEW (3 files)
docs/decisions/ADR-0073c-anchor-lens-composer-wiring.md NEW
This commit is contained in:
parent
9b1b63b253
commit
b35bec6465
12 changed files with 1027 additions and 28 deletions
|
|
@ -43,10 +43,21 @@ from chat.pack_resolver import (
|
|||
mounted_lemmas,
|
||||
resolve_lemma,
|
||||
)
|
||||
from packs.anchor_lens.loader import AnchorLens, UNANCHORED
|
||||
from packs.register.loader import RegisterPack, UNREGISTERED
|
||||
|
||||
PACK_ID: str = "en_core_cognition_v1"
|
||||
|
||||
# ADR-0073c — substrate → mounted pack ids for anchor-lens engagement.
|
||||
# Cognition-tier packs are the primary L1.3 substrate. Micro packs are
|
||||
# included as a defensive fallback for the few distinct lemmas they
|
||||
# carry; the engagement path early-exits once an atom-match is found.
|
||||
_ANCHOR_LENS_SUBSTRATE_PACK_IDS: dict[str, tuple[str, ...]] = {
|
||||
"grc": ("grc_logos_cognition_v1", "grc_logos_micro_v1"),
|
||||
"he": ("he_core_cognition_v1", "he_logos_micro_v1"),
|
||||
"en": (PACK_ID,),
|
||||
}
|
||||
|
||||
_PACK_LEXICON_PATH = (
|
||||
Path(__file__).resolve().parent.parent
|
||||
/ "language_packs"
|
||||
|
|
@ -182,11 +193,155 @@ def _resolve_disclosure_domain_count(
|
|||
return n
|
||||
|
||||
|
||||
@lru_cache(maxsize=8)
|
||||
def _substrate_lexicon_by_entry_id(pack_id: str) -> dict[str, tuple[str, ...]]:
|
||||
"""Map ``entry_id -> semantic_domains`` for a substrate pack.
|
||||
|
||||
Cached for the process lifetime — ratified packs are immutable.
|
||||
Returns an empty dict when the pack is unavailable.
|
||||
"""
|
||||
lexicon_path = (
|
||||
Path(__file__).resolve().parent.parent
|
||||
/ "language_packs"
|
||||
/ "data"
|
||||
/ pack_id
|
||||
/ "lexicon.jsonl"
|
||||
)
|
||||
if not lexicon_path.is_file():
|
||||
return {}
|
||||
out: dict[str, tuple[str, ...]] = {}
|
||||
for line in lexicon_path.read_text(encoding="utf-8").splitlines():
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
try:
|
||||
entry = json.loads(line)
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
entry_id = entry.get("entry_id")
|
||||
if not entry_id:
|
||||
continue
|
||||
out[str(entry_id)] = tuple(entry.get("semantic_domains", ()))
|
||||
return out
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def _en_lemma_to_entry_id() -> dict[str, str]:
|
||||
"""Map ``en lemma -> entry_id`` for the cognition pack.
|
||||
|
||||
Cached for the process lifetime — ratified packs are immutable.
|
||||
"""
|
||||
out: dict[str, str] = {}
|
||||
if not _PACK_LEXICON_PATH.is_file():
|
||||
return out
|
||||
for line in _PACK_LEXICON_PATH.read_text(encoding="utf-8").splitlines():
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
try:
|
||||
entry = json.loads(line)
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
lemma = entry.get("lemma") or entry.get("surface")
|
||||
entry_id = entry.get("entry_id")
|
||||
if not lemma or not entry_id:
|
||||
continue
|
||||
out[str(lemma).lower()] = str(entry_id)
|
||||
return out
|
||||
|
||||
|
||||
def _resolve_anchor_lens_mode(
|
||||
en_lemma: str, anchor_lens: AnchorLens,
|
||||
) -> str | None:
|
||||
"""Return the lens's ``cognitive_mode_label`` if it engages on ``en_lemma``.
|
||||
|
||||
Engagement rule (single):
|
||||
1. Resolve ``en_lemma`` to its entry_id in the cognition pack.
|
||||
2. Walk the alignment graph(s) of every substrate pack matching
|
||||
``anchor_lens.primary_substrate`` and find substrate lemmas
|
||||
whose edges target this en entry_id.
|
||||
3. For each such substrate lemma, check whether its
|
||||
``semantic_domains`` contains any atom from
|
||||
``anchor_lens.semantic_domain_preferences``. First match wins.
|
||||
|
||||
Returns ``None`` when:
|
||||
* ``anchor_lens.is_null_lens()`` (the unanchored sentinel and
|
||||
``default_unanchored_v1`` both early-exit here)
|
||||
* ``primary_substrate`` is ``"none"`` or has no mounted packs
|
||||
* the en lemma is not in the cognition pack
|
||||
* no substrate lemma aligned to this en lemma carries a
|
||||
preferred atom
|
||||
|
||||
The function never reads non-ASCII surface text — it pivots on
|
||||
entry_ids and atom strings only. Glyph-leak is structurally
|
||||
impossible from this engagement path.
|
||||
|
||||
Lazy import of :func:`alignment.graph.load_alignment` keeps the
|
||||
alignment subsystem out of cold-import paths.
|
||||
"""
|
||||
if anchor_lens.is_null_lens() or not anchor_lens.semantic_domain_preferences:
|
||||
return None
|
||||
substrate = anchor_lens.primary_substrate
|
||||
if substrate == "none":
|
||||
return None
|
||||
substrate_packs = _ANCHOR_LENS_SUBSTRATE_PACK_IDS.get(substrate, ())
|
||||
if not substrate_packs:
|
||||
return None
|
||||
en_entry_id = _en_lemma_to_entry_id().get(en_lemma.strip().lower())
|
||||
if not en_entry_id:
|
||||
return None
|
||||
from alignment.graph import load_alignment
|
||||
|
||||
preferred = set(anchor_lens.semantic_domain_preferences)
|
||||
for pack_id in substrate_packs:
|
||||
graph = load_alignment(pack_id)
|
||||
if len(graph) == 0:
|
||||
continue
|
||||
substrate_index = _substrate_lexicon_by_entry_id(pack_id)
|
||||
for edge in graph.edges:
|
||||
if edge.target_id != en_entry_id:
|
||||
continue
|
||||
source_atoms = substrate_index.get(edge.source_id, ())
|
||||
if not source_atoms:
|
||||
continue
|
||||
if any(atom in preferred for atom in source_atoms):
|
||||
return anchor_lens.cognitive_mode_label
|
||||
return None
|
||||
|
||||
|
||||
def _maybe_append_anchor_lens_annotation(
|
||||
surface: str, en_lemma: str, anchor_lens: AnchorLens,
|
||||
) -> str:
|
||||
"""Append ``[lens({lens_id}):{mode_label}]`` when lens engages.
|
||||
|
||||
Annotation goes between the existing trailing period and the end of
|
||||
string, e.g.:
|
||||
|
||||
"...pack-grounded (en_core_cognition_v1)."
|
||||
→
|
||||
"...pack-grounded (en_core_cognition_v1) [lens(grc_logos_v1):systematic]."
|
||||
|
||||
Surface without a trailing period gets the annotation suffixed
|
||||
directly. No-op when the lens does not engage.
|
||||
|
||||
Audit invariant: the annotation is pure ASCII (lens_id and mode
|
||||
label both bounded to 64 ASCII chars by the loader).
|
||||
"""
|
||||
mode = _resolve_anchor_lens_mode(en_lemma, anchor_lens)
|
||||
if mode is None:
|
||||
return surface
|
||||
annotation = f"[lens({anchor_lens.lens_id}):{mode}]"
|
||||
if surface.endswith("."):
|
||||
return f"{surface[:-1]} {annotation}."
|
||||
return f"{surface} {annotation}"
|
||||
|
||||
|
||||
def build_pack_surface_candidate(
|
||||
lemma: str,
|
||||
pack_ids: tuple[str, ...] = DEFAULT_RESOLVABLE_PACK_IDS,
|
||||
*,
|
||||
register: RegisterPack = UNREGISTERED,
|
||||
anchor_lens: AnchorLens = UNANCHORED,
|
||||
):
|
||||
"""Return a :class:`PackSurfaceCandidate` for *lemma*, or ``None``.
|
||||
|
||||
|
|
@ -239,6 +394,12 @@ def build_pack_surface_candidate(
|
|||
f"{_frame_gloss(key, gloss_pos, gloss_text)} "
|
||||
f"pack-grounded ({resolved_pack_id})."
|
||||
)
|
||||
# ADR-0073c — anchor-lens annotation when lens engages on
|
||||
# this en lemma via the substrate alignment graph. No-op
|
||||
# under UNANCHORED / default_unanchored_v1 (null-lift).
|
||||
surface = _maybe_append_anchor_lens_annotation(
|
||||
surface, key, anchor_lens,
|
||||
)
|
||||
return PackSurfaceCandidate(
|
||||
surface=surface,
|
||||
grounding_source="pack",
|
||||
|
|
@ -261,6 +422,12 @@ def build_pack_surface_candidate(
|
|||
f"{key} — pack-grounded ({resolved_pack_id}): {head}. "
|
||||
f"No session evidence yet."
|
||||
)
|
||||
# ADR-0073c — anchor-lens annotation appended after the trailing
|
||||
# period of the disclosure surface. No-op under UNANCHORED /
|
||||
# default_unanchored_v1 (null-lift).
|
||||
surface = _maybe_append_anchor_lens_annotation(
|
||||
surface, key, anchor_lens,
|
||||
)
|
||||
return PackSurfaceCandidate(
|
||||
surface=surface,
|
||||
grounding_source="pack",
|
||||
|
|
@ -279,6 +446,7 @@ def pack_grounded_surface(
|
|||
pack_ids: tuple[str, ...] = DEFAULT_RESOLVABLE_PACK_IDS,
|
||||
*,
|
||||
register: RegisterPack = UNREGISTERED,
|
||||
anchor_lens: AnchorLens = UNANCHORED,
|
||||
) -> str | None:
|
||||
"""Return a deterministic pack-grounded surface for *lemma*, or ``None``.
|
||||
|
||||
|
|
@ -302,7 +470,9 @@ def pack_grounded_surface(
|
|||
|
||||
Returns ``None`` when the lemma is empty or doesn't resolve.
|
||||
"""
|
||||
candidate = build_pack_surface_candidate(lemma, pack_ids, register=register)
|
||||
candidate = build_pack_surface_candidate(
|
||||
lemma, pack_ids, register=register, anchor_lens=anchor_lens,
|
||||
)
|
||||
return candidate.surface if candidate is not None else None
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -752,7 +752,11 @@ class ChatRuntime:
|
|||
lemma = (intent.subject or "").strip()
|
||||
if not lemma:
|
||||
return None
|
||||
surface = pack_grounded_surface(lemma, register=self.register_pack)
|
||||
surface = pack_grounded_surface(
|
||||
lemma,
|
||||
register=self.register_pack,
|
||||
anchor_lens=self.anchor_lens,
|
||||
)
|
||||
if surface is not None:
|
||||
return (surface, "pack")
|
||||
oov_lemma = (intent.subject or "").strip()
|
||||
|
|
|
|||
315
docs/decisions/ADR-0073c-anchor-lens-composer-wiring.md
Normal file
315
docs/decisions/ADR-0073c-anchor-lens-composer-wiring.md
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
# ADR-0073c — First non-trivial lenses + composer wiring (Plan Phase L1.3)
|
||||
|
||||
**Status:** Accepted
|
||||
**Date:** 2026-05-19
|
||||
**Ratified:** 2026-05-19
|
||||
**Author:** Shay
|
||||
**Phase:** Plan Phase L1.3 (first non-trivial lenses + composer wiring)
|
||||
**Parent:** [ADR-0073](./ADR-0073-anchor-lens-substrate.md) (umbrella)
|
||||
**Builds on:** [ADR-0073a](./ADR-0073a-anchor-lens-content-phase.md) (substrate content),
|
||||
[ADR-0073b](./ADR-0073b-anchor-lens-class-loader.md) (class + loader)
|
||||
**Pattern:** mirrors ADR-0070 (terse_v1 — first non-trivial register)
|
||||
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
L1.2 landed the architectural plumbing (AnchorLens class + loader +
|
||||
unanchored sentinel + RuntimeConfig threading) with strict null-lift
|
||||
discipline — no composer reads the lens, every lane is byte-identical
|
||||
under `default_unanchored_v1`. L1.3 now ships the **first non-trivial
|
||||
lenses** and the composer wiring that consumes them.
|
||||
|
||||
The architectural orthogonality claim becomes load-bearing at L1.3:
|
||||
|
||||
* `register-tour`: per prompt, fixing lens, varying register → trace_hash CONSTANT.
|
||||
* `anchor-lens-tour` (L1.4): per prompt, fixing register, varying lens → trace_hash DISTINCT.
|
||||
|
||||
Both must hold. L1.3 lands the substantive surface lift that makes
|
||||
the second claim true; L1.4 packages the demo that asserts it.
|
||||
|
||||
---
|
||||
|
||||
## Decision
|
||||
|
||||
### L1.3 wiring scope (deliberately narrow)
|
||||
|
||||
Composer wiring is restricted to **DEFINITION/RECALL via
|
||||
`pack_grounded_surface` on the English cognition pack**. Other
|
||||
composers (COMPARISON, CORRECTION, PROCEDURE, NARRATIVE, EXAMPLE,
|
||||
CAUSE, VERIFICATION) accept the `anchor_lens` kwarg but do not yet
|
||||
consume it.
|
||||
|
||||
Rationale:
|
||||
|
||||
* The English cognition pack is the cognition lane's load-bearing
|
||||
corpus and the demo target for the L1.4 anchor-lens-tour.
|
||||
* DEFINITION/RECALL is the simplest intent shape — one subject lemma,
|
||||
one composed sentence — so the engagement logic is observable
|
||||
end-to-end without entangling cross-pack chain traversal.
|
||||
* COMPARISON/CORRECTION/PROCEDURE need richer engagement semantics
|
||||
(two-lemma cross product / dialogue context / verb phrases). Those
|
||||
are deferred to L1.3b or later, mirroring how register's R3 shipped
|
||||
one knob before R4 broadened.
|
||||
|
||||
### Engagement criteria (single rule)
|
||||
|
||||
Given an English lemma `en_lemma` resolving to entry id `en_id` in
|
||||
the cognition pack:
|
||||
|
||||
1. If `lens.is_null_lens()` or `lens.primary_substrate == "none"`
|
||||
⇒ no engagement.
|
||||
2. Load `alignment.jsonl` for substrate packs matching
|
||||
`lens.primary_substrate` (e.g. `grc_logos_cognition_v1` for
|
||||
`substrate="grc"`).
|
||||
3. Find substrate lemmas whose alignment edges target `en_id`.
|
||||
4. For each such substrate lemma, check whether its
|
||||
`semantic_domains` contains any atom from
|
||||
`lens.semantic_domain_preferences`.
|
||||
5. First match wins. Lens engages; the composer emits
|
||||
`cognitive_mode_label`.
|
||||
|
||||
The pivot is **shared `semantic_domains` atoms surfaced via the
|
||||
alignment graph**, exactly the language-neutral commitment from
|
||||
ADR-0073. No transliteration tables, no lemma-string lookups, no
|
||||
non-English glyphs in the engagement path.
|
||||
|
||||
### Surface lift
|
||||
|
||||
The pack-grounded surface gains an annotation between the existing
|
||||
provenance tag and the terminating period:
|
||||
|
||||
```
|
||||
no-lens: "Knowledge is justified understanding ... pack-grounded (en_core_cognition_v1)."
|
||||
lens-on: "Knowledge is justified understanding ... pack-grounded (en_core_cognition_v1) [lens(grc_logos_v1):systematic]."
|
||||
```
|
||||
|
||||
The annotation carries both `lens_id` and `cognitive_mode_label` so
|
||||
audit consumers can answer "which lens fired with which mode" without
|
||||
re-deriving from telemetry. The bracket-prefix-suffix shape keeps
|
||||
the annotation parseable; the surface remains pure ASCII (the L1.3
|
||||
hard gate forbids non-ASCII characters at the user surface
|
||||
regardless of substrate).
|
||||
|
||||
### First two ratified lenses
|
||||
|
||||
**`grc_logos_v1`** — primary substrate Greek; pivots on ἐπιστήμη to
|
||||
distinguish systematic from experiential knowledge.
|
||||
|
||||
```json
|
||||
{
|
||||
"lens_id": "grc_logos_v1",
|
||||
"primary_substrate": "grc",
|
||||
"semantic_domain_preferences": ["logos.episteme.systematic_knowledge"],
|
||||
"cognitive_mode_label": "systematic"
|
||||
}
|
||||
```
|
||||
|
||||
Engagement target: en lemma `knowledge` (en-core-cog-007). The grc
|
||||
cognition pack's `grc-core-cog-021` (ἐπιστήμη) carries the
|
||||
preferred atom and is bound to en-007 via the
|
||||
`cross_lang.logos.episteme.en_collapse` alignment edge added at
|
||||
ADR-0073a.
|
||||
|
||||
**`he_logos_v1`** — primary substrate Hebrew; pivots on אמת
|
||||
(`logos.aletheia.verity`) to render truth as covenant-grounded
|
||||
verification.
|
||||
|
||||
```json
|
||||
{
|
||||
"lens_id": "he_logos_v1",
|
||||
"primary_substrate": "he",
|
||||
"semantic_domain_preferences": ["logos.aletheia.verity"],
|
||||
"cognitive_mode_label": "covenant-verity"
|
||||
}
|
||||
```
|
||||
|
||||
Engagement target: en lemma `truth` (en-core-cog-002). The he
|
||||
cognition pack's `he-core-cog-002` (אמת) carries the preferred atom
|
||||
and is bound to en-002 via the `cross_lang.logos.aletheia.en`
|
||||
alignment edge added at ADR-0073a.
|
||||
|
||||
Both lenses are ratified via `scripts/ratify_anchor_lens_packs.py`
|
||||
with the L1.3-widened gate: any lens whose preferences contain an
|
||||
atom existing in at least one substrate pack's lemma is ratifiable
|
||||
under method `anchor_lens_lifts_proposition`. Null lenses keep
|
||||
ratifying under `byte_identity_null_lift`.
|
||||
|
||||
### Ratification-gate widening
|
||||
|
||||
`scripts/ratify_anchor_lens_packs.py` gains a non-null branch:
|
||||
|
||||
* Null lens (substrate=="none", empty prefs, empty label) ⇒
|
||||
`byte_identity_null_lift` (L1.2 method).
|
||||
* Non-null lens ⇒ verifies that every preferred atom appears in at
|
||||
least one substrate-pack lemma's `semantic_domains` (so the lens
|
||||
has a real pivot to land on) AND `cognitive_mode_label` is
|
||||
non-empty AND `primary_substrate ∈ {grc, he, en}`. Method:
|
||||
`anchor_lens_lifts_proposition`.
|
||||
|
||||
Bypass paths are unchanged (`CORE_ALLOW_UNRATIFIED_ANCHOR_LENS=1`).
|
||||
|
||||
### Seam test widening
|
||||
|
||||
`tests/test_anchor_lens_pack_seam.py` adds `chat/pack_grounding.py`
|
||||
(and any other composer L1.3 touches) to the **allowed** import set
|
||||
— the same way ADR-0069 widened the register seam at R2. Truth-path
|
||||
modules stay anchor-lens-free.
|
||||
|
||||
### Runtime threading
|
||||
|
||||
`chat/runtime.py` already exposes `self.anchor_lens` (L1.2). L1.3
|
||||
threads it into `pack_grounded_surface(...)` at every call site in
|
||||
`runtime.py` exactly as `register=self.register_pack` is threaded
|
||||
today. Other composers receive `anchor_lens=self.anchor_lens` for
|
||||
forward-compat but no behavior change yet.
|
||||
|
||||
### Invariants pinned at L1.3
|
||||
|
||||
```
|
||||
anchor_lens_byte_identity_null_lift (L1.2) — preserved
|
||||
register_invariant_grounding (R3) — preserved
|
||||
seeded_variation_replay_equivalence (R4) — preserved
|
||||
register-tour seam (R5) — preserved
|
||||
|
||||
anchor_lens_lifts_proposition (NEW):
|
||||
For every cognition case in {knowledge_define, truth_*}:
|
||||
surface(grc_logos_v1) ≠ surface(unanchored)
|
||||
surface(he_logos_v1) ≠ surface(unanchored)
|
||||
trace_hash differs across {unanchored, grc_logos_v1, he_logos_v1}
|
||||
Pinned by tests/test_anchor_lens_lifts_proposition.py.
|
||||
|
||||
anchor_lens_no_glyph_leak (NEW — hard gate):
|
||||
ChatResponse.surface contains only ASCII characters regardless of
|
||||
loaded lens. Tested across {unanchored, grc_logos_v1, he_logos_v1}
|
||||
× every cognition case. Pinned by tests/test_anchor_lens_no_glyph_leak.py.
|
||||
```
|
||||
|
||||
The no-glyph-leak gate is **load-bearing**. ADR-0073's substrate
|
||||
commitment says English compound phrasing at the user surface, never
|
||||
raw Greek/Hebrew glyphs. A non-ASCII char in `ChatResponse.surface`
|
||||
under any lens fails the lane immediately.
|
||||
|
||||
---
|
||||
|
||||
## Files
|
||||
|
||||
```
|
||||
packs/anchor_lens/grc_logos_v1.json NEW
|
||||
packs/anchor_lens/grc_logos_v1.mastery_report.json NEW
|
||||
packs/anchor_lens/he_logos_v1.json NEW
|
||||
packs/anchor_lens/he_logos_v1.mastery_report.json NEW
|
||||
|
||||
packs/anchor_lens/loader.py EDIT
|
||||
- widen ratification (no schema change, just gate logic)
|
||||
|
||||
scripts/ratify_anchor_lens_packs.py EDIT
|
||||
- LENS_IDS adds grc_logos_v1 / he_logos_v1
|
||||
- widen gate to accept non-null lenses
|
||||
|
||||
chat/pack_grounding.py EDIT
|
||||
- _resolve_anchor_lens_mode(en_lemma, lens) → str | None
|
||||
- build_pack_surface_candidate() gains anchor_lens kwarg
|
||||
- pack_grounded_surface() gains anchor_lens kwarg
|
||||
- surface format gains lens annotation when engaged
|
||||
|
||||
chat/runtime.py EDIT
|
||||
- thread self.anchor_lens into pack_grounded_surface() call sites
|
||||
|
||||
tests/test_anchor_lens_pack_seam.py EDIT
|
||||
- widen allow-list to include chat/pack_grounding.py
|
||||
|
||||
tests/test_anchor_lens_lifts_proposition.py NEW
|
||||
- lens engagement, surface lift, trace_hash divergence
|
||||
|
||||
tests/test_anchor_lens_no_glyph_leak.py NEW
|
||||
- ASCII-only surface across all lenses × all cognition cases
|
||||
|
||||
tests/test_anchor_lens_engagement_unit.py NEW
|
||||
- _resolve_anchor_lens_mode unit coverage
|
||||
|
||||
docs/decisions/ADR-0073c-anchor-lens-composer-wiring.md NEW (this file)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Consequences
|
||||
|
||||
### Capability unlocked at L1.3
|
||||
|
||||
Composer produces structurally different surfaces from different
|
||||
conceptual substrates, deterministically, with audit-traceable
|
||||
provenance. The proposition that English-default would render as
|
||||
"Knowledge is justified understanding..." becomes
|
||||
"...understanding... [lens(grc_logos_v1):systematic]." under the
|
||||
Greek-anchored lens. Trace_hash moves with it.
|
||||
|
||||
### Cognition lane
|
||||
|
||||
* `default_unanchored_v1` byte-identical (null-lift invariant
|
||||
preserved).
|
||||
* `grc_logos_v1` and `he_logos_v1` deliberately move the lane's
|
||||
outputs. L1.3 does NOT update the public-split cognition gate
|
||||
numbers — the cognition eval continues to run against the
|
||||
unanchored default.
|
||||
|
||||
### Backwards compatibility
|
||||
|
||||
* `pack_grounded_surface()` gains a keyword-only kwarg
|
||||
`anchor_lens=UNANCHORED`. Positional-arg callers unaffected.
|
||||
* Surface format change is additive: the lens annotation only
|
||||
appears when a non-null lens engages. Without engagement, the
|
||||
surface is byte-identical to pre-L1.3.
|
||||
|
||||
### Performance
|
||||
|
||||
L1.3 adds one alignment-graph lookup + one substrate-lemma lookup per
|
||||
pack-grounded turn under a non-null lens. Under the unanchored
|
||||
default (the production path until operators opt in) zero overhead.
|
||||
The alignment graph + substrate lexicon are cached via `lru_cache`,
|
||||
same pattern as the existing pack index.
|
||||
|
||||
### Trust boundaries
|
||||
|
||||
* Lens preferences are operator-authored content. The L1.3 ratify
|
||||
gate verifies every preferred atom exists in at least one
|
||||
substrate-pack lemma — operators cannot ship a lens that
|
||||
references atoms not on disk.
|
||||
* `anchor_lens_no_glyph_leak` is a hard gate: any non-ASCII at the
|
||||
user surface fails the lane. This protects the layperson surface
|
||||
contract regardless of substrate.
|
||||
* No new mutation surface; lens packs continue to be proposal-only
|
||||
for runtime, ratifiable only via the operator-only ratify script.
|
||||
|
||||
---
|
||||
|
||||
## Verification
|
||||
|
||||
```
|
||||
python -m pytest tests/test_anchor_lens_engagement_unit.py -q N passed
|
||||
python -m pytest tests/test_anchor_lens_lifts_proposition.py -q N passed
|
||||
python -m pytest tests/test_anchor_lens_no_glyph_leak.py -q N passed
|
||||
python -m pytest tests/test_anchor_lens_null_lift.py -q 4 passed
|
||||
(unchanged)
|
||||
python -m pytest tests/test_anchor_lens_pack_loader.py -q 24 passed
|
||||
(unchanged)
|
||||
python -m pytest tests/test_anchor_lens_pack_seam.py -q N passed
|
||||
(allow-list
|
||||
widened
|
||||
for composer)
|
||||
|
||||
Curated lanes (must remain green):
|
||||
smoke / cognition / teaching / packs / runtime / algebra
|
||||
|
||||
Cognition eval byte-identical under default_unanchored_v1:
|
||||
public 100 / 100 / 91.7 / 100
|
||||
|
||||
core demo register-tour exit 0
|
||||
(R5 seam
|
||||
still holds)
|
||||
```
|
||||
|
||||
The orthogonality between the two tours is the load-bearing
|
||||
architectural commitment. L1.4 packages `anchor-lens-tour` as the
|
||||
falsifiable demo for the substantive axis; L1.3 makes that demo
|
||||
possible by delivering the surface lift the demo will assert.
|
||||
13
packs/anchor_lens/grc_logos_v1.json
Normal file
13
packs/anchor_lens/grc_logos_v1.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"cognitive_mode_label": "systematic",
|
||||
"description": "Greek-substrate anchor lens. Pivots English 'knowledge' onto \u1f10\u03c0\u03b9\u03c3\u03c4\u03ae\u03bc\u03b7's logos.episteme.systematic_knowledge atom via the grc cognition pack's alignment edge to en-core-cog-007. Renders surface as English compound 'systematic'. See ADR-0073c.",
|
||||
"display_name": "Greek logos (systematic knowledge)",
|
||||
"lens_id": "grc_logos_v1",
|
||||
"mastery_report_sha256": "46ab6a43f1ce0e161e9185910b969025104ded4a3c6c6211998a307c8d070882",
|
||||
"primary_substrate": "grc",
|
||||
"schema_version": "1.0.0",
|
||||
"semantic_domain_preferences": [
|
||||
"logos.episteme.systematic_knowledge"
|
||||
],
|
||||
"version": "1.0.0"
|
||||
}
|
||||
20
packs/anchor_lens/grc_logos_v1.mastery_report.json
Normal file
20
packs/anchor_lens/grc_logos_v1.mastery_report.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"evidence": {
|
||||
"atoms_anchored_in_substrate": [
|
||||
"logos.episteme.systematic_knowledge"
|
||||
],
|
||||
"cognitive_mode_label": "systematic",
|
||||
"cognitive_mode_label_empty": false,
|
||||
"primary_substrate": "grc",
|
||||
"semantic_domain_preferences_count": 1,
|
||||
"semantic_domain_preferences_empty": false
|
||||
},
|
||||
"failure_reasons": [],
|
||||
"issued_at": "2026-05-19T00:00:00Z",
|
||||
"lens_id": "grc_logos_v1",
|
||||
"pack_source_sha256": "03b1a12a0ff9caf18be0a4b54760c333949b052b946441062b21827fdbbc7132",
|
||||
"ratification_method": "anchor_lens_lifts_proposition",
|
||||
"ratified": true,
|
||||
"report_sha256": "46ab6a43f1ce0e161e9185910b969025104ded4a3c6c6211998a307c8d070882",
|
||||
"schema_version": "1.0.0"
|
||||
}
|
||||
13
packs/anchor_lens/he_logos_v1.json
Normal file
13
packs/anchor_lens/he_logos_v1.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"cognitive_mode_label": "covenant-verity",
|
||||
"description": "Hebrew-substrate anchor lens. Pivots English 'truth' onto \u05d0\u05de\u05ea's logos.aletheia.verity atom via the he cognition pack's alignment edge to en-core-cog-002. Renders surface as English compound 'covenant-verity'. See ADR-0073c.",
|
||||
"display_name": "Hebrew logos (covenant verity)",
|
||||
"lens_id": "he_logos_v1",
|
||||
"mastery_report_sha256": "aad4cad4a4ff1970c8a81f02bca5a77c58659103b008c87a518c6d6c095e1cc6",
|
||||
"primary_substrate": "he",
|
||||
"schema_version": "1.0.0",
|
||||
"semantic_domain_preferences": [
|
||||
"logos.aletheia.verity"
|
||||
],
|
||||
"version": "1.0.0"
|
||||
}
|
||||
20
packs/anchor_lens/he_logos_v1.mastery_report.json
Normal file
20
packs/anchor_lens/he_logos_v1.mastery_report.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"evidence": {
|
||||
"atoms_anchored_in_substrate": [
|
||||
"logos.aletheia.verity"
|
||||
],
|
||||
"cognitive_mode_label": "covenant-verity",
|
||||
"cognitive_mode_label_empty": false,
|
||||
"primary_substrate": "he",
|
||||
"semantic_domain_preferences_count": 1,
|
||||
"semantic_domain_preferences_empty": false
|
||||
},
|
||||
"failure_reasons": [],
|
||||
"issued_at": "2026-05-19T00:00:00Z",
|
||||
"lens_id": "he_logos_v1",
|
||||
"pack_source_sha256": "8cc30f6f7e923f09870a34e0ecf199eca6cb34c28b35e96f9d747d12cceaa953",
|
||||
"ratification_method": "anchor_lens_lifts_proposition",
|
||||
"ratified": true,
|
||||
"report_sha256": "aad4cad4a4ff1970c8a81f02bca5a77c58659103b008c87a518c6d6c095e1cc6",
|
||||
"schema_version": "1.0.0"
|
||||
}
|
||||
|
|
@ -48,8 +48,39 @@ PACKS_DIR = Path(__file__).resolve().parents[1] / "packs" / "anchor_lens"
|
|||
ISSUED_AT = "2026-05-19T00:00:00Z"
|
||||
LENS_IDS: tuple[str, ...] = (
|
||||
"default_unanchored_v1",
|
||||
"grc_logos_v1",
|
||||
"he_logos_v1",
|
||||
)
|
||||
|
||||
_SUBSTRATE_PACK_IDS: dict[str, tuple[str, ...]] = {
|
||||
"grc": ("grc_logos_cognition_v1", "grc_logos_micro_v1"),
|
||||
"he": ("he_core_cognition_v1", "he_logos_micro_v1"),
|
||||
"en": ("en_core_cognition_v1",),
|
||||
}
|
||||
|
||||
|
||||
def _atom_exists_in_substrate(atom: str, substrate: str) -> bool:
|
||||
"""True iff some lemma in any pack matching ``substrate`` carries ``atom``."""
|
||||
import json as _json
|
||||
from pathlib import Path as _Path
|
||||
|
||||
data_dir = _Path(__file__).resolve().parents[1] / "language_packs" / "data"
|
||||
for pack_id in _SUBSTRATE_PACK_IDS.get(substrate, ()):
|
||||
lexicon_path = data_dir / pack_id / "lexicon.jsonl"
|
||||
if not lexicon_path.is_file():
|
||||
continue
|
||||
for line in lexicon_path.read_text(encoding="utf-8").splitlines():
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
try:
|
||||
entry = _json.loads(line)
|
||||
except _json.JSONDecodeError:
|
||||
continue
|
||||
if atom in entry.get("semantic_domains", []):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _canonical_pack_bytes_for_hashing(pack: dict) -> bytes:
|
||||
"""Serialize the pack with ``mastery_report_sha256`` blanked."""
|
||||
|
|
@ -90,17 +121,60 @@ def _ratify_one(pack_path: Path, lens_id: str) -> tuple[dict, dict[str, Any]]:
|
|||
|
||||
pack_source_sha = _pack_source_sha(pack)
|
||||
|
||||
# L1.2 gate: only null lenses are ratifiable here. L1.3 will
|
||||
# widen this to cover non-null lenses with a different
|
||||
# ratification method.
|
||||
if not _is_null_lens(pack):
|
||||
raise SystemExit(
|
||||
f"L1.2 gate refuses {lens_id!r}: not a null lens. "
|
||||
"primary_substrate must be 'none', semantic_domain_preferences "
|
||||
"must be empty, and cognitive_mode_label must be empty."
|
||||
)
|
||||
|
||||
ratification_method = "byte_identity_null_lift"
|
||||
# L1.3 gate: null lenses ratify under byte_identity_null_lift;
|
||||
# non-null lenses ratify under anchor_lens_lifts_proposition, with
|
||||
# the precondition that every preferred atom exists in at least one
|
||||
# lemma of the named substrate. This is the trust boundary
|
||||
# preventing operators from shipping a lens that references
|
||||
# atoms not on disk.
|
||||
if _is_null_lens(pack):
|
||||
ratification_method = "byte_identity_null_lift"
|
||||
evidence: dict[str, Any] = {
|
||||
"primary_substrate": str(pack.get("primary_substrate", "")),
|
||||
"semantic_domain_preferences_empty": True,
|
||||
"semantic_domain_preferences_count": 0,
|
||||
"cognitive_mode_label_empty": True,
|
||||
}
|
||||
else:
|
||||
substrate = str(pack.get("primary_substrate", ""))
|
||||
if substrate not in ("grc", "he", "en"):
|
||||
raise SystemExit(
|
||||
f"L1.3 gate refuses {lens_id!r}: non-null lens "
|
||||
f"primary_substrate must be one of "
|
||||
f"{{'grc','he','en'}}, got {substrate!r}."
|
||||
)
|
||||
label = str(pack.get("cognitive_mode_label", ""))
|
||||
if not label:
|
||||
raise SystemExit(
|
||||
f"L1.3 gate refuses {lens_id!r}: non-null lens "
|
||||
"cognitive_mode_label must be non-empty."
|
||||
)
|
||||
prefs = pack.get("semantic_domain_preferences", []) or []
|
||||
if not isinstance(prefs, list) or not prefs:
|
||||
raise SystemExit(
|
||||
f"L1.3 gate refuses {lens_id!r}: non-null lens "
|
||||
"semantic_domain_preferences must be non-empty."
|
||||
)
|
||||
atoms_anchored: list[str] = []
|
||||
for atom in prefs:
|
||||
if _atom_exists_in_substrate(atom, substrate):
|
||||
atoms_anchored.append(atom)
|
||||
else:
|
||||
raise SystemExit(
|
||||
f"L1.3 gate refuses {lens_id!r}: preferred atom "
|
||||
f"{atom!r} does not appear in any "
|
||||
f"{substrate!r} substrate lemma. Lenses must "
|
||||
"point at atoms that exist on disk."
|
||||
)
|
||||
ratification_method = "anchor_lens_lifts_proposition"
|
||||
evidence = {
|
||||
"primary_substrate": substrate,
|
||||
"semantic_domain_preferences_empty": False,
|
||||
"semantic_domain_preferences_count": len(prefs),
|
||||
"cognitive_mode_label_empty": False,
|
||||
"cognitive_mode_label": label,
|
||||
"atoms_anchored_in_substrate": atoms_anchored,
|
||||
}
|
||||
|
||||
report: dict[str, Any] = {
|
||||
"lens_id": lens_id,
|
||||
|
|
@ -109,12 +183,7 @@ def _ratify_one(pack_path: Path, lens_id: str) -> tuple[dict, dict[str, Any]]:
|
|||
"pack_source_sha256": pack_source_sha,
|
||||
"ratification_method": ratification_method,
|
||||
"ratified": True,
|
||||
"evidence": {
|
||||
"primary_substrate": str(pack.get("primary_substrate", "")),
|
||||
"semantic_domain_preferences_empty": True,
|
||||
"semantic_domain_preferences_count": 0,
|
||||
"cognitive_mode_label_empty": True,
|
||||
},
|
||||
"evidence": evidence,
|
||||
"failure_reasons": [],
|
||||
"report_sha256": "",
|
||||
}
|
||||
|
|
|
|||
114
tests/test_anchor_lens_engagement_unit.py
Normal file
114
tests/test_anchor_lens_engagement_unit.py
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
"""Unit coverage for the anchor-lens engagement resolver (ADR-0073c).
|
||||
|
||||
Tests :func:`chat.pack_grounding._resolve_anchor_lens_mode` and
|
||||
:func:`chat.pack_grounding._maybe_append_anchor_lens_annotation` in
|
||||
isolation, separate from the full chat() round-trip.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from chat.pack_grounding import (
|
||||
_maybe_append_anchor_lens_annotation,
|
||||
_resolve_anchor_lens_mode,
|
||||
)
|
||||
from packs.anchor_lens import AnchorLens, UNANCHORED, load_anchor_lens
|
||||
|
||||
|
||||
def test_unanchored_sentinel_returns_none_for_every_lemma():
|
||||
for lemma in ("knowledge", "truth", "light", "word"):
|
||||
assert _resolve_anchor_lens_mode(lemma, UNANCHORED) is None
|
||||
|
||||
|
||||
def test_default_unanchored_pack_returns_none_for_every_lemma():
|
||||
lens = load_anchor_lens("default_unanchored_v1")
|
||||
for lemma in ("knowledge", "truth", "light", "word"):
|
||||
assert _resolve_anchor_lens_mode(lemma, lens) is None
|
||||
|
||||
|
||||
def test_grc_logos_v1_engages_on_knowledge_only():
|
||||
lens = load_anchor_lens("grc_logos_v1")
|
||||
assert _resolve_anchor_lens_mode("knowledge", lens) == "systematic"
|
||||
|
||||
|
||||
def test_grc_logos_v1_does_not_engage_on_truth():
|
||||
lens = load_anchor_lens("grc_logos_v1")
|
||||
assert _resolve_anchor_lens_mode("truth", lens) is None
|
||||
|
||||
|
||||
def test_grc_logos_v1_does_not_engage_on_unaligned_lemma():
|
||||
lens = load_anchor_lens("grc_logos_v1")
|
||||
assert _resolve_anchor_lens_mode("polarity", lens) is None
|
||||
|
||||
|
||||
def test_he_logos_v1_engages_on_truth_only():
|
||||
lens = load_anchor_lens("he_logos_v1")
|
||||
assert _resolve_anchor_lens_mode("truth", lens) == "covenant-verity"
|
||||
|
||||
|
||||
def test_he_logos_v1_does_not_engage_on_knowledge():
|
||||
lens = load_anchor_lens("he_logos_v1")
|
||||
assert _resolve_anchor_lens_mode("knowledge", lens) is None
|
||||
|
||||
|
||||
def test_engagement_case_insensitive():
|
||||
lens = load_anchor_lens("grc_logos_v1")
|
||||
assert _resolve_anchor_lens_mode("KNOWLEDGE", lens) == "systematic"
|
||||
assert _resolve_anchor_lens_mode(" knowledge ", lens) == "systematic"
|
||||
|
||||
|
||||
def test_annotation_appended_before_trailing_period():
|
||||
surface = "Knowledge is X. pack-grounded (en_core_cognition_v1)."
|
||||
lens = load_anchor_lens("grc_logos_v1")
|
||||
out = _maybe_append_anchor_lens_annotation(surface, "knowledge", lens)
|
||||
assert out == (
|
||||
"Knowledge is X. pack-grounded (en_core_cognition_v1) "
|
||||
"[lens(grc_logos_v1):systematic]."
|
||||
)
|
||||
|
||||
|
||||
def test_annotation_appended_without_trailing_period():
|
||||
surface = "Knowledge is X. pack-grounded (en_core_cognition_v1)"
|
||||
lens = load_anchor_lens("grc_logos_v1")
|
||||
out = _maybe_append_anchor_lens_annotation(surface, "knowledge", lens)
|
||||
assert out == (
|
||||
"Knowledge is X. pack-grounded (en_core_cognition_v1) "
|
||||
"[lens(grc_logos_v1):systematic]"
|
||||
)
|
||||
|
||||
|
||||
def test_annotation_noop_when_lens_does_not_engage():
|
||||
surface = "Truth is X. pack-grounded (en_core_cognition_v1)."
|
||||
lens = load_anchor_lens("grc_logos_v1")
|
||||
assert _maybe_append_anchor_lens_annotation(surface, "truth", lens) == surface
|
||||
|
||||
|
||||
def test_annotation_noop_under_unanchored():
|
||||
surface = "Knowledge is X. pack-grounded (en_core_cognition_v1)."
|
||||
out = _maybe_append_anchor_lens_annotation(surface, "knowledge", UNANCHORED)
|
||||
assert out == surface
|
||||
|
||||
|
||||
def test_annotation_is_pure_ascii():
|
||||
"""Hard glyph-leak gate at the helper level: annotation must
|
||||
never carry non-ASCII even when the lens substrate is grc/he."""
|
||||
surface = "Truth is X. pack-grounded (en_core_cognition_v1)."
|
||||
lens = load_anchor_lens("he_logos_v1")
|
||||
out = _maybe_append_anchor_lens_annotation(surface, "truth", lens)
|
||||
out.encode("ascii") # raises if any non-ASCII slipped through
|
||||
|
||||
|
||||
def test_synthetic_lens_with_atom_not_in_substrate_returns_none():
|
||||
"""A lens with preferences that don't match any substrate lemma
|
||||
is structurally engagement-incapable; the resolver returns None
|
||||
even if the lens is otherwise well-formed."""
|
||||
fake = AnchorLens(
|
||||
lens_id="synthetic_v1",
|
||||
version="0.0.0",
|
||||
description="test only",
|
||||
display_name="Synthetic",
|
||||
primary_substrate="grc",
|
||||
semantic_domain_preferences=("logos.nonexistent.atom",),
|
||||
cognitive_mode_label="phantom",
|
||||
)
|
||||
for lemma in ("knowledge", "truth", "light", "word"):
|
||||
assert _resolve_anchor_lens_mode(lemma, fake) is None
|
||||
161
tests/test_anchor_lens_lifts_proposition.py
Normal file
161
tests/test_anchor_lens_lifts_proposition.py
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
"""anchor_lens_lifts_proposition — load-bearing L1.3 invariant (ADR-0073c).
|
||||
|
||||
When a non-null lens engages on a cognition-pack lemma:
|
||||
- the surface differs from the unanchored baseline
|
||||
- the surface carries the lens annotation [lens(<id>):<mode>]
|
||||
- the trace_hash differs (the proposition has changed)
|
||||
|
||||
The complementary null-lift invariant (L1.2) continues to hold for the
|
||||
unanchored sentinel and default_unanchored_v1 — pinned by
|
||||
``tests/test_anchor_lens_null_lift.py``.
|
||||
|
||||
Cognition prompts used:
|
||||
"What is knowledge?" — grc_logos_v1 engages, he_logos_v1 does not
|
||||
"What is truth?" — he_logos_v1 engages, grc_logos_v1 does not
|
||||
|
||||
Together they exercise both lenses and confirm engagement is
|
||||
substrate-scoped, not blanket.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from chat.runtime import ChatRuntime
|
||||
from core.cognition.pipeline import CognitiveTurnPipeline
|
||||
from core.config import RuntimeConfig
|
||||
|
||||
|
||||
_KNOWLEDGE_PROMPT = "What is knowledge?"
|
||||
_TRUTH_PROMPT = "What is truth?"
|
||||
|
||||
|
||||
def _run(lens_id: str | None, prompt: str):
|
||||
rt = ChatRuntime(config=RuntimeConfig(anchor_lens_id=lens_id))
|
||||
pipeline = CognitiveTurnPipeline(runtime=rt)
|
||||
result = pipeline.run(prompt)
|
||||
response = rt.turn_log[-1]
|
||||
return result, response
|
||||
|
||||
|
||||
# ---------- grc_logos_v1 engages on "knowledge" ----------
|
||||
|
||||
|
||||
def test_grc_logos_v1_surface_differs_from_unanchored_on_knowledge():
|
||||
_, base = _run(None, _KNOWLEDGE_PROMPT)
|
||||
_, lensed = _run("grc_logos_v1", _KNOWLEDGE_PROMPT)
|
||||
assert base.surface != lensed.surface
|
||||
|
||||
|
||||
def test_grc_logos_v1_surface_carries_annotation_on_knowledge():
|
||||
_, lensed = _run("grc_logos_v1", _KNOWLEDGE_PROMPT)
|
||||
assert "[lens(grc_logos_v1):systematic]" in lensed.surface
|
||||
|
||||
|
||||
def test_grc_logos_v1_trace_hash_differs_from_unanchored_on_knowledge():
|
||||
base_result, _ = _run(None, _KNOWLEDGE_PROMPT)
|
||||
lensed_result, _ = _run("grc_logos_v1", _KNOWLEDGE_PROMPT)
|
||||
assert base_result.trace_hash != lensed_result.trace_hash
|
||||
|
||||
|
||||
# ---------- he_logos_v1 engages on "truth" ----------
|
||||
|
||||
|
||||
def test_he_logos_v1_surface_differs_from_unanchored_on_truth():
|
||||
_, base = _run(None, _TRUTH_PROMPT)
|
||||
_, lensed = _run("he_logos_v1", _TRUTH_PROMPT)
|
||||
assert base.surface != lensed.surface
|
||||
|
||||
|
||||
def test_he_logos_v1_surface_carries_annotation_on_truth():
|
||||
_, lensed = _run("he_logos_v1", _TRUTH_PROMPT)
|
||||
assert "[lens(he_logos_v1):covenant-verity]" in lensed.surface
|
||||
|
||||
|
||||
def test_he_logos_v1_trace_hash_differs_from_unanchored_on_truth():
|
||||
base_result, _ = _run(None, _TRUTH_PROMPT)
|
||||
lensed_result, _ = _run("he_logos_v1", _TRUTH_PROMPT)
|
||||
assert base_result.trace_hash != lensed_result.trace_hash
|
||||
|
||||
|
||||
# ---------- engagement is substrate-scoped (cross-lens isolation) ----------
|
||||
|
||||
|
||||
def test_grc_logos_v1_does_not_engage_on_truth():
|
||||
"""grc lens does not touch truth — surface byte-identical to baseline."""
|
||||
_, base = _run(None, _TRUTH_PROMPT)
|
||||
_, grc = _run("grc_logos_v1", _TRUTH_PROMPT)
|
||||
assert base.surface == grc.surface
|
||||
assert "lens(" not in grc.surface
|
||||
|
||||
|
||||
def test_he_logos_v1_does_not_engage_on_knowledge():
|
||||
"""he lens does not touch knowledge — surface byte-identical to baseline."""
|
||||
_, base = _run(None, _KNOWLEDGE_PROMPT)
|
||||
_, he = _run("he_logos_v1", _KNOWLEDGE_PROMPT)
|
||||
assert base.surface == he.surface
|
||||
assert "lens(" not in he.surface
|
||||
|
||||
|
||||
# ---------- three-way trace_hash divergence (the load-bearing claim) ----------
|
||||
|
||||
|
||||
def test_three_way_surface_distinct_on_knowledge():
|
||||
"""{unanchored, grc, he} produce two distinct surfaces on knowledge
|
||||
(grc engages, he does not so he == unanchored)."""
|
||||
_, base = _run(None, _KNOWLEDGE_PROMPT)
|
||||
_, grc = _run("grc_logos_v1", _KNOWLEDGE_PROMPT)
|
||||
_, he = _run("he_logos_v1", _KNOWLEDGE_PROMPT)
|
||||
distinct = {base.surface, grc.surface, he.surface}
|
||||
assert len(distinct) == 2 # grc differs; he matches base
|
||||
|
||||
|
||||
def test_three_way_surface_distinct_on_truth():
|
||||
"""Symmetric: he engages on truth, grc does not."""
|
||||
_, base = _run(None, _TRUTH_PROMPT)
|
||||
_, grc = _run("grc_logos_v1", _TRUTH_PROMPT)
|
||||
_, he = _run("he_logos_v1", _TRUTH_PROMPT)
|
||||
distinct = {base.surface, grc.surface, he.surface}
|
||||
assert len(distinct) == 2 # he differs; grc matches base
|
||||
|
||||
|
||||
# ---------- replay determinism (same lens × same input → same output) ----------
|
||||
|
||||
|
||||
@pytest.mark.parametrize("lens_id", ["grc_logos_v1", "he_logos_v1"])
|
||||
@pytest.mark.parametrize("prompt", [_KNOWLEDGE_PROMPT, _TRUTH_PROMPT])
|
||||
def test_lens_engagement_is_deterministic(lens_id: str, prompt: str):
|
||||
a_result, a = _run(lens_id, prompt)
|
||||
b_result, b = _run(lens_id, prompt)
|
||||
assert a.surface == b.surface
|
||||
assert a_result.trace_hash == b_result.trace_hash
|
||||
|
||||
|
||||
# ---------- register-tour seam still holds under each lens ----------
|
||||
|
||||
|
||||
@pytest.mark.parametrize("lens_id", [None, "default_unanchored_v1",
|
||||
"grc_logos_v1", "he_logos_v1"])
|
||||
def test_register_seam_within_lens_holds(lens_id: str | None):
|
||||
"""Per ADR-0073 orthogonality: within a fixed lens, varying register
|
||||
keeps trace_hash constant. L1.3 must preserve R5's register-tour
|
||||
invariant inside every lens scope."""
|
||||
pipeline_neutral = CognitiveTurnPipeline(
|
||||
runtime=ChatRuntime(config=RuntimeConfig(
|
||||
register_pack_id="default_neutral_v1",
|
||||
anchor_lens_id=lens_id,
|
||||
))
|
||||
)
|
||||
pipeline_convivial = CognitiveTurnPipeline(
|
||||
runtime=ChatRuntime(config=RuntimeConfig(
|
||||
register_pack_id="convivial_v1",
|
||||
anchor_lens_id=lens_id,
|
||||
))
|
||||
)
|
||||
n = pipeline_neutral.run(_KNOWLEDGE_PROMPT)
|
||||
c = pipeline_convivial.run(_KNOWLEDGE_PROMPT)
|
||||
assert n.trace_hash == c.trace_hash, (
|
||||
f"register-tour seam broken under lens={lens_id!r}: "
|
||||
f"neutral trace_hash {n.trace_hash[:12]}... != "
|
||||
f"convivial trace_hash {c.trace_hash[:12]}..."
|
||||
)
|
||||
104
tests/test_anchor_lens_no_glyph_leak.py
Normal file
104
tests/test_anchor_lens_no_glyph_leak.py
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
"""anchor_lens_no_glyph_leak — hard substrate-glyph gate (ADR-0073c).
|
||||
|
||||
ADR-0073's substrate commitment: anchor lens renders English compound
|
||||
phrasing at the user surface, never raw non-English **substrate
|
||||
glyphs** (Greek / Hebrew / Coptic / Aramaic letters). This test pins
|
||||
that as a falsifiable invariant — a single substrate-block character
|
||||
in ``ChatResponse.surface`` under any lens fails the lane.
|
||||
|
||||
Stylistic punctuation such as em-dash (U+2014) is permitted; it
|
||||
pre-dates L1.3 and is unrelated to the substrate-leak risk this
|
||||
invariant protects against. The forbidden zones are:
|
||||
|
||||
- U+0370..U+03FF Greek and Coptic
|
||||
- U+1F00..U+1FFF Greek Extended
|
||||
- U+0590..U+05FF Hebrew
|
||||
- U+0700..U+074F Syriac (forward-looking)
|
||||
- U+0600..U+06FF Arabic (forward-looking)
|
||||
|
||||
Scope: every cognition lane case × {unanchored, default_unanchored_v1,
|
||||
grc_logos_v1, he_logos_v1}. Forbidden block leaks fail immediately.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from core.config import RuntimeConfig
|
||||
from evals.run_cognition_eval import load_cases, run_eval
|
||||
|
||||
|
||||
_LENS_IDS_TO_TEST = (
|
||||
None,
|
||||
"default_unanchored_v1",
|
||||
"grc_logos_v1",
|
||||
"he_logos_v1",
|
||||
)
|
||||
|
||||
#: Forbidden Unicode blocks: substrate letter scripts that anchor lens
|
||||
#: must not leak. Each tuple is ``(start_codepoint, end_codepoint,
|
||||
#: block_label)``. Inclusive on both ends.
|
||||
_FORBIDDEN_BLOCKS: tuple[tuple[int, int, str], ...] = (
|
||||
(0x0370, 0x03FF, "Greek and Coptic"),
|
||||
(0x1F00, 0x1FFF, "Greek Extended"),
|
||||
(0x0590, 0x05FF, "Hebrew"),
|
||||
(0x0700, 0x074F, "Syriac"),
|
||||
(0x0600, 0x06FF, "Arabic"),
|
||||
)
|
||||
|
||||
|
||||
def _substrate_glyph_violations(surface: str) -> list[tuple[int, str, str]]:
|
||||
"""Return ``[(pos, char, block_label), ...]`` for every substrate
|
||||
glyph in *surface*. Empty list means clean."""
|
||||
out: list[tuple[int, str, str]] = []
|
||||
for i, ch in enumerate(surface):
|
||||
cp = ord(ch)
|
||||
for start, end, label in _FORBIDDEN_BLOCKS:
|
||||
if start <= cp <= end:
|
||||
out.append((i, ch, label))
|
||||
break
|
||||
return out
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def cases():
|
||||
return load_cases()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("lens_id", _LENS_IDS_TO_TEST)
|
||||
def test_cognition_lane_surfaces_free_of_substrate_glyphs(cases, lens_id):
|
||||
report = run_eval(cases, config=RuntimeConfig(anchor_lens_id=lens_id))
|
||||
leaks: list[str] = []
|
||||
for case in report.cases:
|
||||
if not case.surface:
|
||||
continue
|
||||
violations = _substrate_glyph_violations(case.surface)
|
||||
if violations:
|
||||
for pos, ch, block in violations:
|
||||
leaks.append(
|
||||
f" case={case.case_id} "
|
||||
f"substrate_glyph={ch!r} (block={block}) at pos {pos} "
|
||||
f"surface={case.surface!r}"
|
||||
)
|
||||
assert not leaks, (
|
||||
f"anchor_lens_no_glyph_leak violated under lens={lens_id!r}.\n"
|
||||
"ChatResponse.surface MUST NOT contain substrate-script glyphs "
|
||||
"(Greek / Hebrew / etc.) regardless of loaded lens "
|
||||
"(ADR-0073 surface contract). Offending cases:\n"
|
||||
+ "\n".join(leaks)
|
||||
)
|
||||
|
||||
|
||||
def test_lens_annotation_is_ascii_directly():
|
||||
"""Independent of the cognition lane: the lens metadata itself
|
||||
(lens_id, cognitive_mode_label) must be pure ASCII so the
|
||||
annotation can never carry non-ASCII even if a future composer
|
||||
forgets to ASCII-check before emit."""
|
||||
from packs.anchor_lens import load_anchor_lens
|
||||
|
||||
for lens_id in ("default_unanchored_v1", "grc_logos_v1", "he_logos_v1"):
|
||||
lens = load_anchor_lens(lens_id)
|
||||
lens.lens_id.encode("ascii")
|
||||
lens.cognitive_mode_label.encode("ascii")
|
||||
for atom in lens.semantic_domain_preferences:
|
||||
atom.encode("ascii")
|
||||
|
|
@ -5,18 +5,14 @@ Pins the load-bearing commitment of ADR-0073 / ADR-0073b:
|
|||
Anchor lens is a composer-side concept, not a property of the
|
||||
proposition graph or trace hash function.
|
||||
|
||||
At L1.2 the lens is loaded by ``chat/runtime.py`` and stored on the
|
||||
runtime, but no other file imports it. The truth-path modules
|
||||
(cognition / trace / pipeline / intent classification / propagation /
|
||||
vault / algebra) must NOT import ``packs.anchor_lens``.
|
||||
At L1.3 the lens is loaded by ``chat/runtime.py`` and consumed by
|
||||
``chat/pack_grounding.py`` (the composer-side allowlist). The
|
||||
truth-path modules (cognition / trace / pipeline / intent
|
||||
classification / propagation / vault / algebra) must NOT import
|
||||
``packs.anchor_lens``.
|
||||
|
||||
This test fails the moment anchor lens leaks into the truth path.
|
||||
|
||||
L1.3 will widen the allow-list to include ``chat/pack_grounding.py``
|
||||
(and any other composer it wires through) at the same time it adds
|
||||
composer behaviour — exactly the way the register seam was widened at
|
||||
R2. Truth-path purity remains absolute.
|
||||
|
||||
Mirror of ``tests/test_register_pack_seam.py`` for the substantive-axis
|
||||
sibling.
|
||||
"""
|
||||
|
|
|
|||
Loading…
Reference in a new issue