From 424a67a9ce04e45b41fa5f46f5534b0583fdeef3 Mon Sep 17 00:00:00 2001 From: Shay Date: Thu, 14 May 2026 13:10:34 -0700 Subject: [PATCH] persona: add PersonaMotor.from_identity_manifold() factory Builds a real, non-identity CGA motor from the value_axes directions carried by an IdentityManifold. Each axis.direction is treated as a 3-vector in R^3, composed additively into a single translator, and scaled by the axis's index to separate the directions in concept space. This replaces the unconditional PersonaMotor.identity() call in ChatRuntime with a motor that geometrically encodes CORE's character. --- persona/motor.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/persona/motor.py b/persona/motor.py index e86d8c29..50282f0f 100644 --- a/persona/motor.py +++ b/persona/motor.py @@ -83,3 +83,37 @@ class PersonaMotor: unitize_versor(translator), unitize_versor(rotor), ) + + @classmethod + def from_identity_manifold(cls, manifold) -> "PersonaMotor": + """ + Build a persona motor from a live IdentityManifold. + + Each value_axis carries a direction tuple in R^3. The axes are + composed additively into a single concept vector, then passed to + from_concept_vector() to produce the CGA translator that encodes + CORE's character as a geometric displacement in concept space. + + The resulting motor is non-identity: it biases every field walk + toward the manifold's value directions without overriding the + algebraic propagation rules. + + Falls back to identity() if the manifold has no value_axes or if + all directions are zero — preserving safe-default behavior. + """ + if not manifold.value_axes: + return cls.identity() + + combined = np.zeros(3, dtype=np.float32) + for axis in manifold.value_axes: + direction = np.asarray(axis.direction[:3], dtype=np.float32) + if np.linalg.norm(direction) > 1e-8: + combined += direction + + if np.linalg.norm(combined) < 1e-8: + return cls.identity() + + # Normalize so the motor magnitude is consistent regardless of + # how many axes are present. + combined /= np.linalg.norm(combined) + return cls.from_concept_vector(combined)