acdream/docs/research/2026-06-03-p0-conformance-apparatus-notes.md
Erik 0442eadcec test(p1): production-path membership conformance — divergence CONFIRMED (0/11), not a probe artifact
Replays the golden indoor 0170<->0171 segments through the real
PhysicsEngine.ResolveWithTransition (engine builds the global sphere + sweeps;
cells loaded from dats with real BSP). Result: 0/11 match retail. Every segment
restPos==target (the sweep completes the move) but CellId stays on the SOURCE
cell — acdream moves the body across the doorway yet NEVER advances curr_cell.
So the 'probe artifact' hypothesis is FALSIFIED: production membership genuinely
lags retail.

Refined mechanism: both retail and acdream PICK with center-only point_in_cell
(architect's radius-aware-pick hypothesis falsified, confirmed by reading
CEnvCell::point_in_cell -> BSPTREE::point_inside_cell_bsp). The gap is retail's
curr_cell ADVANCES across the portal mid-sweep (swept crossing / leading sphere
point) while acdream's swept advance keeps the source cell. P1 ports that advance.

ProductionPath_IndoorCrossings_DivergeFromRetail_PendingP1 is the RED gate the P1
fix must turn GREEN. Conformance 60 pass / 1 skip / 0 fail.

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

15 KiB

P0 — Conformance apparatus notes (characterized topology + goldens)

2026-06-03. Companion to docs/superpowers/plans/2026-06-03-p0-conformance-apparatus.md. Source of truth for the cell ids + golden inputs the P0 conformance tests pin. All values below were characterized from the real client dats (not guessed) by CottageDoorwayCharacterizationTests against %USERPROFILE%\Documents\Asheron's Call.

Characterized Holtburg (landblock 0xA9B4) indoor neighborhood

Scanned 0xA9B40140..0xA9B4017F. All cells load with a real ContainmentBsp and seenOutside=1. Cells share a per-BUILDING world origin (the building's reference frame). The distinct buildings:

Cell range World origin Stab Identity
0140..0150 (17 cells) (130.50, 11.50, 94.00) 17 Cottage-with-cellar (#98 cellar saga; exit portals at 0145,014C,014E,014F,0150)
0151..0169 (25 cells) (107.50, 36.00, 94.00) 24 Larger building (inn)
016A..016E (5 cells) (79.50, 37.50, 94.00) 4 Small building
016F..0175 (7 cells) (161.93, 7.50, 94.00) 6 THE doorway-threshold building (master-plan 0170/0171)
0176..0178 (65.21, 156.63, 66.00) 2
0179..017A (158.18, 37.71, 94.00) 1
017B..017F (161.72, 105.05, 66.00) 4

The pinned threshold topology (master-plan 0031↔0170↔0171)

Building at world origin (161.93, 7.50, 94.00):

Cell Role Portal dests seenOutside BSP
0xA9B40170 vestibule / doorway [0xFFFF (exit→outdoor), 0x0171] 1 real
0xA9B40171 room (behind the door) [0x0170, 0x0173, 0x0175] 1 real

Grid math confirms the outdoor side: origin (161.93, 7.50) → gridX = ⌊161.93/24⌋ = 6, gridY = ⌊7.5/24⌋ = 0 → landcell id 6*8 + 0 + 1 = 49 = 0x31outdoor landcell 0xA9B40031. So the player crossing the doorway traverses 0031 (outdoor) → 0170 (vestibule, via the 0xFFFF exit portal) → 0171 (room) — exactly the master-plan ping-pong 0031↔0170↔0171. Verified real.

Naming note: a 2026-05-21 capture dir called 0170/0171 the "inn 2nd floor"; that label was loose. By geometry it is the 7-cell building at (161.93, 7.50). Identity (cottage vs inn) is irrelevant to the conformance — what matters is 0170 carries the exit portal (the doorway) and 0171 is the room behind it.

Golden interior points (verified point_in_cell == true)

Cell-LOCAL points whose EnvCell.PointInCell(world) returns true (the world form is Vector3.Transform(local, cellPhysics.WorldTransform)):

Cell Interior LOCAL point Interior WORLD point Notes
0xA9B40170 (5.865, -8.449, 0.417) (156.06, 15.95, 94.42) 115/125 grid-inset points inside (small/irregular vestibule)
0xA9B40171 (6.55, -3.25, 4.60) (157.01, 13.69, 95.53)* bounds-center = bsphere origin; 125/125 inside (clean box)

* world recomputed by the test at runtime via the cell's WorldTransform; the value above is the firstInside probe — the canonical bsphere-origin local (6.55,-3.25,4.60) is also fully interior.

These are retail-faithful by construction: the ContainmentBsp is loaded from the same dats retail loads, so a geometrically-correct containment answer is the retail answer.

Golden provenance summary

Golden Provenance Status
point_in_cell (interior true, far-away false) geometric (real dat BSP) autonomous (Task 3)
find_cell_list deep-inside picks 0171/0170 geometric (real dat cells) autonomous (Task 4)
find_cell_list doorway-threshold pick retail cdb trace Task 6 — USER GATE / mine existing traces
PVS visible-set retail cdb cell_draw_list trace deferred to P4 (Task 7 scaffold)

Existing retail traces — mined, NOT usable for membership

docs/research/2026-05-21-a6-captures/*/retail.log + retail.decoded.log were grepped for change_cell / curr_cell / find_cell_list / cell=0x / insert_into_cell. Zero matches in any retail log — all 15,492 membership/cell-id matches are in the paired acdream.log files (our own probe output). The committed retail traces are collision-only (find_collisions hit-counters, set_neg_poly_hit, etc.); they carry no membership cell-id or position. So the retail-trace-backed threshold golden cannot be built from existing data — a live capture is required (tools/cdb/find-cell-list-capture.cdb).

Retail capture — DONE (2026-06-03). The P0 gate is MET.

Captured live from retail (cdb on CPhysicsObj::change_cell, symbol-driven, PDB MATCH) at the "Agent of Arcanum" house — which by geometry IS the 0170/0171 building. 22 transitions, a perfectly clean monotonic 0031↔0170↔0171 sequence, NO ping-pong (retail's membership is correct-by-construction, as the master plan asserts). Golden fixture committed at tests/AcDream.Core.Tests/Conformance/Fixtures/find-cell-list-threshold.log. Capture+decode tooling: tools/cdb/find-cell-list-capture.cdb + tools/cdb/decode_fcl_capture.py.

★ ROOT-CAUSE FINDING (the central P1 work) ★

The per-transition containment diagnostic (ThresholdDivergenceDiagnosticTests) shows all 22 transitions diverge, for ONE reason:

retail transition retail picks acdream FindCellList foot in_seed in_0170 in_0171
0170→0171 (enter room) 0171 0170 1 1 0
0171→0170 (leave room) 0170 0171 1 0 1
0170→0031 (exit bldg) 0031 0170 1 1 0
0031→0170 (enter bldg) 0170 0031 0 0/1 0

Retail transitions membership at the PORTAL CROSSING (CEnvCell::find_transit_cells — the sphere crosses the doorway polygon plane). acdream's FindCellList re-picks by POINT-IN-CELL containment at the foot. So retail commits the neighbour cell BEFORE the foot point is geometrically inside it (it enters room 0171 while the foot is still inside vestibule 0170's BSP, in_0171=0), and acdream lags. This is the master-plan §0 "hysteresis gap" diagnosis confirmed against live retail — and it is NOT a per-cell hysteresis or a building-entry-only split; it is a single criterion mismatch (directed portal crossing vs point-in-cell) that affects EVERY threshold transition.

P1's central job: port CEnvCell::find_transit_cells (@ 0x52c820 pc:309968) directed portal-crossing so membership transitions at the doorway plane, not at the BSP-containment boundary. (Plus intrinsic building entry A3 + uniform find_env_collisions B1.) The documents-the-bug test FindCellList_DoorwayThreshold_DivergesFromRetail_PendingP1 PASSES while this diverges and FAILS when P1 lands → rewrite it to assert the full sequence then.

P1 design nuances (from reading find_transit_cells @ pc:309968 + the acdream membership map):

  1. find_transit_cells (sphere variant) STRUCTURE: per portal — (a) exit portal (other_cell_id == 0xffffffff): if a sphere crosses the exit-portal polygon plane toward outside → set exitOutside; (b) interior/building portal with a LOADED neighbour: if a sphere CCellStruct::sphere_intersects_cell(neighbour) != OUTSIDEadd_cell(neighbour); (c) interior/building portal with an UNLOADED neighbour: if a sphere crosses the portal POLYGON plane (dist vs ±(radius+0.0002), honoring exact_match) → add_cell(other_cell_id). After all portals, if (exitOutside) add_all_outside_cells. The transition's curr_cell then advances to the crossed/overlapped neighbour — BEFORE foot-containment.
  2. acdream already has CellTransit.FindTransitCellsSphere (a partial port of exactly this: exit→exitOutside, loaded-neighbour→SphereIntersectsCellBsp, unloaded→plane-distance). So P1 is NOT writing find_transit_cells from scratch — it is making curr_cell ADVANCEMENT use the portal-crossing result (the candidate the sphere crossed into) as the membership answer, instead of FindCellSet/FindCellList's point-in-cell interior-wins pick. Audit RunCheckOtherCellsAndAdvance
    • SetCheckPos (the swept-cell advance) — that is where the pick criterion gets chosen.
  3. Test against the PRODUCTION path, not bare FindCellList. The P0 documents-the-bug test calls FindCellList directly (unit-level). P1's conformance must replay the golden POSITIONS through PhysicsEngine.ResolveWithTransition (a trajectory, like CellarUpTrajectoryReplay, seeded incl. the outdoor landcell 0031) and assert the swept CellId sequence matches the captured retail change_cell sequence. Bare-FindCellList is the unit pin; the trajectory is the integration gate. (Needs the outdoor landcell + building portal loaded — more than the building-only cache P0 uses.)

⚠ CORRECTION (same session, after reading point_in_cell + an architect pass)

The "all 22 diverge → retail uses portal-crossing, acdream uses point-in-cell" framing above is the probe-level result and is likely a test artifact. Two decomp reads correct it:

  1. Retail's find_cell_list PICK is center-only point_in_cell, NOT radius-aware. CObjCell::find_cell_list pick @ pc:308810 calls vtable[0x84] = CEnvCell::point_in_cell (@ 0x52c300 pc:309677) → globaltolocalCCellStruct::point_in_cell (@ 0x5338f0 pc:317657) → BSPTREE::point_inside_cell_bsp(cell_bsp, point) — a center-only point test. The radius-aware sphere_intersects_cell_bsp is a SEPARATE method (CCellStruct::sphere_intersects_cell @ 0x533900 pc:317666), used by find_transit_cells' SET-BUILD, not the pick. So acdream's pick criterion (PointInsideCellBsp) ALREADY matches retail. An architect hypothesis that the fix is "use SphereIntersectsCellBsp in the pick" is FALSIFIED — that would DIVERGE from retail.

  2. The pick point retail tests is global_sphere[0].center (the swept sphere center), not the foot origin. find_cell_list(&check_pos, num_sphere, global_sphere, …) (pc:309088); the pick tests arg3->center = global_sphere[0].center. The P0 FindCellList_DoorwayThreshold_* tests + the diagnostic fed pick.Position = the captured m_position.frame.m_fOrigin (the FOOT origin at the resting position), NOT the sphere center, and NOT through the sweep. The diagnostic's own data shows the tell: retail always commits the cell AHEAD of motion while the foot is still behind (0170→0171 with in_0171=0; 0171→0170 with in_0170=0) — the signature of the swept sphere center crossing the portal, which a static foot-point probe cannot reproduce.

What this means for P1. The membership criterion may NOT be broken; acdream's production ResolveWithTransition already feeds sp.GlobalSphere (the sphere center) through the swept transition. The DECISIVE, evidence-first first step is the production-path trajectory conformance (replay the golden positions through ResolveWithTransition, assert the swept CellId sequence == retail's change_cell sequence). Outcomes: (a) production MATCHES → the bare-FindCellList divergence was a probe artifact, P1 shrinks to building-entry cleanup (big de-risk); (b) diverges, fixed by the right point/sweep → small + localized; (c) still diverges → a real bug, design from THAT evidence. Do NOT design or code a P1 membership "fix" before the production-path test exists and its RED/GREEN is read. The P0 ..._DivergesFromRetail_PendingP1 test is a UNIT-level pin only, NOT evidence of a production divergence.

RESOLVED — the production path DIVERGES (the "probe artifact" hypothesis is FALSIFIED)

Built ThresholdPortalCrossingReplayTests.ProductionPath_IndoorCrossings_DivergeFromRetail_PendingP1 (replays the golden indoor 0170↔0171 segments through the REAL ResolveWithTransition — engine builds the global sphere + sweeps; cells loaded from dats with real BSP). Result: 0/11 match retail. Every segment: restPos == target (the sweep COMPLETES the move cleanly) but CellId stays on the SOURCE cell — acdream moves the body across the doorway yet never advances curr_cell. So production membership genuinely lags; the P0 finding is REAL, not a probe artifact.

Refined mechanism (supersedes the "portal-crossing vs point-in-cell criterion" framing). Both retail and acdream PICK with center-only point_in_cell. The divergence is that retail's curr_cell ADVANCES to the neighbour during the sweep (the swept sphere crossing the doorway polygon, and/or a sphere point that leads the foot into the room), so by the time the foot rests at the captured position the membership has already advanced. acdream's swept advance does NOT promote the neighbour — at the end-position its tested sphere center is still inside the source cell's BSP, so the pick keeps the source cell. P1's job: port how retail advances curr_cell across the portal mid-sweep. The open decomp questions for P1: (1) how global_sphere[0] local origin relates to m_position (does retail's sphere point lead the foot?); (2) whether curr_cell advances via find_transit_cells' swept crossing in transitional_insert/validate_transition BEFORE the find_cell_list pick, vs the pick alone. Anchors: CTransition::transitional_insert @ 0x50aa70 pc:272547, CTransition::validate_transition, CPhysicsObj::SetPositionInternal @ 0x515330 pc:283399. The RED production-path test is the gate the P1 fix must turn GREEN.

P0 status / P1-entry checklist — COMPLETE

Apparatus: COMPLETE + GREEN. (Conformance suite 59 pass / 1 skip / 0 fail.)

  • Dat-backed fixture loader (ConformanceDats).
  • Characterized + pinned cottage-doorway topology (0031↔0170↔0171 verified real).
  • point_in_cell goldens vs real dat BSP.
  • find_cell_list unambiguous goldens (interior picks + stale-seed re-pick stability).
  • Retail-trace parser + cdb capture script + decoder + README.
  • PVS-golden scaffold (skipped; filled in P4).
  • Retail-trace golden captured + the threshold divergence pinned (documents-the-bug, GREEN).
  • P0 GATE MET: ≥1 retail-trace-backed assertion exists. P1 may begin.

Re-scope note for P1 (discovered during P0): this branch already carries the membership "Stage 1" work the master plan's §2 "acdream now" column lists as partial — CellArray (ordered CELLARRAY / R1 flap fix), FindCellSet's interior-wins pick (cites pc:308788-308825), RunCheckOtherCellsAndAdvance (collide-then-pick), swept sp.CurCellId return, player-only UpdatePlayerCurrCell. Per the CORRECTION above, the pick criterion is NOT confirmed wrong — retail's pick is center-only point_in_cell, same as acdream's. P1's TRUE first step is the production-path conformance to find where (if anywhere) production membership actually diverges from the retail change_cell golden, THEN the (likely small) fix, THEN delete CheckBuildingTransit + unify find_env_collisions + demote ResolveCellId to seed-only. Re-confirm against the code + the production-path evidence when P1 starts — do not port a "portal-crossing pick" on the probe artifact.