core/tests/test_session_coherence.py
Shay 81ea72482f refactor(field): rule the session anchoring family as sanctioned semantic anchoring (L10 Decision 0)
L10 scoping Decision 0 ruled the session/context.py "drift fix" family as
sanctioned SEMANTIC anchoring, not forbidden drift-repair:
- closure (versor_condition<1e-6) is owned by the sanctioned algebra/versor.py
  sandwich closure and holds BY CONSTRUCTION (measured: 100k-step field walk,
  max versor_condition ~6e-13, flat, with AND without the anchor pull);
- the family preserves the invariant by construction (rotor_power /
  word_transition_rotor / versor_apply on the Spin manifold, no post-hoc
  unitize) and expresses the session concept-attractor model.

CLAUDE.md: add session/context.py semantic anchoring to sanctioned normalization
sites behind a two-clause guard, plus a bright-line paragraph (semantic anchoring
vs drift repair; closure owned solely by algebra/versor.py; no "drift fix" naming).

Rename _anchor_pull -> _session_anchor_pull; reframe the "Drift fix 1/3" /
"conjugate correction against slow angular drift" docs as semantic anchoring
(no behavior change). Update test_session_coherence.py to the new name.

Out of scope (flagged for separate review): generate/stream.py "Drift fix 2"
sits in a forbidden normalization site.

Verified: 15 targeted tests green; INV-02 normalization invariant unaffected.
2026-06-05 07:48:46 -07:00

140 lines
4.9 KiB
Python

from __future__ import annotations
import numpy as np
from algebra.backend import cga_inner
from algebra.versor import unitize_versor, versor_condition
from session.context import SessionContext
from vocab.manifold import VocabManifold
def _positive_unit_reflector(seed: int) -> np.ndarray:
rng = np.random.default_rng(seed)
vec4 = rng.standard_normal(4).astype(np.float32)
norm4 = float(np.linalg.norm(vec4))
if norm4 < 1e-6:
vec4[0] = 1.0
norm4 = 1.0
vec = np.zeros(5, dtype=np.float32)
vec[:4] = vec4
vec[4] = 0.25 * norm4 * np.tanh(float(rng.standard_normal()))
mv = np.zeros(32, dtype=np.float32)
mv[1:6] = vec
return unitize_versor(mv)
def _random_rotor(seed: int) -> np.ndarray:
"""Build a random even-grade versor (rotor) for CGA inner-product comparison.
Field states are even-grade (grade 0+2+4). Reflectors are grade-1 and
orthogonal under cga_inner, so we need rotors to get nonzero scores.
"""
a = _positive_unit_reflector(seed)
b = _positive_unit_reflector(seed + 10000)
from algebra.cl41 import geometric_product as gp
rotor = gp(a, b)
return unitize_versor(rotor)
def _vocab() -> VocabManifold:
vocab = VocabManifold()
vocab.add("logos", _positive_unit_reflector(1))
vocab.add("arche", _positive_unit_reflector(2))
vocab.add("pneuma", _positive_unit_reflector(3))
vocab.add("truth", _positive_unit_reflector(4))
vocab.add("nous", _positive_unit_reflector(5))
return vocab
def _farther_unrelated(result_F: np.ndarray, prompt_F: np.ndarray, start_seed: int) -> np.ndarray:
prompt_score = cga_inner(result_F, prompt_F)
for seed in range(start_seed, start_seed + 2048):
candidate = _random_rotor(seed)
if prompt_score > cga_inner(result_F, candidate):
return candidate
raise AssertionError("Could not construct a deterministic farther unrelated versor.")
def test_finalize_turn_enforces_cga_hemisphere_consistency() -> None:
"""Vault entries must share the anchor's CGA hemisphere for recall to rank correctly."""
from generate.result import GenerationResult
from field.state import FieldState
vocab = _vocab()
session = SessionContext(vocab=vocab)
session.ingest(["logos"])
anchor = session._anchor_field.copy()
anti_F = -session.state.F
anti_state = FieldState(
F=anti_F,
node=session.state.node,
step=session.state.step,
holonomy=session.state.holonomy,
energy=session.state.energy,
valence=session.state.valence,
)
anti_result = GenerationResult(tokens=("arche",), final_state=anti_state, vault_hits=0)
assert cga_inner(anti_F, anchor) < 0.0, "precondition: anti-hemisphere field"
session.finalize_turn(anti_result, dialogue_role="assert")
assert cga_inner(session.state.F, anchor) >= 0.0, (
"finalize_turn must flip anti-hemisphere fields to keep vault recall consistent"
)
def test_repeated_prompt_accumulates_field_and_stays_prompt_coherent() -> None:
session = SessionContext(vocab=_vocab())
prompt = ["logos", "arche"]
initial = session.ingest(prompt)
first = session.respond(max_tokens=4)
second_prompt_state = session.ingest(prompt)
assert not np.array_equal(second_prompt_state.F, initial.F)
second = session.respond(max_tokens=4)
assert second.tokens != first.tokens
assert not np.array_equal(second.final_state.F, first.final_state.F)
for i, result in enumerate((first, second)):
random_unrelated = _farther_unrelated(result.final_state.F, initial.F, 11 + (i * 64))
prompt_score = cga_inner(result.final_state.F, initial.F)
random_score = cga_inner(result.final_state.F, random_unrelated)
assert prompt_score > random_score
def test_session_anchor_pull_output_satisfies_versor_condition() -> None:
"""_session_anchor_pull must not break the versor condition (W-015 fix contract).
The previous _slerp_toward implementation interpolated on S^31 rather than
the Spin sub-manifold, producing versor_condition values up to 38.58.
The rotor-geodesic replacement must satisfy vc < 1e-6 by construction.
"""
from field.state import FieldState
vocab = _vocab()
session = SessionContext(vocab=vocab)
session.ingest(["logos"])
# Build a drifted field well separated from the anchor.
drifted = _random_rotor(seed=42)
drifted_state = FieldState(
F=drifted,
node=session.state.node,
step=session.state.step,
holonomy=session.state.holonomy,
energy=session.state.energy,
valence=session.state.valence,
)
pulled = session._session_anchor_pull(drifted_state)
vc = versor_condition(pulled.F)
assert vc < 1e-6, (
f"_session_anchor_pull output violated versor_condition: {vc:.3e} >= 1e-6. "
"The rotor-geodesic path must stay on the Spin sub-manifold by construction."
)