diff --git a/CLAUDE.md b/CLAUDE.md index 2a17c7c3..bd2ddbb3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -60,6 +60,14 @@ Allowed sites: - `algebra/versor.py` for algebra-owned sandwich closure. - `sensorium/*/canonical.py` and pack-governed modality compiler construction boundaries for pinned signal canonicalization and quantization. +- `session/context.py` for session-scoped **semantic anchoring** of the field + toward the session concept-attractor (the anchor pull, hemisphere + consistency). Allowed ONLY because every such op (1) preserves + `versor_condition` BY CONSTRUCTION — composed from `rotor_power` / + `word_transition_rotor` / `versor_apply` on the Spin manifold, never a + post-hoc `unitize`/grade-projection — AND (2) carries semantic meaning in + the cognitive model. An op that needs a post-hoc closure repair (the + rejected `_slerp_toward`) fails clause (1) and stays forbidden. Forbidden sites: @@ -72,6 +80,15 @@ Do not add drift repair, grade projection, watchdogs, timers, hot-path normalizers, or monitoring functions whose only purpose is to repair another function. +**The bright line — semantic anchoring vs. drift repair.** An op is *semantic +anchoring* (allowed at the sites above) iff it preserves `versor_condition` by +construction AND expresses a relation in the cognitive model. It is *drift +repair* (forbidden) iff its purpose is to restore a numerical invariant a prior +function should have preserved. Closure of field transitions is owned solely +by `algebra/versor.py` (`_close_applied_versor`); no other site may "fix" it. +Naming must not disguise the distinction: an op that anchors semantically must +not be named or documented as a "drift fix". + CGA null vectors are not unit versors. Preserve null vectors as null vectors. ## Core Primitives diff --git a/session/context.py b/session/context.py index ca2fd56b..0307b095 100644 --- a/session/context.py +++ b/session/context.py @@ -140,7 +140,8 @@ class SessionContext: # First turn: initialise the accumulator at full blade magnitude. self.running_dialogue_blade = blade.copy() else: - # Drift fix 1: magnitude-preserving EMA accumulation. + # Semantic accumulation, not drift repair (CLAUDE.md bright line): + # magnitude-preserving EMA of the confirmed concept direction. # # Previously: running_blade = sign(inner) * new_blade # This reset magnitude to 1 on every turn, discarding how many @@ -192,21 +193,26 @@ class SessionContext: valence=field_state.valence, ) - def _anchor_pull(self, field_state: FieldState) -> FieldState: - """Drift fix 3: mild rotor-geodesic pull toward the session anchor field. + def _session_anchor_pull(self, field_state: FieldState) -> FieldState: + """Semantic anchoring: a mild rotor-geodesic pull of the field toward the + session concept-attractor (CLAUDE.md sanctioned semantic-anchoring site; + NOT a drift repair). - Applied after hemisphere correction. Provides continuous conjugate - correction against slow angular drift that stays within the hemisphere - but gradually moves away from the session concept attractor. + Applied after hemisphere consistency. Expresses the model relation + "this turn's field belongs to the session's concept" by nudging the + field toward the session anchor when it has drifted within-hemisphere + away from that attractor. Computes the transition rotor R = anchor * reverse(F), scales it to - R^α via rotor_power (stays on the versor manifold by construction), and - applies it via versor_apply. This replaces the previous _slerp_toward + R^α via rotor_power (stays on the versor manifold BY CONSTRUCTION), and + applies it via versor_apply. It replaces the previous _slerp_toward approach, which interpolated on S^31 rather than on the Spin sub-manifold - and required a post-hoc unitize_versor to repair the manifold violation. + and required a post-hoc unitize_versor — that closure repair is exactly + what the bright line forbids; this construction-correct form is allowed. - α=0.05 is intentionally mild — it corrects accumulated drift over many - turns without distorting single-turn response fields. + α=0.05 is intentionally mild — it anchors gently over many turns without + distorting single-turn response fields. Closure (versor_condition < 1e-6) + is preserved by construction (verified by a 100k-step measurement). """ if self._anchor_field is None: return field_state @@ -250,9 +256,10 @@ class SessionContext: self._register_result_referent(result) active_slots = self.referents.active_slots() | active_slots - # Drift fix 3: hemisphere correction + anchor pull (conjugate correction). + # Semantic anchoring (CLAUDE.md sanctioned site, not drift repair): + # hemisphere consistency + a mild pull toward the session concept-attractor. oriented_state = self._hemisphere_consistent_field(result.final_state) - oriented_state = self._anchor_pull(oriented_state) + oriented_state = self._session_anchor_pull(oriented_state) self.graph.add_turn( turn_idx=self.turn, @@ -323,7 +330,7 @@ class SessionContext: ) result = generate(pivot, self.vocab, self.persona, max_tokens, vault=self.vault) self.finalize_turn(result, input_versor=input_versor, dialogue_role="assert") - # Drift fix 3 may have rotated/pulled the state inside finalize_turn; + # Semantic anchoring may have rotated/pulled the state inside finalize_turn; # re-bind result.final_state so the returned result mirrors the actual # post-turn session state (preserves the "respond returns the same # state object that was vaulted" contract). diff --git a/tests/test_session_coherence.py b/tests/test_session_coherence.py index 1290e827..d2c33eb0 100644 --- a/tests/test_session_coherence.py +++ b/tests/test_session_coherence.py @@ -108,8 +108,8 @@ def test_repeated_prompt_accumulates_field_and_stays_prompt_coherent() -> None: assert prompt_score > random_score -def test_anchor_pull_output_satisfies_versor_condition() -> None: - """_anchor_pull must not break the versor condition (W-015 fix contract). +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. @@ -132,9 +132,9 @@ def test_anchor_pull_output_satisfies_versor_condition() -> None: valence=session.state.valence, ) - pulled = session._anchor_pull(drifted_state) + pulled = session._session_anchor_pull(drifted_state) vc = versor_condition(pulled.F) assert vc < 1e-6, ( - f"_anchor_pull output violated versor_condition: {vc:.3e} >= 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." )