From 15ed2cee8965864b28c626ceb15039885b4afaa5 Mon Sep 17 00:00:00 2001 From: Shay Date: Fri, 15 May 2026 08:14:38 -0700 Subject: [PATCH] Tighten hot-path backend consistency - route SessionContext anchor CGA through algebra.backend - move aspect-weight carry into FieldEnergyOperator.compute - remove duplicated propagate_step threshold patch and per-step imports - add carry_aspect_weight tests for parity, fallback, and propagation preservation - preserve normalization, propagation, vault, Rust dispatch, and energy cadence semantics --- core/physics/energy.py | 3 ++- field/propagate.py | 32 ++------------------------------ session/context.py | 4 ++-- tests/test_energy.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 33 deletions(-) diff --git a/core/physics/energy.py b/core/physics/energy.py index 5ca4453f..32644ad7 100644 --- a/core/physics/energy.py +++ b/core/physics/energy.py @@ -87,12 +87,13 @@ class FieldEnergyOperator: coherence_residual: float = 0.0, morphology_features: Mapping[str, object] | None = None, anchor_adjacent: bool = False, + carry_aspect_weight: float = 0.0, ) -> EnergyProfile: convergence = min(log1p(max(0, convergence_density)) / log1p(8), 1.0) age = max(0, int(current_cycle) - int(last_activation_cycle)) recency = min(max(0, activation_count), 8) / 8.0 * exp(-age / 12.0) residual = min(max(0.0, float(coherence_residual)), 1.0) - aspect = aspect_weight(morphology_features) + aspect = carry_aspect_weight if carry_aspect_weight > 0.0 else aspect_weight(morphology_features) raw = (0.35 * convergence) + (0.25 * recency) + (0.20 * residual) + (0.20 * aspect) if anchor_adjacent and raw >= 0.72: energy_class = EnergyClass.E4 diff --git a/field/propagate.py b/field/propagate.py index ed751b32..003a53f3 100644 --- a/field/propagate.py +++ b/field/propagate.py @@ -55,38 +55,10 @@ def propagate_step(state: FieldState, V) -> FieldState: current_cycle=new_step, last_activation_cycle=ep.last_activation_cycle, coherence_residual=0.0, - morphology_features=None, # aspect weight baked at injection; not re-read here + morphology_features=None, anchor_adjacent=ep.anchor_adjacent, + carry_aspect_weight=ep.aspect_weight, ) - # Carry the baked aspect_weight forward: the operator won't re-derive - # it from morphology_features=None, so we patch the raw score to - # preserve the aspect contribution that was set at the gate. - if ep.aspect_weight > 0.0: - from dataclasses import replace as _replace - # Recompute with the original aspect weight patched back in: - # raw already accounts for convergence/recency/residual from above. - # We rebuild raw adding the aspect component the operator lost. - patched_raw = new_energy.raw + 0.20 * ep.aspect_weight - patched_raw = min(patched_raw, 1.0) - from core.physics.energy import EnergyClass as _EC - if ep.anchor_adjacent and patched_raw >= 0.72: - patched_class = _EC.E4 - elif patched_raw >= 0.82: - patched_class = _EC.E4 - elif patched_raw >= 0.62: - patched_class = _EC.E3 - elif patched_raw >= 0.38: - patched_class = _EC.E2 - elif patched_raw >= 0.16: - patched_class = _EC.E1 - else: - patched_class = _EC.E0 - new_energy = _replace( - new_energy, - raw=patched_raw, - energy_class=patched_class, - aspect_weight=ep.aspect_weight, - ) else: new_energy = None diff --git a/session/context.py b/session/context.py index 68283f41..77fcc359 100644 --- a/session/context.py +++ b/session/context.py @@ -14,8 +14,8 @@ from __future__ import annotations import numpy as np -from algebra.backend import versor_apply -from algebra.cga import cga_inner, outer_product +from algebra.backend import cga_inner, versor_apply +from algebra.cga import outer_product from field.state import FieldState from generate.dialogue import DialogueTurn from generate.proposition import Proposition diff --git a/tests/test_energy.py b/tests/test_energy.py index ba486e0b..cbc5b7fa 100644 --- a/tests/test_energy.py +++ b/tests/test_energy.py @@ -344,6 +344,48 @@ class TestPropagateStepEnergyRecomputation: assert state.energy.energy_class is not EnergyClass.E4 +# --------------------------------------------------------------------------- +# carry_aspect_weight consolidation +# --------------------------------------------------------------------------- + +class TestCarryAspectWeight: + def test_carry_aspect_matches_morphology_derived(self): + """carry_aspect_weight produces identical raw/class as morphology-derived aspect.""" + base_kw = dict( + convergence_density=4, + activation_count=4, + current_cycle=1, + last_activation_cycle=0, + coherence_residual=0.0, + anchor_adjacent=False, + ) + from_morph = _op.compute(**base_kw, morphology_features={"mood": "imperative"}) + from_carry = _op.compute(**base_kw, carry_aspect_weight=0.90) + assert from_carry.raw == pytest.approx(from_morph.raw) + assert from_carry.energy_class is from_morph.energy_class + assert from_carry.aspect_weight == pytest.approx(from_morph.aspect_weight) + + def test_carry_zero_falls_back_to_morphology(self): + ep = _op.compute( + morphology_features={"aspect": "yiqtol"}, + carry_aspect_weight=0.0, + ) + assert ep.aspect_weight == pytest.approx(0.65) + + def test_propagate_step_preserves_baked_aspect_weight(self): + """Aspect weight injected at the gate survives propagation via carry.""" + ep = _op.compute( + convergence_density=4, + activation_count=2, + current_cycle=0, + morphology_features={"mood": "imperative"}, + ) + state = FieldState(F=_clean_versor(), node=0, step=0, energy=ep) + new_state = propagate_step(state, _identity_rotor()) + assert new_state.energy.aspect_weight == pytest.approx(0.90) + assert new_state.energy.raw > 0.0 + + # --------------------------------------------------------------------------- # EnergyProfile field storage round-trip on FieldState # ---------------------------------------------------------------------------