docs(p1): canonical pickup handoff — swept curr_cell advance is the fix

Single pickup document for the next session: state both altitudes, the 8
session commits, the confirmed finding (production membership diverges 0/11 —
swept move completes but curr_cell never advances across the portal), the
DO-NOT-RETRY list (3 falsified hypotheses), the apparatus inventory + run
commands, the P1 fix scope (two decomp questions + the acdream code map + the
RED gate), the test baseline, and a copy-paste pickup prompt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-03 16:00:49 +02:00
parent 0442eadcec
commit db94cb1c90

View file

@ -0,0 +1,209 @@
# 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.
## 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.
```