acdream/docs/research/2026-06-03-p1-membership-swept-advance-handoff.md
Erik 9017107960 fix(p1): membership already matches retail — the 0/11 was a cdb capture artifact
The P1 "doorway membership lags retail" premise is FALSIFIED. acdream's swept
ResolveWithTransition already matches retail's true per-frame curr_cell: the
production gate ProductionPath_IndoorCrossings reads 9/9 on the indoor 0170<->0171
crossings with NO code change, once fed an aligned retail golden.

Root cause of the false 0/11: CPhysicsObj::SetPositionInternal calls change_cell
(acclient_2013_pseudo_c.txt:283456) BEFORE set_frame writes m_position (:283458),
so the original golden (find-cell-list-capture.cdb, read at the change_cell BP)
paired each frame's NEW cell with the PREVIOUS frame's position — a one-frame skew.
Verified 3 ways: the decomp ordering; golden_picked[i] == geom(golden_position[i+1])
for all 22 rows; acdream's static pick == golden_picked[i-1] for all rows. Both
retail and acdream pick with center-only point_in_cell on global_sphere[0] (no XY
lead; cache_global_sphere @ pc:274196). curr_cell commits via validate_transition
(@ pc:272608, curr_cell = check_cell) = the find_cell_list pick, structurally
identical to acdream's RunCheckOtherCellsAndAdvance -> FindCellSet -> SetCheckPos.
There was nothing to port; a swept advance would make membership LEAD by a frame.

- tools/cdb/find-cell-list-capture-aligned.cdb: re-capture reads the committed
  position from the set_frame that follows change_cell (cell+position same instant).
- Fixtures/find-cell-list-threshold.log: replaced with the aligned capture.
- ThresholdPortalCrossingReplayTests / FindCellListConformanceTests: rewritten from
  documents-the-bug to assert retail truth (per-segment / per-indoor-pick equality).
- handoff + notes + README + memory: banners correcting the disproven premise.

Still open (NOT indoor membership, which is DONE): outdoor->indoor 0031<->0170 entry
conformance (needs landcell + building stab in the gate cache); master-plan cleanups
(delete CheckBuildingTransit, unify find_env_collisions, demote ResolveCellId) refactor
working retail-faithful code -> need explicit user approval.

Conformance 60 pass / 1 skip / 0 fail; full Core 1309 pass / 5 fail (pre-existing
2 BSPStepUp + 3 door-collision = P2) / 1 skip.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 18:54:27 +02:00

17 KiB

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 RunCheckOtherCellsAndAdvanceFindCellSetSetCheckPos. 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).
  • 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, docs/research/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.csResolveDatDir() (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.pdbC:\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.ResolveWithTransitionsrc/AcDream.Core/Physics/PhysicsEngine.cs:~621; returns sp.CurCellId as result.CellId at ~894.
  • Transition.FindEnvCollisionssrc/AcDream.Core/Physics/TransitionTypes.cs:~1939; indoor branch ~1974; calls RunCheckOtherCellsAndAdvance at ~2082 (indoor) / ~2131 (outdoor).
  • RunCheckOtherCellsAndAdvanceTransitionTypes.cs:~2143; CellTransit.FindCellSet(...) at ~2149; sp.SetCheckPos(sp.CheckPos, containingCellId) at ~2160 (where CheckCellId gets the answer).
  • ValidateTransitionTransitionTypes.cs:~3426; sp.CurCellId = sp.CheckCellId at ~3436 (the commit site).
  • CellTransit.BuildCellSetAndPickContainingsrc/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.FindTransitCellsSphereCellTransit.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.