Instruments _anchor_pull to measure versor_condition(pulled_F) before unitize_versor across 4,138 samples from session/chat test suites. Verdict: (c) upstream construction violation. _slerp_toward operates on S^31 (the 32D unit sphere) rather than the Spin sub-manifold, producing off-manifold state with vc up to 38.58 for non-negligible field-to-anchor angles. Distribution is strictly bimodal: vc < 1e-6 when theta ≈ 0 (slerp is near-identity), otherwise vc >> 1e-3 — confirming the slerp is the sole source. Recommended fix (separate PR): replace _slerp_toward with rotor geodesic interpolation via the Lie group exponential map (same principle as rotor_power used in generate/stream.py:220), eliminating the post-slerp unitize by construction.
5.1 KiB
W-015 Investigation: session/context.py unitize root cause
Question
What is the source of the versor-condition drift that session/context.py:236
corrects, and which of the three resolution paths applies?
Evidence collected
Instrumented _anchor_pull to record versor_condition(pulled_F) immediately
before unitize_versor(pulled_F). Ran tests/test_session_coherence.py,
tests/test_achat.py, tests/test_chat_runtime.py, and
tests/test_warmed_session_lane.py (4,138 samples total across 25 tests).
| Band | Count | % |
|---|---|---|
| < 1e-6 (invariant satisfied — unitize is near no-op) | 1,912 | 46% |
| [1e-6, 1e-3) (small drift) | 0 | 0% |
| [1e-3, 1) (large violation) | 2,201 | 53% |
| ≥ 1 (massive violation) | 25 | 1% |
Distribution is strictly bimodal: no samples fall in [1e-6, 1e-3). The maximum observed pre-unitize versor_condition is 38.58; median is 0.19 across the dirty half.
The 1,912 "clean" samples correspond to calls where _slerp_toward's near-
parallel fallback fires (theta < 1e-6), i.e., the field has not drifted from
the anchor — result ≈ (1-α)·F + α·target ≈ F, so vc is preserved. The 2,226
dirty samples are turns with non-negligible field-to-anchor angle.
Upstream trace
Call chain leading to the unitize:
SessionContext.respond() session/context.py:321
└→ generate(self.state, ...) generate/stream.py (generate())
└→ GenerationResult(
final_state=_close_final_state(current) stream.py:641
) # unitizes F → vc < 1e-6
└→ SessionContext.finalize_turn(result) session/context.py:337
└→ _hemisphere_consistent_field(result.final_state) context.py:272
# negates F if wrong hemisphere — vc-preserving
└→ _anchor_pull(oriented_state) context.py:273
└→ _slerp_toward(field_state.F,
self._anchor_field,
_ANCHOR_PULL_ALPHA) context.py:235
# ← SOURCE OF VIOLATION
└→ unitize_versor(pulled_F) context.py:236
# ← THE SITE UNDER INVESTIGATION
_close_final_state (generate/stream.py:132–140) calls unitize_versor and
ensures result.final_state.F satisfies vc < 1e-6 on entry. The hemisphere
flip preserves vc. The sole source of the violation is _slerp_toward.
_slerp_toward (session/context.py:38–64) performs spherical linear
interpolation (slerp) on the 32-component multivector representation. Slerp
traces a geodesic on S³¹ (the unit sphere in 32D), not on the Spin
sub-manifold embedded within Cl(4,1). For any non-negligible angle θ between
F and anchor, the slerp output is a point on S³¹ that is not a proper versor —
the versor condition |v·rev(v) − 1| can diverge far from zero. The strict
bimodal distribution (no samples in [1e-6, 1e-3)) confirms this: either θ ≈ 0
(clean) or the slerp leaves the versor manifold dramatically (dirty).
This pattern is explicitly noted at generate/stream.py:218–219:
"by construction (versor_condition stays < 1e-6), unlike a linear blend
weight·V + (1-weight)·identitywhich violates closure."
Stream.py avoids linear blending for exactly this reason. _slerp_toward is
the same category of error applied to an anchor-pull operation.
Cross-check: field/operators.py:_unitize_f32
field/operators.py:69 defines _unitize_f32, used by GraphDiffusionOperator
and ConstraintCorrectionOperator to close blend-then-unitize steps in the
field propagation pulse loop. This is an independent concern: it lives
inside operator-pipeline boundaries (L1 audit flagged it as pulse-only
normalization), whereas session/context.py's site is at the session
finalization boundary. The two sites share the same class of error (blending on
the wrong manifold) but operate at different layers and have different fix paths.
Verdict
(c) Upstream construction violation. The drift is not small and consistent
(ruling out (a)) and is not a near-no-op (ruling out (b)). The source is
_slerp_toward at session/context.py:38–64: it interpolates on S³¹ rather
than on the Spin group, producing off-manifold state with vc up to 38.58 for
non-negligible field-to-anchor angles.
Recommended next action
Replace _slerp_toward with proper rotor geodesic interpolation via the
Lie group exponential map:
R_rel = R_anchor · reverse(R_current)
R_step = exp(α · log(R_rel))
result = R_step · R_current
This stays on the versor manifold by construction (same principle as
rotor_power used at generate/stream.py:220), eliminating the need for
unitize_versor in _anchor_pull. The fix lives entirely in session/context.py
(replace _slerp_toward + remove the unitize_versor call). Sized as a small
focused PR; a test asserting versor_condition(_anchor_pull(s).F) < 1e-6
without the closing unitize would verify the fix without altering the invariant.
algebra/versor.py already exposes versor_apply and reverse; rotor_power
lives in generate/stream.py — the implementation is straightforward once the
operator algebra is right.