From db94cb1c909e6ad842cafe7ba173176161d2d9a8 Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 3 Jun 2026 16:00:49 +0200 Subject: [PATCH] =?UTF-8?q?docs(p1):=20canonical=20pickup=20handoff=20?= =?UTF-8?q?=E2=80=94=20swept=20curr=5Fcell=20advance=20is=20the=20fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- ...-03-p1-membership-swept-advance-handoff.md | 209 ++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 docs/research/2026-06-03-p1-membership-swept-advance-handoff.md diff --git a/docs/research/2026-06-03-p1-membership-swept-advance-handoff.md b/docs/research/2026-06-03-p1-membership-swept-advance-handoff.md new file mode 100644 index 0000000..17ef0ed --- /dev/null +++ b/docs/research/2026-06-03-p1-membership-swept-advance-handoff.md @@ -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. +```