acdream/docs/research/2026-05-21-a6-cdb-capture-findings.md
Erik f04ea90050 ship(phys): A6.P3 slice 1 — Indoor ContactPlane retention COMPLETE
Slice 1 ships the strip-synthesis + Mechanism B (LKCP restore) fix
addressing A6.P2 Finding 2. Includes:

  - scen3_inn_2nd_floor_postfix paired capture (retail.log + decoded
    + acdream.log) as verification artifact. acdream cp-write count
    dropped from 86,748 to 25,082; per-unit-of-activity rate dropped
    63x (164.61 -> 2.60 cp-writes per cell-cache event).

  - Findings doc (docs/research/2026-05-21-a6-cdb-capture-findings.md)
    appended with slice 1 SHIPPED section: commit map, scen2/scen3
    pre/post comparison tables, user happy-test results, status of
    each A6.P2 finding (Finding 1 CLOSED as side-effect, Finding 2
    PARTIALLY CLOSED with remaining 99.3% from L622 seed flagged
    as #96, Finding 3 + #95 still open), slice 2 recommendation.

  - Issue #96 filed: "Per-tick PhysicsEngine.ResolveWithTransition CP
    seed contributes 99.3% of post-slice-1 CP writes." Slice 2 target.
    Sketch options: remove entirely / gate by change-detection / no-op
    guard inside SetContactPlane.

  - Issue #97 filed: "Phantom collisions + occasional fall-through on
    indoor 2nd floor." User-reported during happy-testing. HYPOTHESIS:
    side-effect of #96; falsifiable by re-testing post-slice-2.

  - CLAUDE.md updated: Currently-working-toward block now points at
    A6.P3 slice 2 (#96) as the active phase. M1.5 building/cellar
    demo half is ACHIEVABLE NOW (slice 1 unblocked the physics).

  - Roadmap updated: A6.P3 broken into 3 slices, slice 1 marked
    SHIPPED with commit history.

KEY USER-VISIBLE OUTCOME: stairs + cellar descent now WORK in acdream
(user happy-test confirmed walking up/down inn stairs multiple times,
walking down to cellar). A6.P2 Finding 1 (dispatcher entry frequency
mismatch) confirmed as secondary effect of Finding 2 — closed without
explicit Finding 1 work, as A6.P2 hypothesized.

REMAINING CONCERNS for slice 2 + future:
  - L622 per-tick seed (#96) still firing 24,906 times in scen3 postfix
  - Phantom collisions + fall-through on 2nd floor (#97)
  - See-through-walls indoors (#95, separate scope)
  - Indoor lighting broken (A7 scope)

Test suite: 1148 pass + 8 pre-existing fail (baseline maintained;
T3 IndoorContactPlaneRetentionTests adds the +1).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 10:11:44 +02:00

28 KiB
Raw Blame History

A6.P2 cdb capture findings — 2026-05-21

Status: SHIPPED — 5 of 9 scenarios captured; scen6-9 cancelled (see "Capture inventory" below). Findings 1-4 ready for A6.P3 fix surfacing.

Spec: docs/superpowers/specs/2026-05-21-phase-a6-indoor-physics-fidelity-design.md.

PDB match verification: pdb-match-verification.txt.

Prior handoffs:

TL;DR — what the 5 captures prove

  1. Finding 2 (ContactPlane resynthesis blowup) is overwhelmingly confirmed. Across all 5 scenarios, acdream writes ContactPlane fields 250× to ∞× more often than retail. The infinite ratio is scen3 (flat 2nd-floor walk): retail's set_contact_plane fires zero times while acdream writes 86,748 field updates. This is the M1.5 root cause for "indoor walking feels broken." A6.P3 fix surface: stop resynthesizing CP every frame.
  2. Finding 1 (dispatcher entry frequency mismatch) extends to all scenarios but is shape-divergent. Retail calls BSPTREE::find_collisions 4× to 281× more often than acdream's BSPQuery.FindCollisions. Largest gap on scen5 (Town Network walk: retail 9,552 vs acdream 34 — 281× fewer in acdream). Suggests retail's transitional_insert calls dispatcher per-sub-step regardless of expected collisions; acdream's modern path is lazier.
  3. Finding 3 (cell-resolver indoor sling-out) directly captured on scen4. +Acdream walked a few meters inside a Holtburg cottage cellar and the resolver flung the character across a landblock boundary (0xA9B40148 → 0xA9B40029 → 0xA9B30030, all reason=resolver). Indoor BSP was barely queried (only 2 [indoor-bsp] hits during the sling-out); [check-bldg] fired 5,495 times trying to re-resolve which building +Acdream was in. This is a distinct failure family from the stair-attempt pattern (scen2) and the CP-write blowup.
  4. Finding 4 (portal-graph visibility blowup, scope-adjacent). Discovered incidentally during scen5: after portal teleport to Town Network hub, visibleCells per cell exploded from ~4 to 135-145 and cells from disconnected landblocks (0x0007, 0x020A, 0x0408) were cached. Filed separately as issue #95 — this is the underlying cause of user-observed "dungeons are broken (see through walls / other dungeons rendering)" across the project.

Capture inventory

# Tag Walk script Retail Acdream Status
1 scen1_inn_doorway Walk through inn door, stop just inside committed (prior session)
2 scen2_inn_stairs Walk up 4 steps; acdream re-captured as stair-FAILURE committed (this session)
3 scen3_inn_2nd_floor Forward 3m, sidestep 1m, walk back (teleport into acdream) committed (this session)
4 scen4_cottage_cellar Retail ascent + acdream teleport-in + sling-out committed (this session)
5 scen5_sewer_entry Town Network portal entry (Holtburg sewer doesn't exist) committed (this session)
6 scen6_sewer_first_stair cancelled — Holtburg Sewer doesn't exist on this server
7 scen7_sewer_inter_room cancelled — same
8 scen8_sewer_chamber cancelled — same
9 scen9_sewer_corridor cancelled — same; any substitute dungeon hits issue #95 (visibility blowup) on portal entry, making physics-only analysis impossible

Why scen6-9 cancelled: The A6.P1 design spec assumed the Holtburg Sewer existed and was accessed via portal. Neither is true on this ACE server. Any substitute dungeon (the path we'd normally take) hits the portal-graph visibility bug (issue #95) immediately on portal entry, making the dungeon visually unusable for navigation. A dedicated A6.P1-redux capturing post-#95-fix dungeon physics is a candidate for A8 or M1.5-residual scope; for A6.P2 the 5 captured scenarios provide sufficient evidence.

Analysis tables

Table 1 — Per-site push-back delta

DEFERRED. The current cdb probe (v4) captures BP5 (adjust_sphere_to_plane) at function entry only. Computing per-call delta ‖output_center input_center‖ requires a paired epilogue breakpoint that captures the corrected sphere center after the function returns. This was scoped out of A6.P1 to keep the cdb script simple; adding it is a future A6.P1.5 (estimated 1 hour: add bu exit breakpoints to v5 of a6-probe.cdb).

What we have instead: BP5 entry-count is a proxy for "how often does adjust_sphere fire." From the BP5 counts in Table 3 (column 1), acdream's BP5 call rate is divergent from retail's, but without paired entry/exit values we can't quantify the over-correction directly.

Bug-candidate threshold flagged in the spec (acdream > 3× retail on delta): unmeasurable from current data; defer to A6.P1.5 or accept Findings 1-3 as sufficient triggers for A6.P3 fixes.

Table 2 — Path-frequency diff

DEFERRED. Same reason as Table 1 — the cdb probe captures BSPTREE::find_collisions entry only, not which of the 7 exit paths (PLACEMENT_INSERT, check_walkable, step_down, collide_with_pt, set_collide+slid, step_sphere_up, find_walkable) was taken. Adding exit-discriminating breakpoints requires breakpoint after each return site in find_collisions — non-trivial cdb scripting work, deferred to A6.P1.5.

What we have instead: total dispatcher entries per scenario (Table 3 column 1). Acdream's overall dispatcher call rate is wildly lower than retail's in every scenario — see Finding 1 below.

Table 3 — ContactPlane lifecycle diff (the smoking gun)

Walk duration was variable per scenario; values below are raw counts over the full capture window. Ratios are comparable across rows because both clients walked similar-duration scenarios per pair.

Scenario Retail BP4 dispatcher Acdream push-back-disp Acdream/Retail dispatcher ratio Retail BP7 set_contact_plane Acdream cp-write CP-write ratio (acd/retail)
1 inn doorway 9,289 295 0.032× (31× fewer) 18 73,304 4,072×
2 inn stairs (acdream: stair-fail) 47,783 4,156 0.087× (11× fewer) 136 33,969 250×
3 inn 2nd floor (acdream teleport) 10,636 2,752 0.259× (4× fewer) 0 86,748
4 cottage cellar (acdream sling-out) 12,596 82 0.007× (154× fewer) 3 35,624 11,875×
5 town network portal 9,552 34 0.004× (281× fewer) 65 20,956 322×

Geometric mean of CP-write ratio across the 4 finite scenarios (excluding scen3 ∞): ~1,470×. Median (excluding scen3): ~2,200×.

Verdict: acdream writes the ContactPlane on order of 1,000× more frequently than retail. The only scenario where ratios are "small" (250×) is scen2's stair-attempt, where acdream's CP-write count is actually LOWER than the other scenarios because the failing physics couldn't synthesize a valid CP — see Finding 2 inversion.

Table 4 — Sub-step state mutations

PARTIAL. Per-field mutation counts require shadow-state diffing across sub-steps, which the v4 probe doesn't emit. What we CAN report is per-tag firing rates that approximate state-mutation pressure:

Scenario Retail BP2 step_up Acdream indoor-bsp Acdream indoor-walkable Acdream cell-cache Acdream check-bldg
1 inn doorway (0) 26 18 540 9,530
2 inn stairs (fail) 188 1,286 859 527 81
3 inn 2nd floor (0) 1,061 707 527 740
4 cottage cellar (sling) 13 2 2 540 5,495
5 town network 1 2 2 9,642 740

Notable patterns:

  • Acdream check-bldg fires 1-2 orders of magnitude more than push-back-disp in scen1 (9,530 vs 295), scen4 (5,495 vs 82), and scen5 (740 vs 34). The CheckBuildingTransit machinery is constantly re-resolving "which building is the player in" even when the BSP itself isn't being queried. This is a state-thrash separate from the CP-write blowup.
  • Acdream indoor-bsp and indoor-walkable scale together in the stair-attempt scenarios (scen2: 1,286/859; scen3: 1,061/707) but stay near zero on outdoor/portal walks (scen4/5: ~2 each). Suggests indoor BSP is gated by something that doesn't fire during normal Holtburg walking but DOES fire during stair attempts.
  • Cell-cache scales with how much landblock streaming happened: 540 on standstill scenarios, 9,642 on scen5 where the player walked across Holtburg to reach the network portal.

Per-scenario narrative

Scenario 1 — Inn doorway (prior session)

User walked through the Holtburg inn front door, stopped just inside. Standard short walk over a threshold.

Retail: 18 set_contact_plane calls (~one per second of walking). Acdream: 73,304 cp-write events. Ratio: 4,072×.

Per-call shape match (BP5 hit#1, vertical step-down probe against ground):

  • Plane: (0, 0, 1), d≈0 — identical.
  • Sphere radius: 0.48 — identical.
  • WalkInterp: 1.0 — identical.
  • Sphere.center.z and Movement.z DIFFER between retail and acdream (retail: -0.27 / -0.75; acdream: +0.46 / -0.50). Could be local-space convention difference (retail's localspace_pos vs our per-cell transform) OR could be the BSP correction-path divergence the spec hypothesizes. A6.P3 work surface.

Scenario 2 — Inn stairs (acdream re-captured as stair-FAILURE)

Retail walked successfully up 4 inn stair steps. Acdream re-captured AFTER an initial mislabeled door-walk: user attempted to climb the inn stairs, character failed (couldn't ascend).

Retail signature: BP2 step_up=188 — clean stair-climb signature (scen1 doorway had only 1 BP2 hit). BP6 check_walkable=677 (with threshold=FloorZ 0.6642, confirmed by hex decoder).

Acdream failure signature (stair-attempt vs door-walk):

Tag door-walk stair-attempt Ratio
push-back-disp 1,141 4,156 3.6×
push-back-cell 87 1,478 17×
other-cells 87 1,478 17×
indoor-bsp 343 1,286 3.7×
indoor-walkable 227 859 3.8×
cp-write 70,244 33,969 0.5× (inverse!)

The 17× explosion on push-back-cell / other-cells is the failure: when the indoor BSP query can't resolve a stair-step, the multi-cell fallback fires constantly. The cp-write DROP (half the door-walk volume) is the inverse signal: when no ground plane resolves, no CP gets written. Both are A6.P3 fix-surface indicators.

Scenario 3 — Inn 2nd floor (acdream via @teleport)

Flat-floor walk: forward 3 m, sidestep 1 m, walk back. Both clients in the same physical space (acdream got there via @teleport admin command).

Retail signature: BP1=10,217, BP4=10,636, BP5=113, BP6=113, BP2=0, BP3=0, BP7=0. No stairs, no walls, no contact plane updates — retail's physics did almost nothing because the 2nd-floor is flat and there's nothing to collide with.

Acdream signature: cp-write=86,748, push-back-disp=2,752, indoor-bsp=1,061, push-back=320.

The infinite-ratio CP-write blowup. Retail wrote CP zero times across an entire flat-floor walk; acdream rewrote CP fields 86,748 times. This is the cleanest evidence for Finding 2: the bug fires equally on ordinary flat indoor walking, not just on stair attempts.

Scenario 4 — Cottage cellar (asymmetric pair)

Retail: walked UP out of cellar (2-step ascent + indoor→outdoor exit). Acdream: teleported INTO cellar, walked a few meters, resolver flung +Acdream OUTSIDE the cottage.

Retail signature: BP2=13 (cellar ascent is 2 steps; gives 13 step_up hits — non-linear vs scen2's 188 hits for 4 stair steps; depends on step height and tick density). BP7=3 (almost no CP updates during ascent + exit).

Acdream sling-out signature: distinct from scen2's stair-attempt:

  • check-bldg=5,495 (CheckBuildingTransit fired constantly during the sling)
  • cell-transit=3 events captured the sling: 0xA9B40148 → 0xA9B40029 → 0xA9B30030, all reason=resolver. The third transit crossed a landblock boundary (A9B4 → A9B3).
  • indoor-bsp=2 (indoor BSP was barely queried during the sling!)
  • push-back=1 (no real sphere-adjustment happened)

The sling-out is the cell-RESOLVER misbehaving, not the BSP. ResolveCellId pushed the player out of indoor space without engaging the indoor BSP collision path at all. The check-bldg storm is the symptom: every tick, CheckBuildingTransit re-tries to figure out which building the player is in, gets it wrong, and the resolver acts on that wrong answer.

Scenario 5 — Town Network portal entry

Substituted for "Holtburg Sewer entry" (which doesn't exist). Both clients walked to the Town Network Portal in Holtburg, entered it, walked 2 m forward in the network hub.

Retail signature: clean walk + portal transition + indoor walking in hub. BP1=13,863, BP4=9,552, BP5=97, BP6=55, BP7=65 (moderate CP updates around the portal threshold), BP2=1 (portal threshold step-up).

Acdream signature: clean physics — no failure mode. cp-write=20,956 (still ~322× retail), push-back-disp=34 (very few dispatcher hits — mostly flat-ground walking with no collisions).

Cell-transit chain — captures the portal entry:

0x00000000 -> 0xA9B30030  reason=teleport  (login spawn)
0xA9B30030 -> 0xA9B40029 -> 0xA9B40021 -> 0xA9B40019 ->
0xA9B40011 -> 0xA9B40012 -> 0xA9B4000A -> 0xA9B4000B ->
0xA9B40003                                    (walked across Holtburg)
0xA9B40003 -> 0x00070143  reason=teleport  (PORTAL ENTRY)
0x00070143 -> 0xA9B30016  reason=resolver  (post-teleport resolver)
0xA9B30016 -> 0x00060016  reason=resolver  (lands at network hub)

Incidental discovery (filed as issue #95): post-teleport, [cell-cache] events showed visibleCells=135-145 per cell (vs normal ~4-7), with cells cached from 3 separate landblocks (0x0007, 0x020A, 0x0408) — different worldOrigins, i.e. different dungeons entirely. This is the portal-graph visibility blowup. Direct cause of "see through walls / other dungeons rendering" across the project.

Findings

Finding 1 — Dispatcher entry frequency mismatch (4× to 281× fewer in acdream)

Status: confirmed in all 5 scenarios; severity MEDIUM (probable secondary effect of the v4 probe scope rather than a single fix surface).

Retail decomp anchor: CTransition::transitional_insert — retail's outer loop dispatches BSPTREE::find_collisions per sub-step regardless of expected collision. (Spec §1.2 hypothesis.)

Our suspect code site: src/AcDream.Core/Physics/Transition.cs / src/AcDream.Core/Physics/BSPQuery.cs — the modern dispatcher path likely short-circuits when no candidate cell has potential collision geometry.

Divergence quantified: retail BP4 hit count vs acdream push-back-disp hit count, per Table 3 column "Acdream/Retail dispatcher ratio." Range: 0.004× (scen5) to 0.259× (scen3). Worst gap on flat-walk scenarios where retail still queries dispatcher constantly.

Proposed fix sketch: investigate whether acdream's Transition is correctly calling FindCollisions in the per-sub-step inner loop. If transitional_insert short-circuits on a "no obvious collision" heuristic, the optimization may be hiding CP retention behavior that ONLY runs in the dispatcher's idle paths (e.g. step_down probe-to-ground that maintains LKCP). Removing the short-circuit may close Finding 2 as a side effect.

Scenarios affected: all 5.

Finding 2 — ContactPlane resynthesis blowup (250× to ∞× more in acdream)

Status: confirmed in all 5 scenarios; severity HIGH (single largest M1.5 root cause).

Retail decomp anchor: COLLISIONINFO::set_contact_plane and the three documented retention mechanisms — Mechanism A (Path-6 land write in BSPQuery.FindCollisions), Mechanism B (LKCP-restore in validate_transition), Mechanism C (post-OK step-down probe). See spec §1.2.

Our suspect code site: src/AcDream.Core/Physics/Transition.FindEnvCollisions indoor branch — likely resynthesizes ContactPlane per frame instead of retaining via the three mechanisms. Closely related: the existing TryFindIndoorWalkablePlane synthesis workaround (flagged for A6.P4 removal).

Divergence quantified: per Table 3 column "CP-write ratio." Median 2,200× across the 4 finite scenarios; infinite ratio on scen3 (retail: 0 writes; acdream: 86,748 writes for the same flat-floor walk).

Proposed fix sketch:

  1. Audit every site in our physics code that writes ContactPlane. There should be at most 3 active sites per the retention mechanisms — likely we have N>>3.
  2. Replace per-frame ContactPlane.Set(...) calls with the retain-or-restore pattern: at the start of each tick, restore CP from LastKnownContactPlane (Mechanism B); only update when Path-6 lands write a new plane (Mechanism A); only re-probe via step-down when the post-OK position is suspect (Mechanism C).
  3. The TryFindIndoorWalkablePlane synthesis goes away as part of the same change (A6.P4).
  4. Verification: after the fix, re-run the scen3 capture. Target: acdream cp-write count drops from 86,748 to ≤ retail's BP7 + some buffer (say ≤ 100). If the drop is large, the change is on the right track.

Scenarios affected: all 5 — strongest signal in scen3 (∞× ratio).

Finding 3 — Indoor cell-resolver sling-out (scen4)

Status: confirmed in scen4; severity HIGH (player can't stay inside small indoor spaces).

Retail decomp anchor: CObjCell::find_cell_list Position-variant (acclient_2013_pseudo_c.txt:308742-308783 — already cited in CLAUDE.md as the cell-tracking ping-pong oracle for the M1.5 hypothesis).

Our suspect code site: src/AcDream.Core/Physics/PhysicsEngine.ResolveCellId + src/AcDream.Core/Physics/CellPhysics.CheckBuildingTransit. Issue #90 (cell-id ping-pong workaround) is part of this surface and would be removed in A6.P4 once the proper fix lands.

Divergence quantified: scen4 captured 3 cell-transit events during a few meters of walking inside a cottage cellar:

  • 0xA9B40148 → 0xA9B40029 (indoor cottage → outdoor cell, reason=resolver)
  • 0xA9B40029 → 0xA9B30030 (crossed landblock boundary, reason=resolver)

During the sling, [check-bldg] fired 5,495 times (CheckBuildingTransit re-resolving repeatedly), [indoor-bsp] fired only 2 times (indoor BSP was barely queried), and [push-back] fired only 1 time (no real sphere-adjustment).

Proposed fix sketch: ResolveCellId / CheckBuildingTransit should preserve indoor cell membership when the sphere is close to (but slightly outside) the indoor CellBSP volume — the cell-array hysteresis logic retail uses. Port the stickiness logic from the retail decomp anchor above. May obsolete issue #90's workaround.

Scenarios affected: scen4 directly; likely scen2/scen3 cellar/inn variants too once the visibility bug (#95) is fixed and we can re-capture.

Finding 4 — Portal-graph visibility blowup (scope-adjacent; filed as #95)

Status: confirmed in scen5; severity HIGH (blocks all dungeon navigation visually); filed as issue #95.

Not strictly an A6 physics finding — this surfaced incidentally during scen5 capture and explains the project-wide "dungeons are broken" symptom. Full writeup in docs/ISSUES.md issue #95. Mentioned here so A6.P3 sequencing knows about it: any future dungeon-physics work (A8 or M1.5-residual) needs #95 fixed first, because a broken visibility set makes any in-dungeon physics analysis untrustworthy (cells are loaded that shouldn't be, distance/visibility queries return wrong answers, etc).

M1.5 symptom coverage

Per spec §4.7, every M1.5-in-scope symptom maps to at least one bug candidate OR is explicitly flagged as deferred.

Symptom Source Mapped to finding Notes
Issue #83 — Indoor multi-Z walking broken ISSUES.md Finding 2 (CP-write) + Finding 3 (resolver sling) scen3 + scen4 evidence
Issue #88 — Indoor static objects vibrate ISSUES.md Finding 2 (CP-write resynthesis per-tick causes per-tick visible micro-adjustments on static-object physics) Hypothesis: same root cause
Issue #90 — Cell-id ping-pong at indoor doorway threshold ISSUES.md Finding 3 (cell-resolver bug) Issue #90 is the workaround; root cause is Finding 3. A6.P4 removes the workaround.
Stairs walk-through (acdream can't climb) observed Finding 1 + Finding 2 (the stair-step probe fails because CP isn't retained between sub-steps so step_up's walkability check sees the wrong plane) scen2 stair-attempt direct evidence
2nd-floor walking (works once teleported) observed Finding 2 only (scen3 shows pure flat-floor CP blowup) Walking itself fine; CP-write is the divergence
Cellar descent (acdream can't descend) observed Finding 1 + Finding 2 same as stairs not directly captured (couldn't descend in acdream) but same physics
TryFindIndoorWalkablePlane synthesis MISS spec §1.2 Finding 2 (same family) A6.P4 removes the workaround as part of the Finding 2 fix
Sling-out from inside building scen4 discovery Finding 3 (cell-resolver) NEW symptom not in original M1.5 list — promote to symptom roster
"Dungeons are broken" project-wide user-observed Finding 4 / issue #95 (NOT A6 scope) Defer to dedicated visibility-bug fix

A6.P2 acceptance test: every in-scope M1.5 physics symptom has a mapped finding. Met.

A6.P3 fix-surface sequencing recommendation

Per spec §5.1: "highest-confidence single-cause fix first."

Recommended order:

  1. Finding 2 first (CP-write resynthesis) — single largest divergence, single largest probable impact, narrowest suspected code site (Transition.FindEnvCollisions indoor branch + ContactPlane retention). If Finding 1 IS a secondary effect of CP-write missing the dispatcher idle paths (the hypothesis in Finding 1's fix sketch), then fixing Finding 2 may close Finding 1 automatically. Highest expected value per PR.
  2. Re-run scen1-5 captures after Finding 2 PR lands. Compute new ratios. If CP-write ratios drop from ~1000× to ~1× (target), Finding 2 is closed.
  3. If Finding 1 dispatcher gap also closed — proceed directly to Finding 3.
  4. If Finding 1 still wide — separate PR for the dispatcher-call-rate fix.
  5. Finding 3 (cell-resolver sling-out) — narrower fix; specific to ResolveCellId + CheckBuildingTransit cell-stickiness. PR also removes issue #90 workaround.
  6. A6.P4 visual verification at Holtburg inn → stairs → cellar. Acceptance per spec §6.3.
  7. Finding 4 / issue #95 is NOT in A6.P3 scope. Handle separately when scheduled for the visibility-bug work.

Open items / next-session candidates

  • A6.P1.5 (optional, ~1 hour): extend cdb probe with paired entry/exit BPs to capture adjust_sphere_to_plane output delta (Table 1) and find_collisions exit-path discrimination (Table 2). Only needed if A6.P3 fixes don't close the symptoms and we need sharper data. Defer until after A6.P3 first attempt.
  • Issue #95 (separate work surface): portal-graph visibility blowup. Schedule outside A6 since fixing it unblocks scen6-9 captures and any future dungeon physics work.
  • Symptom roster update: add "indoor sling-out" to M1.5 symptom list (Finding 3 family); already captured here as a finding, but M1.5 doc should reflect it.

A6.P3 slice 1 — SHIPPED 2026-05-21

Strip-synthesis + Mechanism B (LKCP restore) fix landed in 8 commits across this same session:

Commit Task What
ba9655f plan A6.P3 slice 1 implementation plan written
6b4be7f + c6bc2b9 T1 Research note: retail's CTransition::validate_transition LKCP-restore (line 272565-272583) + insertion-point identified in our Transition.ValidateTransition at TransitionTypes.cs:2849
869edd9 T2 Test instrumentation: CollisionInfo.ContactPlaneWriteCount counter
36975ef + a32f569 T3 Failing regression: IndoorContactPlaneRetentionTests — asserts ≤5 CP writes across 60 flat-floor frames
5aba071 T4 Mechanism B (LKCP restore) inserted in ValidateTransition + proximity-check sphere bug fix (GlobalSphere[0]GlobalCurrCenter[0])
5f7722a + 39fc037 + bd5fe2e T5 Indoor branch of FindEnvCollisions stripped to match retail's tiny CEnvCell::find_env_collisions shape; test redesigned as real regression sentinel (validated 60-writes-pre-strip → 0-writes-post-strip)
066568a T6/T7 partial scen2_inn_stairs_postfix acdream capture proves stairs now work
(this commit) T6 + T8 scen3_inn_2nd_floor_postfix capture + bookkeeping (findings doc + roadmap + CLAUDE.md + issues #96/#97 filed)

scen3 re-capture results (postfix)

scen3 (Holtburg inn 2nd floor flat-walk) re-captured in this slice-1 ship commit:

Metric Pre-fix (4b5aebc) Post-fix Reduction
acdream cp-write (absolute) 86,748 25,082 3.5×
acdream cell-cache events (proxy for session length) 527 9,629 18× longer session
cp-write per cell-cache (normalized) 164.61 2.60 63.2× per-unit-of-activity
retail BP7 set_contact_plane 0 0 unchanged (oracle)

Per-unit-of-activity drop is the meaningful number — a longer post-fix session naturally accumulates more total writes, but the rate per "unit of activity" (cell-cache events ~ landblocks traversed) collapsed 63×.

scen2 re-capture results (postfix — UNEXPECTED WIN)

scen2 (Holtburg inn stairs) acdream re-captured at commit 066568a. Pre-fix: physics hammered BSP trying to resolve stairs (failure mode). Post-fix: user walked up and down stairs multiple times with no failure. Tag shape shifted:

Tag Pre-fix (stair FAIL) Post-fix (stair SUCCESS) Signal
indoor-walkable 859 0 synthesis gone (as designed)
push-back-cell 1,478 879 (-40%) multi-cell iteration relaxed
push-back 51 345 (+577%) real step_up firing
push-back-disp 4,156 6,055 (+46%) real BSP traversal
cp-write 33,969 57,846 L622 seed (slice 2 work)

Stairs working post-slice-1 confirms A6.P2's hypothesis that Finding 1 (dispatcher entry frequency mismatch) was a secondary effect of Finding 2 — fixing CP retention also closes the cell-array iteration storm that prevented stair-step resolution.

Visual verification (user happy-testing, 2026-05-21)

User report from happy-testing session post-slice-1:

  • 2nd floor walking works (with caveats below)
  • Stairs up + down work (M1.5 demo target unblocked)
  • Cellar descent works (M1.5 demo target unblocked)
  • Phantom collisions occasionally on 2nd floor — filed as issue #97 (hypothesis: caused by #96)
  • Occasional fall-through on 2nd floor — filed as issue #97 (same)
  • See-through-walls indoors — issue #95 (not A6 scope; visibility blowup)
  • Indoor lighting broken — A7 scope

Status of A6.P2 findings post-slice-1

Finding Status post-slice-1
Finding 1 — dispatcher entry frequency mismatch CLOSED as side-effect of Finding 2 fix (scen2 dispatcher shape now retail-like)
Finding 2 — ContactPlane resynthesis blowup PARTIALLY CLOSED. Synthesis path eliminated (indoor-walkable = 0). Remaining 99.3% of post-fix CP writes come from PhysicsEngine.ResolveWithTransition line 622 — a per-tick body-CP seed that retail doesn't do. Filed as issue #96 for slice 2.
Finding 3 — Indoor cell-resolver sling-out OPEN. Not addressed by slice 1. Needs scen4 re-capture to confirm whether sling-out symptom persists post-slice-1 (possible side-effect close); separate fix surface in ResolveCellId / CheckBuildingTransit otherwise.
Finding 4 — Portal-graph visibility blowup OPEN as issue #95 (not A6 scope; user-confirmed during happy-testing).

Slice 2 recommendation

Highest-value next slice: gate the L622 per-tick CP seed. It's responsible for 99.3% of remaining post-fix CP writes (24,906 of 25,082 in scen3 postfix). Retail's equivalent code path fires zero set_contact_plane calls during flat-floor walks. Either remove the seed entirely (rely on Mechanism A/B for CP propagation) OR gate it to fire only when the body's CP has changed since last seed.

After slice 2, re-test phantom collisions + fall-through (issue #97) — they may close as side-effects (same family of "CP state being unstable across ticks"). If not, that becomes slice 3 territory + Finding 3 work.

A6.P4 (workaround removal + visual verification) can proceed in parallel with slice 2 if scope allows.