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.
This commit is contained in:
Shay 2026-05-24 19:41:41 -07:00 committed by GitHub
parent 085bfe0895
commit cad24f12b1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -0,0 +1,111 @@
# 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.
## 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.