fix #137 (mechanism 2): BSP full-hit stubs leaked sliding normals — the corridor absorbing wedge

The Facility Hub corridor dead-stop's second half: after one seam hit,
every forward resolve returned ok=False hit=no with zero advance. The
body-persisted SlidingNormal (-1,0,0) projected the exactly-anti-parallel
corridor push to zero in AdjustOffset and the step loop aborted at step 0
before any collision test could refresh the state.

Audit (docs/research/2026-07-06-137-sliding-normal-lifecycle-audit.md):
retail's only in-transition sliding-normal writer is validate_transition
(0x0050ac21); the whole sphere/BSP layer writes NONE (grep-verified), and
the body persistence (SetPositionInternal 0x005154c2, SLIDING_TS bit sync
0x005154e1) runs only on transition success. Our BSPQuery Contact-branch
full-hit responses were stubs (SetCollisionNormal + SetSlidingNormal +
return Slid) where retail dispatches the real slide_sphere — so the seam
hit (a SUCCESSFUL full-advance resolve per the live log) persisted the
phantom wall's normal, which retail's lifecycle structurally cannot do.

- BSPQuery Contact foot full-hit fallback + head full-hit now route
  through Transition.SlideSphereInternal (CSphere::slide_sphere
  0x00537440 — in-frame slide, no sliding-normal write; ACE
  BSPTree.cs:202,310-316). The dead stub is rewritten as the faithful
  BSPTREE::slide_sphere wrapper.
- PhysicsEngine sliding writeback gated on ok (retail success-only
  placement; behaviorally latent, removes the failed-frame leak class).
- Register: TS-4 amended (Path-6 steep-tangent sites still write the
  normal — now documented), TS-45 added (SphereCollision's write — same
  leak class, left for a follow-up out of #171's blast radius).
- Pins: Issue137SlidingNormalLifecycleTests — both site pins RED->GREEN,
  plus the retail persist/absorb/clear wall lifecycle (validate-write
  persistence, faithful absorbed anti-parallel frame, oblique escape
  clears the bit). BSPQueryTests full-hit pin updated to the real slide.

Mechanism 1 (PortalSide portal polys solid in the physics set) stays
OPEN - #137 not closed; the corridor re-test rides that session.

Suites: Core 2545 / App 713 / UI 425 / Net 385, 0 failures.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-07-05 18:08:29 +02:00
parent e73e45da54
commit a11df5b8d3
7 changed files with 474 additions and 31 deletions

View file

@ -1220,6 +1220,27 @@ matching the door's open/closed state.)
(get_object_info pc:279992 governs only the NEXT frame — #116 notes);
audit who clears the body's sliding normal when no contact recurs.
**MECHANISM 2 FIXED 2026-07-06 (audit complete — full lifecycle in
`docs/research/2026-07-06-137-sliding-normal-lifecycle-audit.md`):**
retail's ONLY in-transition sliding-normal writer is
`validate_transition` (0x0050ac21); the BSP/sphere layer never writes it,
and the body persistence (`SetPositionInternal` 0x005154c2/0x005154e1)
is success-only. Our BSPQuery Contact-branch full-hit responses were
STUBS (`SetSlidingNormal + return Slid`) where retail dispatches the
real `slide_sphere` — the seam hit (a SUCCESSFUL full-advance resolve,
`ok=True` in the log, not a failed one) leaked the phantom wall's normal
into the body, and the seed absorbed every later forward push. Fix:
both stub sites now route through the real
`Transition.SlideSphereInternal` (`CSphere::slide_sphere` 0x00537440,
in-frame, no sliding write) and the body writeback is gated on
transition success. Pins: `Issue137SlidingNormalLifecycleTests` (2 site
pins + the persist/absorb/clear wall lifecycle). Register: TS-4 amended
(steep-tangent sites still write the normal — documented), TS-45 added
(`SphereCollision`'s write — same class, out of blast radius). The
absorbed exactly-anti-parallel frame at a REAL wall is retail-faithful
(the persisted normal is a "still pressed" cache); only the phantom
PROVENANCE was the bug. Corridor re-test rides the mechanism-1 session.
**Files:** `src/AcDream.Core/Physics/` (EnvCell collision, CellTransit, the door apparatus),
`src/AcDream.Core/Physics/ShadowObjectRegistry.cs` (per-cell registration). See
`claude-memory/project_physics_collision_digest.md` (the collision SSOT + DO-NOT-RETRY table).