acdream/docs/research/2026-07-06-137-corridor-phantom-pickup-prompt.md
Erik e73e45da54 docs: #137 corridor-phantom pickup prompt for the next session
Both mechanisms + evidence pointers + the done-already oracle greps,
ordered mechanism-2-first (the sliding-normal wedge, pure acdream-side)
then the mechanism-1 retail cdb trace (needs the user running retail
at the Facility Hub corridor).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 17:26:32 +02:00

4.6 KiB
Raw Blame History

Pickup prompt — #137 corridor phantom collision (paste into a fresh session)

Read claude-memory/project_physics_collision_digest.md FIRST (binding DO-NOT-RETRY table), then ISSUES #137 (the 2026-07-05 CHARACTERIZED section — the full evidence chain lives there), then this file. The 2026-07-05 session's ledger for context: #172 (CCylSphere family port), #173 (remote ceiling bounce), #174 (motion-queue drain — doors work after jumping), #175 (door collision at the motion-table closed pose, two takes).

The bug (user-verified repro, Facility Hub 0x8A02): running down a corridor, an INVISIBLE blocker stops the player mid-corridor; the player can walk around it. Two stacked mechanisms, both evidence-pinned:

Mechanism 1 — PortalSide portal polygons are solid for us

  • Live: crossing corridor cells 0x8A02016E → 0x8A02017A at world x≈85.25 recorded ONE wall hit, normal (1,0,0) — straight against the movement (launch-175-verify2.log:42858, worktree root).
  • Dat (Issue137CorridorSeamInspectionTests, committed): cell 0x8A02016E's three portals to 0x011E (polys 1/3/5, flags=PortalSide, NOT ExactMatch) are PRESENT in CellStruct.PhysicsPolygons; every ExactMatch portal in the same cell is absent from the physics set. The cell's rotation maps those portal planes into the world X wall the player hit.
  • Oracle greps DONE (do not repeat): CCellStruct::UnPack 0x00533d00 loads physics polys + BSP verbatim (no portal stripping); CPolygon::pos_hits_sphere/hits_sphere/polygon_hits_sphere_slow_but_sure (0x005394f0/0x00539540/0x00538a10) are pure geometry; CCellPortal (0x0053bab0) carries portal→CPolygon + portal_side + exact_match but the BSP test chain never consults it.
  • NEXT (step 1 protocol, needs the user): cdb-attach a live retail client at this exact corridor (Facility Hub, the 016E↔011E portals) and trace which path lets retail through: breakpoint BSPTREE::find_collisions / BSPLEAF::sphere_intersects_poly / CPolygon::pos_hits_sphere and see whether the portal polys are ever TESTED (candidate A: sidedness/stippling — the polys carry stip=NoPos — or the pos_hits_sphere tail's directional cull) or never REACHED (candidate B: transit/membership order hands the sphere to the neighbor cell whose geometry has a real hole). Toolchain crib: claude-memory/project_retail_debugger.md + the CLAUDE.md "Retail debugger toolchain" section. Verify the binary with py tools/pdb-extract/check_exe_pdb.py first.
  • ⚠️ Do NOT ship a "skip portal polys in the physics BSP" filter on assumption — if retail's answer is sidedness or test order, a blanket skip opens holes (some PortalSide polys may be legitimately solid from one side — one-way drops etc.).

Mechanism 2 — the sliding-normal absorbing wedge (fix independently)

  • After the single seam hit, EVERY forward resolve returns ok=False hit=no with zero advance: the body-persisted SlidingNormal (1,0,0) projects the +X offset to exactly ZERO in Transition.AdjustOffset, and the stepping loop's abort-small-offset fires at step 0 (TransitionTypes FindValidPosition loop, return i != 0 && …) — BEFORE any collision test could refresh the state. An ABSORBING wedge; strafing escapes it (the user's "push through on the side").
  • Retail re-derives slide state per frame — OBJECTINFO::get_object_info pc:279992 "governs only the NEXT frame" (#116 notes in the digest). AUDIT: who writes the body's persisted SlidingNormal (PhysicsEngine.ResolveWithTransition seed ~:995-1040 + the writeback), and where retail CLEARS it when contact does not recur. This is the #116 slide-response family — check ISSUES #116 before changing anything (oracle-first; the digest's DO-NOT-RETRY table applies).
  • Likely the bigger playability win: without the wedge, mechanism 1 alone would be a momentary stutter, not a dead stop.

Order: mechanism 2 first (pure acdream-side audit + fix, testable with a replay-style unit test: seed a body with a stale sliding normal, resolve forward with no obstruction in range, assert the step is NOT zeroed), then the mechanism-1 cdb session when the user can run retail side-by-side.

Gotchas: PowerShell Tee logs are UTF-16 (tr -d '\000' before grep); the user manages client lifecycle; probes RESOLVE/CELL/BUILDING are DebugPanel-toggleable (ACDREAM_DEVTOOLS=1); the [shape-pose] line (ACDREAM_DUMP_MOTION=1) prints each BSP registration's pose source. Register rows to touch if fixes ship: none exist yet for either mechanism — add per the same-commit rule; #116's row interactions per the digest.