core/docs/audit/W-015-investigation.md
Shay cad24f12b1
audit(W-015): investigate session/context.py unitize root cause (#252)
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.
2026-05-24 19:41:41 -07:00

5.1 KiB
Raw Permalink Blame History

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:132140) 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:3864) 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:218219:

"by construction (versor_condition stays < 1e-6), unlike a linear blend weight·V + (1-weight)·identity which 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:3864: 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.

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.