# Door collision — end-of-session handoff (2026-05-24, late) **Branch:** `claude/strange-albattani-3fc83c` **Worktree:** `C:\Users\erikn\source\repos\acdream\.claude\worktrees\strange-albattani-3fc83c` ## TL;DR — what was actually accomplished **No user-visible bug was fixed this session.** The door bug the user reported at the start (center blocks, off-center walks through, inside-out walks through) is **identically reproducible after the 4 commits** as it was before them. What changed: infrastructure. Server-spawned doors now register multi-part shadow shapes (cylinder + BSP slab) instead of one cylinder approximation. The BSP slab is queried 135 times per door near approach but produces **zero collision hits**, so observed behavior is unchanged. **Don't re-do the infrastructure.** It's correctly built and necessary for any future fix. The remaining work is downstream of it. ## Commits landed (4) ``` 163a1f0 diag(phys): [bsp-test] probe + grounded apparatus test + handoff ca9341c feat(phys): A6.P4 Task 7 — RegisterLiveEntityCollision uses ShadowShapeBuilder + RegisterMultiPart 3b7dc46 fix(phys): GetNearbyObjects dedup-by-entityId silently drops multi-part shadows e1d94d7 test(phys): door setup + GfxObj dat-inspection — Hypothesis A falsified ``` **Real-but-latent value from these:** - The dedup-by-entityId issue (3b7dc46) was a latent footgun: any future attempt at multi-part shadows (NPCs with hit-region capsules, multi-part creatures, props with separated collision) would have silently dropped all but the first shape. Now safe. - The dat-inspection (e1d94d7) proved part 0 (`0x010044B5`) has a real 1.9×0.26×2.5 m BSP slab in the dat. A future fix doesn't have to question whether the data exists. - The Task 7 wiring (ca9341c) puts the right architecture in place — doors now register the shapes retail expects (cyl per CylSphere + cyl per Sphere + BSP per Part-with-BSP). - The `[bsp-test]` probe (163a1f0) fires before the cache lookup, distinguishing "cache miss → silent skip" from "queried but no hit" — neither of which `[resolve-bldg]` ever showed. **Brutally clear: zero of these commits change observed door behavior.** ## What we now know vs. what we don't ### Known (from this session's probes) - `0x010044B5` PhysicsBSP has 6 collision-bearing polygons forming a 1.925 × 0.261 × 2.490 m door slab. All `SidesType=Landblock` (two-sided). Bounding sphere radius 1.975 m. Verified by direct dat read. - `0x010044B6` (the two leaf parts) have `HasPhysics=false`, `PhysicsBSP=null`, `PhysicsPolygons.Count=0`. Visual-only by retail design — our skip matches retail's `CPhysicsPart::find_obj_collisions:275051`. - Live Holtburg doors register with `shapes=cyl1+bsp1`. Cache is populated. BSP entries are visited (135x for one door at player approaches as close as 0.42 m). - The BSP traversal produces ZERO attributed hits during live walking (all 19 `[resolve-bldg]` lines show `gfxObj=0x00000000`, which is the Cylinder shape). Whatever is happening inside `SphereIntersectsPolyInternal` or the dispatch around it is swallowing the hit silently. ### NOT known (don't speculate further) - **Whether `DoStepUp` is involved.** The prior handoff doc (`2026-05-24-door-collision-task7-shipped-but-bug-remains.md`) asserted "step-up incorrectly succeeds" as the leading hypothesis. That was over-reach. In the apparatus, `ACDREAM_DUMP_STEPUP=1` produced no `stepup: ENTER` lines — `DoStepUp` was never called. So the apparatus shows `hit=yes n=(0,0,1)` from some OTHER path (terrain step-down? walkable poly preservation?). It does not confirm step-up is the production bug. - **Whether the production hit happens at the BSP polygon edge test, the BSP node traversal, or some other layer.** - **Whether the production code path is the same as the apparatus path 5 in the first place.** The earlier framing of "step-up is the bug" was a guess I inflated into a conclusion. Treat it as a candidate, not a finding. ## Proper next move **Same pattern that closed issue #98 after 6+ failed speculation rounds: live capture + apparatus replay.** The infrastructure for this already exists in the codebase: 1. `ACDREAM_CAPTURE_RESOLVE=` env var (see `src/AcDream.Core/Physics/PhysicsResolveCapture.cs`) captures every player-side `PhysicsEngine.ResolveWithTransition` call as a JSON Lines record with full `PhysicsBody` before-and-after snapshots. 2. `CellarUpTrajectoryReplayTests.LoadCapturedRecord` + `AssertCallMatchesCapture` replay a captured record through a harness engine and emit the first per-field divergence between live and harness outputs. The plan: 1. Launch with `ACDREAM_CAPTURE_RESOLVE=door-walkthrough.jsonl` (no other probes — capture is independent). 2. Walk into a closed Holtburg cottage door 50 cm off-center. 3. Close gracefully. Save the JSONL. 4. Write a new test `LiveCompare_DoorOffCenterWalkthrough` that loads the failing-tick record and replays it through a harness with the real `0x010044B5` BSP hydrated + registered via `RegisterMultiPart`. Compare per-field. 5. The first divergent field names the broken assumption. Fix that. This is concrete, deterministic, and doesn't ask you to relaunch multiple times for each fix attempt. The harness round-trip is <500 ms; a fix iteration is ~3 seconds. ## What NOT to do 1. **Do NOT re-do the multi-part registration.** It's correct. The dedup fix is correct. Task 7 is correct. Verified by 53/53 tests in the targeted scope. 2. **Do NOT speculate-and-fix.** This session burned cycles on a "step-up is the bug" hypothesis that wasn't supported by the evidence. The apparatus-first rule (`feedback_apparatus_for_physics_bugs.md`) exists for exactly this. Build the apparatus before the fix. 3. **Do NOT re-investigate whether the door has BSP polygons.** It does. 6 of them. Forming a full door slab. Cached. Visited. 4. **Do NOT relaunch with more probes hoping for an obvious signal.** The probes we have already say "BSP visited 135 times, no hits." More log lines won't tell us WHY it doesn't hit. The apparatus replay will. ## Files to read first - This doc (you're in it). - `docs/research/2026-05-24-door-dat-inspection-findings.md` — the dat data, polygon layout, bounding sphere center vs frame offset. - `docs/research/2026-05-24-door-collision-task7-shipped-but-bug-remains.md` — the prior end-of-session handoff. **Read with skepticism** — its "leading hypothesis" section overstates confidence in the step-up theory (corrected here). - `tests/AcDream.Core.Tests/Physics/CellarUpTrajectoryReplayTests.cs` — the capture+replay pattern to mirror for the door bug. See `LiveCompare_*` tests. ## State of the M1.5 milestone Doors at Holtburg cottages: center blocks, off-center walks through, inside-out walks through. Same as it was 24 hours ago. The walking- through case is the actual user pain point. Until the apparatus replay names the divergence, treat M1.5 indoor-world as still incomplete on the door front. The infrastructure is in place for the eventual fix. The fix itself remains future work. ## Pickup prompt for the next session ``` Door collision investigation. Previous session shipped infrastructure (multi-part registration + GetNearbyObjects dedup fix) but did NOT fix the user-visible bug: off-center / inside-out approaches still walk through closed Holtburg cottage doors. Read docs/research/2026-05-24-door-collision-session-end-handoff.md State both altitudes: Currently working toward: M1.5 — Indoor world feels right Current phase: A6.P4 door bug — apparatus replay phase. Multi-part registration shipped; need live capture + per-field divergence comparison to identify why the door's BSP slab fires zero attributed hits despite being visited 135x per approach. First move: launch the client with ACDREAM_CAPTURE_RESOLVE=, walk into a closed Holtburg cottage door 50 cm off-center, close gracefully. Then write a LiveCompare_* test in CellarUpTrajectoryReplayTests that loads the captured failing tick + replays through a harness with the door BSP hydrated via the existing 0x010044B5 dat read pattern and registered via RegisterMultiPart. DO NOT redo the multi-part registration. DO NOT speculate about step-up without evidence — the apparatus tested DoStepUp and it didn't fire. The bug is upstream of step-up. The replay will name the actual divergence. ```