acdream/docs/research/2026-06-03-p2-door-stepup-handoff.md
Erik f984e92e37 docs(p2): correct the handoff — B1 was the Path 5 near-miss gate, not the climb
The prior localization (step-up CLIMB) was disproven by an ITestOutputHelper
capture. Records the real root cause (A6.P4 near-miss missing retail's
num_sphere>1 gate, fixed in abbd761), that the door blocks faithfully with a
real floor, and that the remaining red tests are separate (apparatus
synthetic-floor artifact, LiveCompare buggy-captures, D4 airborne) — not
simple "flip to green" targets. Next is the user visual gate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 09:35:14 +02:00

15 KiB
Raw Blame History

P2 pickup handoff — door / building-shell collision = BSP Path 5 grounded step-up

🔴 CORRECTED 2026-06-04 — the localization below (the step-up CLIMB) was WRONG; B1 is FIXED. An ITestOutputHelper capture of B1 (xunit swallows Console.WriteLine) proved the climb code (find_walkable/AdjustSphereToPlane/step_sphere_down/DoStepUp/DoStepDown) is correct and matches ACE exactly. The real B1 bug was the A6.P4 near-miss dispatch in BSPQuery.FindCollisions Path 5 (Contact branch), which diverged from retail three ways: (1) recorded a near-miss NegPolyHit unconditionally — retail gates both set_neg_poly_hit calls behind if (num_sphere > 1) (acclient_2013_pseudo_c.txt:323852); (2) checked the foot near-miss before the head's (retail checks the head/sphere1 first); (3) reversed the neg_step_up mapping (retail: head index0→FALSE/slide, foot index1→TRUE/step-up, per SPHEREPATH::set_neg_poly_hit :323279). For B1's single foot sphere the spurious near-miss → outer !NegStepUp → Collided → revert → the mover wedged at x=0.1, never reached the wall to step up. Verbatim fix committed (abbd761): the gate+order+mapping now match retail; B1 climbs (foot→(0.6,0,0.25)); the Holtburg door blocks faithfully (slab (0,-1,0) normal) when the scenario has a real floor.

Remaining red (NOT simple flips — all separate from B1):

  • Apparatus_Grounded_50cmOffCenter — its tick-0 (0,0,1) "block" is a synthetic-test artifact: the apparatus sets terrain=-1000 so the only BSP is the door slab; the contact-maintenance step-down finds no floor underfoot → false Collided/revert, then the mover walks through. With a real floor (terrain=0) the door blocks faithfully at Y≈11.5 with (0,-1,0). Fix = give the grounded test a real floor + assert the block normal is the door's ±Y (NOT the tick-0 (0,0,1) contact-maintenance hiccup, which is a separate cold-seed first-frame artifact). Do NOT just flip to Assert.True(blocked) — that blesses the artifact.
  • LiveCompare_DoorOffCenterWalkthrough_Tick13558 / _DoorBlocksFromOutside_Tick22760 — compare against captured buggy-live positions; a correct fix makes the harness diverge (blocks earlier). Re-baseline to the corrected behavior or retire as documents-the-bug.
  • D4_AirborneMover_TallWall_PersistsSlidingNormalAcrossFramesairborne (Path 6), a separate sliding-normal-persistence issue, unrelated to the Path 5 grounded near-miss. Pre-existing.

See memory/project_p2_door_stepup_findings.md. Next: USER VISUAL GATE (walk through a cottage door cleanly; step up a stair) — the authoritative P2 acceptance. The original (wrong) analysis is retained below for the record.

Canonical pickup for the next session. Branch claude/thirsty-goldberg-51bb9b (do NOT branch/worktree; do NOT push without asking; NEVER git stash/gc). PowerShell on Windows; launch logs are UTF-16. Read this FIRST.

State both altitudes

  • Milestone: M1.5 — Indoor world feels right.
  • Effort: the VERBATIM spatial-pipeline port (master plan: docs/superpowers/specs/2026-06-03-verbatim-spatial-pipeline-port-master-plan.md).
  • P1 (membership) = DONE. Proven to already match retail; the "0/11 lag" was a cdb capture artifact. Merged to main + pushed to both remotes (HEAD f0d37d8). See docs/research/2026-06-03-p1-membership-swept-advance-handoff.md (RESOLVED banner) + memory/project_retail_membership_criterion.md.
  • P2 (door / building-shell collision) = IN PROGRESS, root cause LOCALIZED. The fix is the next step.
  • Next concrete step: read+compare acdream find_walkable/step_sphere_down vs retail BSPTREE::step_sphere_down (pc:323665) + find_walkable, pin the step-up CLIMB divergence, and drive B1_GroundedMover_LowStep_StepsUp + the door apparatus RED→GREEN.

TL;DR (the P2 finding)

All 5 failing Core tests localize to BSP Path 5 (the grounded Contact + StepSphereUp branch):

Test Symptom
BSPStepUpTests.B1_GroundedMover_LowStep_StepsUp grounded mover wall-slides a walkable 0.25 m step instead of stepping up (CurPos.Z stays 0). The cleanest isolation of the bug.
BSPStepUpTests.D4_AirborneMover_TallWall_PersistsSlidingNormalAcrossFrames airborne tall-wall sliding-normal count (Expected: 2) — Path 4/5 sliding-normal family.
DoorCollisionApparatusTests.Apparatus_Grounded_50cmOffCenter_FrontApproach_DocumentsBug synthetic door + grounded off-center: now blocks at tick 0 with an (0,0,1) up-normal + goes airborne (Path 5 step-up artifact, not a faithful door block).
DoorBugTrajectoryReplayTests.LiveCompare_DoorOffCenterWalkthrough_Tick13558 replay of captured live tick diverges from the engine (documents-the-bug).
DoorBugTrajectoryReplayTests.LiveCompare_DoorBlocksFromOutside_Tick22760 same, outdoor-block tick.

These have been the documented baseline RED set for a while (they are P2's target). They did NOT regress this session — P1's work only touched conformance tests + docs.

Root cause — PRECISELY localized (the whole upstream chain is verified faithful)

For B1 (the cleanest case), the path is reached + dispatched correctly; the divergence is deep in the climb. Verified faithful and ruled out this session (DO NOT re-investigate these — see DO-NOT-RETRY):

  1. Path 5 dispatch is reached. BSPStepUpFixtures.MakeGroundedTransition sets State = Contact | OnWalkable + StepDown=true — but that StepDown is ObjectInfo's flag; the Path 3 dispatch gates on the SpherePath.StepDown flag (only set during a step-down probe), so the main sweep correctly lands in Path 5's Contact branch (BSPQuery.cs:1814).
  2. Recursion guard passes. if (engine is not null && !path.StepUp && !path.StepDown) return StepSphereUp(...) (BSPQuery.cs:1848) — on the main sweep both SpherePath flags are false → StepSphereUpDoStepUp IS called on the wall hit.
  3. DoStepUp (TransitionTypes.cs:3254) = faithful port of retail CTransition::step_up (pc:273099). Same structure: clear contact-plane, stepDownHeight = OnWalkable ? StepUpHeight : 0.04, walkableAllowance = OnWalkable ? GetWalkableZ() : LandingZ, call DoStepDown(...), return its result. (acdream adds a restore-contact-plane-on-failure block — benign.)
  4. DoStepDown (TransitionTypes.cs:3074) = faithful port of retail CTransition::step_down (pc:272946). Skips the down-offset when StepUp is set, runs TransitionalInsert(5), accepts iff OK && ContactPlaneValid && ContactPlane.N.z >= walkableZ, then placement-validates.

So the divergence is INSIDE the step-up climb: DoStepDownTransitionalInsert(5) → Path 3 step_sphere_downfind_walkable's upper-floor find + sphere-up-adjust when sp.StepUp=true. It fails to locate/lift onto the 0.25 floor within the 0.30 budget → DoStepDown returns false → StepUpSlide → wall-slide → Z=0. The retail oracle for the climb is BSPTREE::step_sphere_down (@ 0x53a210 pc:323665) + BSPNODE/BSPLEAF::find_walkable + adjust_sphere_to_plane.

acdream code map: BSPQuery.StepSphereUp (:1372), BSPQuery.step_sphere_down (:1206), BSPQuery.find_walkable (:693), BSPQuery.AdjustSphereToPlane (search it), Transition.DoStepUp (:3254), Transition.DoStepDown (:3074).

⚠ TOOLING NOTE (cost me a probe cycle — don't repeat)

xunit swallows Console.WriteLine. The built-in ACDREAM_DUMP_STEPUP=1 trace (in DoStepUp) and the [step-walk] probe (PhysicsDiagnostics.ProbeStepWalkEnabled) both print via Console → they do NOT surface in dotnet test ... -l "console;verbosity=detailed". The tests that DID show output used ITestOutputHelper (_out.WriteLine). So to trace which climb condition fails (TransitionalInsert(5) result / contact-plane / N.z / placement), add an ITestOutputHelper-based trace to B1 (or a focused harness) — don't rely on the Console probes inside the engine.

DO-NOT-RETRY (verified faithful this session)

  1. Path 5 dispatch / the Contact-branch reachability — confirmed B1 reaches Path 5.
  2. The recursion guard / StepSphereUp call — confirmed DoStepUp is called.
  3. DoStepUp vs retail step_up — faithful, ruled out.
  4. DoStepDown vs retail step_down — faithful, ruled out. The bug is downstream in find_walkable/step_sphere_down's step-up adjust. Start there.

Next steps (evidence-first — the door saga burned many SPECULATIVE fixes; do NOT repeat)

  1. Read+compare acdream BSPQuery.find_walkable (:693) + step_sphere_down (:1206) + AdjustSphereToPlane against retail BSPTREE::step_sphere_down (pc:323665) + BSPNODE/BSPLEAF:: find_walkable + adjust_sphere_to_plane. Focus on the step_up==1 path: how retail lifts the sphere onto a step within step_down_amt, and where acdream fails to.
  2. Instrument B1 with ITestOutputHelper (Console is swallowed — see tooling note) to pin which condition fails: does TransitionalInsert(5) return OK? is ContactPlaneValid set? is the landing N.z >= walkableZ? does placement (TransitionalInsert(1)) reject? B1 is sub-second, headless.
  3. Fix the climb verbatim (cite the decomp anchor), drive B1 GREEN, then B2 (must still block the 5 m wall), then the door apparatus (Apparatus_Grounded_50cmOffCenter… flips to block-not-walkthrough → rewrite its assertion to Assert.True(blocked) && pos.Y < 12.0), then the 2 LiveCompare ticks, then D4.
  4. Definitive cross-check if the decomp is ambiguous: cdb-attach to retail at a Holtburg cottage doorway, break on CTransition::step_up/step_down/BSPTREE::step_sphere_down, walk a low step + the door, capture what retail does. PDB MATCHES; tooling in tools/cdb/ (CLAUDE.md "Retail debugger toolchain"). Needs the user's retail client up + walking.
  5. User visual gate: at a doorway, walk through cleanly (foot Y stable, no oscillation), walls block; step up a low step (cottage stair) climbs.

Test baseline (going into the P2 fix)

Core 1309 pass / 5 fail / 1 skip — the 5 are exactly this P2 target (Apparatus_Grounded…, LiveCompare_DoorOffCenterWalkthrough_Tick13558, LiveCompare_DoorBlocksFromOutside_Tick22760, BSPStepUpTests.D4…, BSPStepUpTests.B1…). Conformance 60 pass / 1 skip / 0 fail. App 177 green.

Parked (do NOT touch without explicit user approval)

  • (a)(d) membership cleanups — approval-gated refactors of WORKING code (CLAUDE.md "don't replace working retail-faithful logic without approval"): (a) remove redundant ResolveCellId (already out of the prod per-frame path; survives only in the DataCache==null test fallback); (b) unify the forked find_env_collisions; (c) replace the CheckBuildingTransit bridge with intrinsic building stabs in find_transit_cells; (d) make the per-cell ObjCell graph the collision authority (collision still uses the landblock-wide ShadowObjectRegistry). The one soft spot: outdoor→indoor 0031↔0170 building-entry is live-clean but NOT conformance-locked (rides on CheckBuildingTransit).
  • Render residuals (P3/P4) — the VISIBLE doorway seam is now in the render path: the flap = camera-collision residual (chase eye drifts out of the cell → viewer-cell flips; master-plan P3, SmartBox::update_viewer); the void = unported PView seal (P4). Membership (physics) is correct. See docs/research/2026-06-03-p1-visual-gate-render-residuals.md. Master-plan order: P2 → P3 → P4.

FRESH-SESSION PROMPT (copy-paste)

Continue the VERBATIM retail spatial-pipeline port for acdream. Branch claude/thirsty-goldberg-51bb9b
(do NOT branch/worktree; do NOT push without asking; NEVER git stash/gc). PowerShell on Windows;
launch logs are UTF-16.

STATE: M1.5 (Indoor world feels right). P1 membership = DONE (proven to already match retail; the
"0/11 lag" was a cdb capture artifact; merged + pushed, HEAD f0d37d8). P2 (door/building-shell
collision) = IN PROGRESS, root cause LOCALIZED to BSP Path 5 grounded step-up. The fix is the job.

READ FIRST (canonical, in order):
1. docs/research/2026-06-03-p2-door-stepup-handoff.md  (THIS handoff — the localization, the
   DO-NOT-RETRY list, the tooling note, the next steps).
2. docs/superpowers/specs/2026-06-03-verbatim-spatial-pipeline-port-master-plan.md (§3 P2; §1/§2 B3/B4).
3. docs/research/2026-05-25-door-bug-partial-fix-shipped.md (the door saga state + its do-not list).

THE FINDING: all 5 failing Core tests localize to BSP Path 5 (grounded Contact + StepSphereUp). For
B1 (cleanest: a grounded mover wall-slides a walkable 0.25 m step with a 0.30 m budget, Z stays 0),
the whole upstream chain is VERIFIED FAITHFUL + correctly reached — Path 5 dispatch, the recursion
guard, DoStepUp (= retail CTransition::step_up pc:273099), DoStepDown (= retail step_down pc:272946).
The divergence is INSIDE the step-up CLIMB: DoStepDown → TransitionalInsert(5) → Path 3
step_sphere_down → find_walkable's upper-floor find + sphere-up-adjust when sp.StepUp=true. It fails
to lift onto the 0.25 floor → StepUpSlide → wall-slide.

DO NOT RE-INVESTIGATE (verified faithful): Path 5 dispatch, the recursion guard, DoStepUp, DoStepDown.
DO NOT speculate on the BSP fix without apparatus (the door saga burned many speculative fixes).
TOOLING: xunit swallows Console.WriteLine — the ACDREAM_DUMP_STEPUP / [step-walk] probes don't surface
in the runner; instrument B1 with ITestOutputHelper to trace the climb conditions.

THE JOB (P2 fix, evidence-first):
1. Read+compare acdream BSPQuery.find_walkable (:693) / step_sphere_down (:1206) / AdjustSphereToPlane
   vs retail BSPTREE::step_sphere_down (pc:323665) + BSPNODE/BSPLEAF::find_walkable + adjust_sphere_to_plane,
   focused on the step_up==1 climb. Pin the divergence (instrument B1 with ITestOutputHelper if needed).
2. Port the climb verbatim (cite the anchor). Drive RED→GREEN: B1 (steps up), then B2 (still blocks the
   5 m wall), then DoorCollisionApparatusTests.Apparatus_Grounded_50cmOffCenter (flips to block — then
   rewrite its documents-the-bug assertion to Assert.True(blocked) && pos.Y < 12.0), then the 2
   LiveCompare ticks, then D4.
3. If the decomp is ambiguous: cdb-attach to retail at a cottage doorway (break on step_up/step_down/
   step_sphere_down) — needs the user's retail client. PDB matches; tools/cdb/.
4. USER VISUAL GATE: walk through a doorway cleanly (foot Y stable, walls block); step up a cottage
   stair (climbs).

TEST BASELINE: Core 1309 pass / 5 fail (the P2 target above) / 1 skip; Conformance 60 pass / 1 skip;
App 177 green. PARKED (need explicit approval): the (a)-(d) membership cleanups + the render residuals
(P3/P4 — the visible doorway flap/void). Do NOT speculate a Path-5 fix before the climb divergence is
pinned with evidence.