# P1 pickup handoff — doorway membership: port retail's swept `curr_cell` advance > **Canonical pickup document 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, then the linked docs as needed. ## ⚠ RESOLVED 2026-06-03 — premise REVERSED (read this first) The thesis of this doc ("port retail's swept `curr_cell` advance") is **FALSIFIED**. acdream's doorway membership already matches retail. The "0/11 production divergence" was a **cdb CAPTURE ARTIFACT**: `CPhysicsObj::SetPositionInternal` calls `change_cell` (`acclient_2013_pseudo_c.txt:283456`) BEFORE `set_frame` writes `m_position` (`:283458`), so the original golden paired each frame's NEW cell with the PREVIOUS frame's position — a one-frame skew (`golden_picked[i] == geom(golden_position[i+1])`, verified on all 22 rows; and acdream's static pick `== golden_picked[i-1]` on all rows). Re-capturing the COMMITTED position from the following `set_frame` (`tools/cdb/find-cell-list-capture-aligned.cdb`) aligns cell+position, and the production gate `ProductionPath_IndoorCrossings_MatchRetail` reads **9/9 with NO code change**. Both retail and acdream pick with **center-only `point_in_cell` on `global_sphere[0]`** (the foot sphere does not lead the foot; `cache_global_sphere` @ pc:274196); `curr_cell` commits via `CTransition::validate_transition` (@ pc:272608, `curr_cell = check_cell`) where `check_cell` is the `find_cell_list` pick — **structurally identical to acdream's `RunCheckOtherCellsAndAdvance` → `FindCellSet` → `SetCheckPos`**. There is nothing to port; porting a swept advance would make membership LEAD the foot by a frame (a bug to satisfy a mis-aligned golden). Corrected finding + current state: `memory/project_retail_membership_criterion.md` and `tests/AcDream.Core.Tests/Conformance/Fixtures/README.md`. The two decomp questions below ARE answered (Q1: no XY lead; Q2: commit is the `find_cell_list` pick via `validate_transition`) — the text below is retained as the investigation trail. **Still open** (separate from indoor membership, which is DONE): outdoor→indoor (`0031↔0170`) building-entry conformance (the building-only gate cache can't promote an outdoor seed — needs the landcell + building stab loaded); the master-plan cleanups (delete `CheckBuildingTransit`, unify `find_env_collisions`, demote `ResolveCellId`) refactor WORKING retail-faithful code → need explicit user approval. --- ## 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`](../superpowers/specs/2026-06-03-verbatim-spatial-pipeline-port-master-plan.md)). - **Phase:** **P0 conformance apparatus = COMPLETE & gate met.** **P1 = membership, IN PROGRESS** — the decisive evidence is in and the RED gate is built; the remaining work is the FIX. - **Next concrete step:** port how retail advances `curr_cell` ACROSS the doorway portal **mid-sweep**, and turn the RED production-path gate GREEN. (Details in "The P1 fix" below.) ## TL;DR (what we learned, in one paragraph) At a building doorway, acdream's cell membership LAGS retail. A live cdb capture of retail (`CPhysicsObj::change_cell` at the Holtburg `0031↔0170↔0171` doorway) gave a clean 22-transition golden. Replaying its indoor `0170↔0171` segments through acdream's **real** `ResolveWithTransition` yields **0/11 match**: the swept move COMPLETES (`restPos == target`) but `CellId` never leaves the SOURCE cell — acdream carries the body across the doorway yet never advances `curr_cell`. **Both** retail and acdream PICK the cell with center-only `point_in_cell`; the gap is that **retail advances `curr_cell` to the neighbour DURING the sweep** (the swept sphere crossing the portal / a sphere point that leads the foot), while acdream only re-picks at the resting end position where the sphere center is still in the source cell. P1 = port that swept advance. ## What shipped this session (commits, base `a859116`) | SHA | What | |---|---| | `a90f343` | Dat-backed conformance loader (`ConformanceDats`) + characterized the cottage-doorway topology (`0031↔0170↔0171` verified real from dats; vestibule `0170` w/ exit portal, room `0171`, outdoor landcell `0031` by grid math) | | `ec78beb` | `find_cell_list` unambiguous goldens (interior picks + stale-seed re-pick stability) | | `b35e491` | Retail-trace parser (`RetailTrace`) + cdb value-capture tooling | | `1662da8` | Threshold-trace golden wiring + PVS scaffold + P1-entry checklist | | `bb4dead` | **Retail-trace golden CAPTURED** (live cdb) + divergence pinned (P0 gate met) | | `81ea3aa` | P1 design nuances (acdream already has `FindTransitCellsSphere`; test the production path) | | `46a86d2` | CORRECTION — retail pick is center-only `point_in_cell`; bare-`FindCellList` divergence might be a probe artifact | | `0442ead` | **Production-path conformance — divergence CONFIRMED (0/11), NOT a probe artifact** | Plan + notes: [`docs/superpowers/plans/2026-06-03-p0-conformance-apparatus.md`](../superpowers/plans/2026-06-03-p0-conformance-apparatus.md), [`docs/research/2026-06-03-p0-conformance-apparatus-notes.md`](2026-06-03-p0-conformance-apparatus-notes.md) (the notes doc has the full finding + the `## ✅ RESOLVED` section + the per-transition containment table). ## ⛔ DO-NOT-RETRY (hypotheses falsified this session — don't waste a cycle) 1. **"The fix is a radius-aware pick (`SphereIntersectsCellBsp` instead of `PointInsideCellBsp` in the `find_cell_list` pick)."** FALSIFIED. `CObjCell::find_cell_list` pick @ pc:308810 calls `vtable[0x84]` = `CEnvCell::point_in_cell` (`@ 0x52c300 pc:309677`) → `CCellStruct::point_in_cell` (`@ 0x5338f0 pc:317657`) → **`BSPTREE::point_inside_cell_bsp`** — CENTER-ONLY. acdream's pick criterion already matches retail. The radius-aware `sphere_intersects_cell_bsp` (`@ 0x533900 pc:317666`) is a SEPARATE method, used by `find_transit_cells`' set-build only. 2. **"Retail uses portal-crossing, acdream uses point-in-cell — different PICK criteria."** Imprecise → effectively wrong. Both PICK with center-only `point_in_cell`. The difference is WHEN `curr_cell` advances (mid-sweep vs at-rest), not the pick test. 3. **"The 0/22 bare-`FindCellList` divergence is a probe artifact."** FALSIFIED by the production-path test (0/11 through the real swept engine). The divergence is real in production. 4. (Historical, already known dead — do not re-litigate) `CheckBuildingTransit` stickiness / #90 workarounds / the R1-flap ordered-CELLARRAY are already on this branch; they are NOT the doorway lag. See the master-plan §1 DELETE list. ## The apparatus (what exists + how to use it) All under `tests/AcDream.Core.Tests/Conformance/` unless noted. Conformance suite is GREEN (`60 pass / 1 skip / 0 fail`). - **`ConformanceDats.cs`** — `ResolveDatDir()` (skip if dats absent), `LoadEnvCell(dats, cache, id)` → loads a real EnvCell from the dats with its **real** ContainmentBsp + caches a `CellPhysics` (real collision BSP). `FixturesDir`, `HoltburgLandblock = 0xA9B40000`. - **`CottageDoorwayCharacterizationTests.cs`** — maps the Holtburg `0140..017F` indoor neighbourhood; pins the threshold building (origin `161.93,7.50,94.00`): `Vestibule0170`, `Room0171`, `OutdoorLandcell0031`; verified interior points (`Interior0170Local`, `Interior0171Local`). - **`RetailTrace.cs`** — parses `[fcl] seed=0x.. px=.. py=.. pz=.. picked=0x..` → `RetailCellPick`. - **Golden:** `Conformance/Fixtures/find-cell-list-threshold.log` — 22 retail `change_cell` transitions, decoded to decimals. The retail TRUTH. - **`FindCellListConformanceTests.cs`** — unambiguous interior picks (GREEN) + `FindCellList_DoorwayThreshold_DivergesFromRetail_PendingP1` (documents-the-bug, UNIT-level pin). - **`ThresholdDivergenceDiagnosticTests.cs`** — prints per-transition `point_in_cell` containment (the `in_seed/in_0170/in_0171` table). Always passes; invaluable for diagnosis. - **`ThresholdPortalCrossingReplayTests.cs`** — ★ the **production-path** gate: `ProductionPath_IndoorCrossings_DivergeFromRetail_PendingP1`. Replays the golden indoor segments through the REAL `ResolveWithTransition`. Currently `0/11`; **the fix must make it `11/11`.** - **`PvsConformanceTests.cs`** — render-PVS golden scaffold (skipped; filled in P4). - **cdb capture tooling** (`tools/cdb/`): `find-cell-list-capture.cdb` (breakpoints `CPhysicsObj::change_cell`, symbol-driven `@@c++`/offset reads; offsets verified live via `discover-types.cdb`), `decode_fcl_capture.py` (hex floats → decimals), READMEs. **PDB MATCH confirmed** (`refs/acclient.pdb` ↔ `C:\Turbine\Asheron's Call\acclient.exe`). The retail in-world client was PID 31020 this session (1164 MB = the game; the small one is the launcher). **Run the gate:** ``` dotnet test tests/AcDream.Core.Tests/AcDream.Core.Tests.csproj -c Debug --filter "FullyQualifiedName~Conformance" ``` **See the production-path detail (per-segment MATCH/DIVERGE):** ``` dotnet test tests/AcDream.Core.Tests/AcDream.Core.Tests.csproj -c Debug --filter "FullyQualifiedName~ProductionPath_IndoorCrossings" -l "console;verbosity=detailed" ``` **Capture more retail goldens** (user must launch retail + walk a doorway): run `tools/cdb/find-cell-list-capture.cdb` per its README; `decode_fcl_capture.py` → drop at the fixture path. ## The P1 fix — port retail's swept `curr_cell` advance **Symptom (from `ProductionPath_IndoorCrossings`):** `restPos == target` (sweep completes) but `result.CellId` = source cell, every indoor segment. So `curr_cell` never advances across the portal. **Two decomp questions to settle FIRST (grep-named → read → pseudocode, do not guess):** 1. **Does retail's membership point LEAD the foot?** What is `global_sphere[0]`'s local origin relative to `m_position.frame.m_fOrigin` (the captured foot point)? If the sphere center is offset toward motion / into the room, retail's center-only `point_in_cell` lands in the neighbour while the foot is behind. Read how retail builds `global_sphere` (`SPHEREPATH`/`CSphere` setup) and compare to acdream's `sp.GlobalSphere[0].Origin`. Anchor: `SPHEREPATH` (acclient.h:32625), `CPhysicsObj::SetPositionInternal @ 0x515330 pc:283399`. 2. **Does `curr_cell` advance via the swept CROSSING before/independent of the `find_cell_list` pick?** Read `CTransition::transitional_insert @ 0x50aa70 pc:272547` + `CTransition::validate_transition` + `CObjCell::find_cell_list @ 0x52b4e0 pc:308742` (set-build by `find_transit_cells`, then the pick). Determine whether `curr_cell` moves to a neighbour the swept sphere CROSSED (set-build / `find_transit_cells` @ `0x52c820 pc:309968`) even if the end-position center isn't yet inside it. `CPhysicsObj::change_cell @ 0x513390 pc:281192` is the commit-on-diff. **acdream code map (architect-reported line numbers — VERIFY on pickup, the branch evolves):** - `PhysicsEngine.ResolveWithTransition` — `src/AcDream.Core/Physics/PhysicsEngine.cs:~621`; returns `sp.CurCellId` as `result.CellId` at `~894`. - `Transition.FindEnvCollisions` — `src/AcDream.Core/Physics/TransitionTypes.cs:~1939`; indoor branch `~1974`; calls `RunCheckOtherCellsAndAdvance` at `~2082` (indoor) / `~2131` (outdoor). - `RunCheckOtherCellsAndAdvance` — `TransitionTypes.cs:~2143`; `CellTransit.FindCellSet(...)` at `~2149`; `sp.SetCheckPos(sp.CheckPos, containingCellId)` at `~2160` (where `CheckCellId` gets the answer). - `ValidateTransition` — `TransitionTypes.cs:~3426`; **`sp.CurCellId = sp.CheckCellId` at `~3436`** (the commit site). - `CellTransit.BuildCellSetAndPickContaining` — `src/AcDream.Core/Physics/CellTransit.cs:~426`; the center-only `point_in_cell` pick at `~520` (this is CORRECT — do not "fix" it to radius-aware). - `CellTransit.FindTransitCellsSphere` — `CellTransit.cs:~74` (the partial `find_transit_cells` port: exit→exitOutside, loaded-neighbour→`SphereIntersectsCellBsp`, unloaded→plane-distance). **Most likely fix shape (HYPOTHESIS, verify against the decomp — do NOT code before the two questions above are answered):** the membership answer should be the cell the swept sphere CROSSED into (`find_transit_cells` candidate the sweep entered), not the cell whose BSP contains the resting end-position center. Likely a change in how `RunCheckOtherCellsAndAdvance` / the swept advance picks from the candidate set, OR feeding the correct (leading/swept) sphere point. Could also be that acdream's `global_sphere` point differs from retail's. **The evidence-first move: instrument acdream's `ResolveWithTransition` to print `sp.GlobalSphere[0].Origin` + the candidate set + the pick at each sub-step for one failing segment, and compare to retail (capture `find_cell_list`'s `arg3->center` + `*arg5` via a new cdb breakpoint). Then port. Iterate against the `ProductionPath_IndoorCrossings` gate (sub-second loop).** **Then (rest of P1, after the gate is GREEN):** the outdoor-involving segments (`0031↔0170`) need the landcell + building portal loaded into the test cache (the `ProductionPath` test currently skips them — extend it); delete `CheckBuildingTransit` (master-plan #5); unify `find_env_collisions`; demote `ResolveCellId` to spawn/teleport-seed. Update/rewrite the documents-the-bug tests to assert the full sequence (they FAIL when the fix lands — that's the signal). Master-plan §4: if a faithful port breaks an acdream test, the test encoded a hybrid assumption — fix the test to retail truth, don't bend the port. **P1 gate (user visual):** stand in the doorway → no ping-pong, `[cell-transit]` DELTA=0 standing still; walk in/out → clean monotonic cell sequence. ## Test baseline (going into P1 fix) Core: **1308 pass / 5 fail / 1 skip** — the 5 are the documented baseline (2 `BSPStepUp` + 3 door-collision = P2's target). Conformance subset: **60 pass / 1 skip / 0 fail**. App: 177 green (untouched this session). The 2 NEW documents-the-bug tests (`FindCellList_DoorwayThreshold_DivergesFromRetail_PendingP1`, `ProductionPath_IndoorCrossings_DivergeFromRetail_PendingP1`) are GREEN-while-broken; they FLIP when P1 lands. ## Memory updated `memory/project_retail_membership_criterion.md` (the finding + the swept-advance target + the falsified hypotheses) and the `MEMORY.md` index hook are current. Read them on pickup. --- ## PICKUP PROMPT (copy-paste for the next session) ``` Continue the VERBATIM retail spatial-pipeline port. Branch claude/thirsty-goldberg-51bb9b (no branch/worktree; no push without asking; never git stash/gc). PowerShell on Windows; launch logs UTF-16. State: M1.5. P0 conformance apparatus COMPLETE + gate met. P1 (membership) IN PROGRESS — the decisive evidence is in and the RED gate is built; remaining work is the FIX. READ FIRST: docs/research/2026-06-03-p1-membership-swept-advance-handoff.md (canonical), then docs/research/2026-06-03-p0-conformance-apparatus-notes.md (the ✅ RESOLVED section + the containment table) and memory/project_retail_membership_criterion.md. THE FINDING (confirmed in production): acdream's swept ResolveWithTransition completes the doorway move (restPos==target) but never advances curr_cell across the portal — 0/11 vs the retail change_cell golden. Both retail and acdream PICK with center-only point_in_cell (radius-aware-pick + "different pick criterion" + "probe artifact" hypotheses are ALL FALSIFIED — see the DO-NOT-RETRY list). The gap is that retail advances curr_cell to the neighbour DURING the sweep (swept crossing / a sphere point that leads the foot), acdream only re-picks at the resting end position. THE JOB (P1 fix, evidence-first per the #98 lesson — do NOT speculate): 1. Answer the two decomp questions in the handoff: (a) does retail's global_sphere[0] point lead the foot vs acdream's sp.GlobalSphere[0].Origin? (b) does curr_cell advance via find_transit_cells' swept crossing in transitional_insert/validate_transition before the find_cell_list pick? Anchors: transitional_insert @0x50aa70 pc:272547, validate_transition, find_cell_list @0x52b4e0 pc:308742, find_transit_cells @0x52c820 pc:309968, SetPositionInternal @0x515330 pc:283399, change_cell @0x513390. 2. Instrument acdream's swept path (print sp.GlobalSphere[0].Origin + candidate set + pick per sub-step for ONE failing 0170->0171 segment) and, if needed, capture retail's find_cell_list arg3->center + *arg5 via a new cdb breakpoint (PDB MATCHES; tooling in tools/cdb/). Compare. THEN port verbatim. 3. Iterate against the RED gate ThresholdPortalCrossingReplayTests.ProductionPath_IndoorCrossings_ DivergeFromRetail_PendingP1 (sub-second loop) until 11/11. Then extend it to the outdoor 0031 segments (load landcell + building portal), delete CheckBuildingTransit, unify find_env_collisions, demote ResolveCellId to seed-only, and rewrite the documents-the-bug tests to assert the full sequence. 4. USER VISUAL GATE: doorway, no ping-pong, clean in/out cell sequence. Test baseline: Core 1308 pass / 5 fail (2 BSPStepUp + 3 door-collision = P2) / 1 skip; Conformance 60 pass / 1 skip. Do NOT code a membership fix before the two decomp questions are answered with evidence. ```