Commit graph

118 commits

Author SHA1 Message Date
Erik
ceeb06be7d ship(phys): A6.P3 slice 3 — cell-resolver ping-pong CLOSED + #98 re-diagnosed
Slice 3 v2 (3e140cf) added point-in cell-stickiness in
ResolveCellId's indoor branch. User verification + slice3v2 capture
confirms: cell-resolver ping-pong is FULLY CLOSED.

Data:
- scen2_v2 capture (pre-slice-3): 20+ cell-transit events with
  rampant ping-pong (0xA9B4014B ↔ 0xA9B4014A ↔ 0xA9B4013F at the
  cellar boundary, Z stable ~96.4 — same tick re-classification)
- slice3v2 capture (post-fix): 1 cell-transit event (login teleport
  only) — ping-pong fully eliminated

Findings:
- A6.P2 Finding 3 (cell-resolver sling-out family) CLOSED.
- Issue #90 (sphere-overlap stickiness workaround in same function)
  now redundant; can be removed in A6.P4 after broader visual
  verification.
- Issue #97 (phantom collisions + fall-through on 2nd floor) hypothesis
  pending: same instability family, likely closed as side-effect of
  this fix. Re-test on next happy-test session.
- Issue #98 (cellar-up stuck) PERSISTS but with NEW DIAGNOSIS.
  Originally filed as cell-resolver ping-pong (which was true and now
  fixed), but user verification shows the cellar-up symptom remains
  with a DIFFERENT root cause: BSP step-physics at the cellar stair
  TOP. Push-back trace from slice3v2 capture:
    n=(0, -0.719, 0.695) sloped face (walkable per FloorZ=0.664)
    delta=(0, 0, 0.75) step-down probe lifts sphere by 0.75m
    winterp=1.0->0.0 entire walk-interp consumed per tick
  Player progresses up most of the stairs but blocks at top step
  where the cellar transitions to the cottage main floor. #98 issue
  updated with this re-diagnosis.

Includes:
- scen4_cottage_cellar_slice3 acdream.log (slice 3 v1 evidence;
  ping-pong already closed by v1's sphere-overlap stickiness, but
  v1 over-corrected by holding player in cellar during legitimate
  transitions)
- scen4_cottage_cellar_slice3v2 acdream.log (slice 3 v2 evidence;
  point-in stickiness fixes the over-correction; cellar-up reveals
  the deeper BSP step-physics bug)

Docs updated:
- ISSUES.md — #98 re-diagnosed
- docs/plans/2026-04-11-roadmap.md — A6.P3 slice 3 marked SHIPPED;
  slice 4 (or A6.P4) scoped for #98 step-physics investigation
- CLAUDE.md — Currently-working-toward block updated

Test suite: 1148 pass + 8 pre-existing fail (baseline maintained).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 12:11:56 +02:00
Erik
d868946537 ship(phys): A6.P3 slice 2 — L622 seed investigation + #98 filed
Slice 2 v1 (`892019b`) attempted to close issue #96 by removing the
PhysicsEngine.cs L622 per-tick CP seed. v1 build/test green, CP-write
count dropped 91% in scen3 re-capture, BUT user happy-test surfaced
a regression: BSP step_up at the last step of stairs failed because
sub-step 1's AdjustOffset had no ContactPlane to compute the lift
direction.

Slice 2 v2 (`f8d669b`) reverted the seed removal + added a no-op-if-
unchanged guard inside CollisionInfo.SetContactPlane. The guard
early-returns when called with values matching current ci state.

Outcome:
- #96 PARTIALLY ADDRESSED, scope updated in ISSUES.md to "accepted as
  documented retail divergence." The seed is load-bearing for step_up;
  closing #96 fully would require deeper refactor (AdjustOffset
  fallback to body.ContactPlane). Guard is benign improvement.
- Slice 2 v2 verification capture (scen3_inn_2nd_floor_slice2v2/
  acdream.log) committed as evidence — 226,464 cp-writes from L624
  seed confirms guard doesn't trigger for fresh-ci-per-tick pattern.
- Slice 2 v1 verification capture (scen3_inn_2nd_floor_slice2/
  acdream.log) also committed — confirms v1 actually reduced cp-writes
  (2,690 total) but the step_up regression made it unshippable.

NEW M1.5 BLOCKER FILED — issue #98: cellar ascent stuck at last step.
Evidence in slice2v2 capture's cell-transit chain:
  0xA9B4014B → 0xA9B4014A → 0xA9B4013F → 0xA9B4014A → 0xA9B4014B → ...
  (Z stable ~96.4; CellId ping-pongs every tick)
This is Finding 3 family (cell-resolver hysteresis missing) — same
root cause as #90 workaround + scen4 sling-out. Retail oracle:
CObjCell::find_cell_list Position-variant at
acclient_2013_pseudo_c.txt:308742-308783.

NEXT — A6.P3 slice 3:
- Port retail's cell-array hysteresis into ResolveCellId +
  CheckBuildingTransit.
- Closes #98 (cellar-up), possibly #97 (phantom collisions same
  instability family), enables #90 workaround removal.

Documents updated:
- ISSUES.md — #96 scope updated, #98 filed
- docs/plans/2026-04-11-roadmap.md — A6.P3 slice 2 marked SHIPPED,
  slice 3 scope added
- CLAUDE.md — Currently-working-toward block updated to slice 3

Test suite: 1148 pass + 8 pre-existing fail (baseline maintained).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 11:58:05 +02:00
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
Erik
066568a711 capture(research): A6.P3 slice 1 — scen2 postfix proves stairs now work
Unexpected slice 1 win: the synthesis-strip + Mechanism B (LKCP
restore) fix didn't just close Finding 2 (CP-write blowup) — it also
unblocked stair-walking, which A6.P2 had categorized as Finding 1+3
territory expected to need separate fixes. User reports walking up
and down the inn stairs multiple times in acdream post-fix.

Shape shift in tag distribution:

  Tag                  Pre-fix (FAIL)  Post-fix (SUCCESS)  Signal
  ----                 -------------   ------------------  ------
  indoor-walkable        859              0                synthesis gone
  push-back-cell        1478            879 (-40%)         multi-cell relaxed
  push-back               51            345 (+577%)        real step-up firing
  push-back-disp        4156           6055 (+46%)         real traversal
  cp-write             33969          57846                L622 seed (slice 2)

Pre-fix: synthesis firing while physics hammers BSP trying to resolve
stair-step (failure mode). Post-fix: real BSP queries succeeding, real
step-up + step-down landing. Same shape as retail's stair-climb
(retail scen2: BP2 step_up=188, push-back-disp dominates).

A6.P2 Finding 1 (dispatcher entry frequency mismatch) hypothesis was
"likely secondary effect of Finding 2 — may close as side effect of
the fix." Confirmed empirically: dispatcher activity now matches
retail-like shape without explicit Finding 1 work.

Remaining (slice 2 territory):
- L622 per-tick PhysicsEngine.ResolveWithTransition seed fires 99.3%
  of remaining cp-writes; retail's equivalent fires zero times on
  flat-floor walks. Gate this seed to close the remaining CP-write
  gap.
- Phantom collisions + occasional fall-through on 2nd floor reported
  by user during happy-testing. New issue to file.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 10:07:35 +02:00
Erik
c6bc2b9980 fix(research): A6.P3 slice 1 T1 — citation corrections + LKCP re-latch note
Code-review feedback on commit 6b4be7f:
  - Section 1: strip stale [309NNN] inline annotations (off by 2-8
    lines from actual file content; the 0052c1xx address comments
    are the reliable anchor); address comments already present in the
    decomp output are now used as inline anchors instead
  - Section 2: validate_transition function header is at file line
    272547 (was: 272538, inside the preceding check_collisions
    function). Address 0050aa70 + LKCP-block range 272565-272583
    were already correct. References section updated to match.
  - Section 5: add note that SetContactPlane re-latches LKCP fields
    (no-op when LKCP is the source, but non-obvious side effect)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 08:20:24 +02:00
Erik
6b4be7f863 docs(research): A6.P3 slice 1 — retail Mechanism B oracle for CP retention
Pre-fix research note grounding the indoor CP-retention refactor in
retail's exact LKCP-restore pattern (acclient_2013_pseudo_c.txt:272565-272582)
and CEnvCell::find_env_collisions tiny shape (line 309573).

Key findings:
- find_env_collisions writes NO ContactPlane — only BSP Path 6 does (Mech A)
- validate_transition Collided/Slid/Adjusted branch calls set_contact_plane
  from LKCP when proximity guard passes (global_curr_center, not global_sphere)
- Our ValidateTransition is missing the SetContactPlane call in that branch
  (sets Contact/OnWalkable flags only) — this is the gap Task 4 closes
- Proximity sphere should be GlobalCurrCenter[0] not GlobalSphere[0]
- Exact insertion point: TransitionTypes.cs ~line 2849, inside the
  'radius + EPSILON > |angle|' proximity-guard branch

Output of this note drives the per-transition Mechanism B insertion
point selection in Task 4 + the slice-1 acceptance shape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 08:09:40 +02:00
Erik
90fbdc02df docs(roadmap+milestones): mark A6.P1/A6.P2 shipped; update M1.5 demo
A6.P1 (cdb probe spike) + A6.P2 (analysis report) both SHIPPED this
session. Updated:

  docs/plans/2026-04-11-roadmap.md — M1.5 phase block now shows A6.P1
  + A6.P2 SHIPPED with commit refs; A6.P3 entry expanded with the
  Finding-2-first sequencing recommendation from A6.P2; A6.P4 entry
  notes the original "Holtburg Sewer end-to-end" acceptance walk is
  unreachable (sewer doesn't exist).

  docs/plans/2026-05-12-milestones.md — M1.5 demo scenario split into
  building/cellar half (achievable post-A6.P3) + dungeon half (blocked
  on issue #95 visibility blowup; promote to post-M1.5 if #95 isn't
  fixed in scope). Issue list updated: added #95 + indoor sling-out
  (new from scen4); marked stairs/2nd-floor/cellar as characterized by
  A6.P2 Finding 2 family.

  CLAUDE.md — Currently-working-toward block now points at A6.P3 as
  the active phase. A6.P1 + A6.P2 ship noted with the findings doc
  pointer. Demo-scenario note updated to reflect the sewer + #95
  reality. Issues-in-scope updated.

Also includes a 1-line trailing-prompt addition to scen3 + scen4
retail.log files (cdb wrote one more `0:000>` after the kill that
landed after the original capture commits).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 21:17:21 +02:00
Erik
184933d796 docs(research): A6.P2 — capture findings report (5 of 9 scenarios)
Replaces the A6.P1 stub with the analysis pass over 5 paired captures
(scen1-5). Scen6-9 (sewer-specific) cancelled because the Holtburg Sewer
doesn't exist on this ACE server and any substitute dungeon hits issue
#95 (portal-graph visibility blowup) on entry.

Four findings ready for A6.P3 sequencing:

  Finding 1 — Dispatcher entry frequency mismatch (4x to 281x fewer in
              acdream). Likely secondary effect; may close as side-effect
              of Finding 2 fix.

  Finding 2 — ContactPlane resynthesis blowup. 250x to INFINITE more CP
              writes in acdream. Strongest single signal; scen3 shows
              retail wrote CP zero times during a flat 2nd-floor walk
              while acdream wrote 86,748 field updates. Primary M1.5
              root cause. HIGH severity.

  Finding 3 — Indoor cell-resolver sling-out (scen4). Resolver flings
              +Acdream across landblock boundary; CheckBuildingTransit
              fires 5,495 times during the sling while indoor BSP is
              barely queried. Same family as the M1.5 cell-tracking
              ping-pong hypothesis. HIGH severity.

  Finding 4 — Portal-graph visibility blowup (scen5 incidental). Filed
              as issue #95; not strictly A6 scope but documented here so
              A6.P3 sequencing knows about it.

Tables 1+2 (per-site push-back delta + path-frequency diff) deferred to
A6.P1.5: the v4 cdb probe captures function entry only, not exit values.
Adding paired exit BPs is ~1 hour of cdb scripting work but not needed
unless A6.P3 fixes fail to close the symptoms.

Table 3 (CP lifecycle) fully populated — geometric mean CP-write ratio
across 4 finite scenarios is ~1,470x; median ~2,200x.
Table 4 (sub-step state mutations) partially populated with proxy
metrics (per-tag firing rates).

M1.5 symptom coverage matrix: every in-scope physics symptom maps to
at least one finding. Acceptance per spec §4.7 met.

A6.P3 sequencing recommendation: Finding 2 first (highest-confidence
single-cause; may close Finding 1 as side effect), re-run captures, then
Finding 3. Issue #95 handled separately outside A6.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 21:13:07 +02:00
Erik
35d5c58c7b capture(research): A6.P1 scen5 — town network portal entry paired traces
Substituted "Holtburg Sewer" portal with Town Network Portal — no
sewer entry exists in this world (user-verified). Town Network is
also an outdoor->indoor portal transition with the same physics
signature.

Both clients walked to the portal, entered, walked 2 m inside.
Retail: clean traversal. Acdream: also clean (no failure mode).

Retail (decoded, 23,890 raw / 9,769 BP lines):
  BP1 transitional_insert: 13,863
  BP4 find_collisions:      9,552
  BP5 adjust_sphere:           97
  BP6 check_walkable:          55
  BP7 set_contact_plane:       65  (moderate, portal threshold + indoor)
  BP2 step_up:                  1

Acdream (31,914 lines, no failure):
  [cp-write]:        20,956  (vs retail BP7 = 65 — ~322x ratio)
  [cell-cache]:       9,642  (Holtburg landblock streaming)
  [check-bldg]:         740
  [push-back-disp]:      34  (flat-ground walking)
  [push-back]:            1
  [cell-transit]:        12  (CLEAN traversal, no thrashing)

cell-transit event chain — captures the portal entry signature:
  0x00000000 -> 0xA9B30030  (login teleport)
  0xA9B30030 -> 0xA9B40029 -> 0xA9B40021 -> 0xA9B40019 ->
  0xA9B40011 -> 0xA9B40012 -> 0xA9B4000A -> 0xA9B4000B ->
  0xA9B40003  (walked across Holtburg, all reason=resolver)
  0xA9B40003 -> 0x00070143 reason=teleport  (PORTAL ENTRY)

scen5 is the "control" — both clients reached their target, no
visible failure. The CP-write blowup persists as the only A6.P2
divergence. Useful baseline for separating "indoor physics broken
during walking" (scen2, scen3, scen4) from "indoor physics okay
when portal-delivered" (scen5).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 20:59:00 +02:00
Erik
46c6e08ee5 capture(research): A6.P1 scen4 — cottage cellar paired traces
Asymmetric pair (scenario-level, not protocol-failure):
- Retail: user walked UP out of the cellar (ascent of 2 cellar
  steps + exit through doorway) — captures ascent + indoor-to-
  outdoor transition.
- Acdream: user teleported INTO the cellar, walked a few meters,
  the resolver flung +Acdream OUTSIDE the cottage entirely
  (landblock prefix changed A9B4 -> A9B3 mid-walk) — captures
  a real indoor physics failure that's not a stair issue per se.

Both traces are valuable to A6.P2 even though they don't match
walk-for-walk.

Retail (decoded, 22,536 raw / 12,875 decoded BP lines):
  BP1 transitional_insert:  9,402
  BP4 find_collisions:     12,596  (ended in mem-access error
                                    @ hit#12596 - cdb hit a null
                                    transition arg, dropped to
                                    interactive prompt; worth a
                                    note for A6.P2 retail edge)
  BP5 adjust_sphere:          136
  BP6 check_walkable:         128
  BP2 step_up:                 13  (2-step cellar = 13 vs scen2
                                    4-step inn = 188; non-linear)
  BP7 set_contact_plane:        3  (Finding 2 holds)

Acdream (42,001 lines, ended with sling-out):
  [cp-write]:        35,624
  [check-bldg]:       5,495  (CheckBuildingTransit fired
                              constantly trying to re-resolve
                              which building +Acdream was in)
  [cell-cache]:         540
  [push-back-disp]:      82  (very few dispatcher hits)
  [push-back]:            1  (almost no sphere-adjustment)
  [indoor-bsp]:           2  (indoor BSP barely queried!)
  [cell-transit]:         3  (3 transit events captured the sling:
                              0xA9B40148 -> 0xA9B40029 -> 0xA9B30030
                              all reason=resolver)

Sling-out signature: indoor BSP never engaged (only 2 indoor-bsp
hits), but the cell resolver fired 3 transit events crossing a
landblock boundary, with check-bldg thrashing in between. This is
distinct from scen2's stair-attempt pattern (which hammered the
BSP); scen4 shows the resolver pushing the character out of indoor
space entirely without triggering the indoor BSP collision path.

A6.P2 fix surface: investigate why ResolveCellId / CheckBuildingTransit
push a player from indoor cell 0xA9B40148 to outdoor cell 0xA9B30030
through routine walking. Likely the same family as the M1.5 hypothesis:
indoor cell membership isn't sticky (the ping-pong bug from the
2026-05-20 A4 handoff in a different guise).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 20:50:09 +02:00
Erik
4b5aebc61f capture(research): A6.P1 scen3 — inn 2nd floor paired traces
User reached the 2nd floor in acdream via ACE @teleport (stair-physics
unblocked separately by scen2). Retail walked normally to 2nd floor.
Both clients performed the same walk: forward 3 m, sidestep 1 m,
walk back. Flat-floor scenario, no stairs, no transitions.

Retail (decoded, 21,337 lines):
  BP1 transitional_insert: 10,217 hits
  BP4 find_collisions:     10,636 hits
  BP5 adjust_sphere:          113 hits
  BP6 check_walkable:         113 hits  threshold=0.6642
  BP2 step_up:                  0 hits  (no stairs)
  BP3 set_collide:              0 hits  (no walls)
  BP7 set_contact_plane:        0 hits  (KEY: zero CP updates)

Acdream (93,558 lines):
  [cp-write]:        86,748  (vs retail BP7 = 0 — INFINITE ratio)
  [push-back-disp]:   2,752
  [push-back]:          320
  [push-back-cell]:     550
  [other-cells]:        550
  [indoor-bsp]:       1,061
  [indoor-walkable]:    707

KEY FINDING for A6.P2: scen3 is the strongest CP-write blowup
evidence yet. On a flat 2nd-floor walk where retail's
set_contact_plane fires ZERO times across the entire scenario,
acdream rewrites the contact plane 86,748 times. This is the
exact pattern Finding 2 hypothesized (M1.5 design spec §1.2):
acdream resynthesizes CP every frame instead of retaining it
through the documented retention mechanisms (LKCP-restore,
Path-6 land write, post-OK step-down probe).

scen3 pair confirms CP-write blowup isn't stair-specific — it
fires equally for ordinary flat-floor walking inside any indoor
cell. A6.P3 fix surface: same as Finding 2 — stop resynthesizing
CP per frame.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 20:39:46 +02:00
Erik
297d1c54e8 capture(research): A6.P1 scen2 — replace acdream pair with stair-attempt
Original acdream capture (a9a427f) was a doorway-walk because acdream's
indoor stair physics doesn't work. For A6.P2 to characterize the
divergence we need the FAILURE captured, not a substitute walk.

User re-attempted the inn stairs in acdream (whatever it produces:
bumping, sliding, stuck). Failure signature is dramatic vs door-walk:

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

The 17x explosion on push-back-cell / other-cells says acdream's
CheckOtherCells loop fires constantly when physics can't resolve a
stair-step — the indoor BSP query fails, then the multi-cell
fallback fails, then the next tick repeats. 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.P2 fix-surface
indicators.

Now scen2 pair = retail successfully climbs (BP2 step_up=188) vs
acdream tries and fails (push-back-cell explosion).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 20:26:20 +02:00
Erik
a9a427fff9 capture(research): A6.P1 scen2 — inn stairs paired traces
Walked +Acdream up 4 steps of Holtburg inn, stopped on landing,
shuffled briefly to push past 50K cdb threshold. Acdream walk
exceeded the minimal scenario script (user explored further) —
scen2 dataset is a superset of the protocol; A6.P2 analysis can
window to the comparable section.

Retail (decoded, 110,104 lines):
  BP1 transitional_insert: 60,289 hits  (~5x scen1)
  BP2 step_up:                188 hits  (vs scen1 = 1 — stair signal)
  BP4 find_collisions:     47,783 hits  (~8x scen1)
  BP5 adjust_sphere:          780 hits  (~65x scen1)
  BP6 check_walkable:         677 hits  threshold=0.6642 confirmed
  BP7 set_contact_plane:      136 hits  (~7x scen1)

Acdream (73,937 lines):
  [cp-write]:        70,244 — CP-write blowup persists on stairs
  [push-back-disp]:   1,141
  [push-back]:          101
  [push-back-cell]:      87
  [indoor-bsp]:         343
  [indoor-walkable]:    227

Note: cdb's qd-in-BP-action threshold doesn't actually self-detach
(documented CLAUDE.md cdb watchout) — user closed retail manually.
Three remaining workflow steps work cleanly (decode + acdream pair
+ graceful close).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 20:21:05 +02:00
Erik
2f2b63f8bd docs(handoff): A6.P1 partial-ship — infra DONE, captures 1/9
Pickup-prompt + lessons doc for the A6.P1 capture work. Documents:

- The 16 commits shipping today (infrastructure Tasks 1-14 + cdb
  script v1→v4 iteration + scen1 capture + decoder).
- WHY cdb iterated 4 times: v1 wrong offsets, v2 PowerShell UTF-16,
  v3 cdb %f unreliable with dwo()/@@c++, v4 hex output works.
- Scen1 findings already strong: dispatcher entry frequency mismatch
  (acdream 20× fewer than retail) + ContactPlane write blowup
  (~100-1000× more frequent in acdream) — directly confirms the
  spec's M1.5 hypothesis about per-frame CP resynthesis.
- Per-scenario protocol validated by scen1.
- Pasteable session-start prompt for picking up scenarios 2-9.
- Known issues (kill-cdb-kills-retail, .printf %f unreliable, etc).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 20:05:17 +02:00
Erik
194ed3ef21 feat(cdb): A6.P1 — decode_retail_hex.py hex→float decoder
Python tool that decodes the retail.log hex-bits float fields produced
by a6-probe.cdb v4 into IEEE 754 single-precision values. Required
because cdb's .printf %f doesn't reliably format floats from dwo()
reads — v4 works around this by emitting 32-bit hex, this script
reinterprets via struct.unpack('<f', struct.pack('<I', value)).

Verified against scen1 retail.log:
  BP6 threshold_h=0x3F2A0751 → threshold=0.6642 (= FloorZ exactly)
  BP5 hit#1 Nz_h=0x3F800000 → Nz=1.0 (ground normal)
  9,517 float fields decoded across 9,331 lines.

Output written next to input as .decoded.log. Format matches
acdream-side [push-back] probe (4-decimal floats), so A6.P2
analysis can compare line-for-line.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 20:03:03 +02:00
Erik
8ca718a56d capture(research): A6.P1 scen1 — acdream.log paired with retail.log
Acdream-side capture for the Holtburg inn doorway walk, paired with
the v4 retail capture committed at 180b4a5. 84,130 lines total.

Probe line distribution (~30 sec session, ~2 sec actual walk):
  [push-back] (adjust_sphere): 8 hits   — vs retail BP5 12 hits
  [push-back-disp] (dispatch): 295      — vs retail BP4 5818 (!)
  [push-back-cell] (other_cells): 5     — vs retail's check_other_cells
  [indoor-bsp]: 26
  [cell-transit]: 30 (cell ID changes)
  [cp-write]: 73,304 (per-field writes) — vs retail BP7 18 fn calls (!)
  [cell-cache]: 540

Two major divergences already visible from this single scenario:

1. DISPATCH FREQUENCY: retail's BSPTREE::find_collisions fires 20×
   more than acdream's BSPQuery.FindCollisions. Could reflect either
   different physics tick rate, different sub-step cadence, or
   different call paths into the dispatcher.

2. CONTACTPLANE LIFECYCLE: acdream writes CP fields 73,304 times
   in 30 seconds (~2,400/sec). Retail calls set_contact_plane 18
   times (~0.6/sec). Even with a 6× field-write multiplier per
   set_contact_plane call, that's ~100 actual CP updates in retail
   vs ~12K in acdream — 100-1000× more frequent in acdream. This
   directly confirms the spec's hypothesis that FindEnvCollisions
   indoor branch is rewriting CP every frame (sub-step?) instead
   of retaining it across frames. Same family as the
   TryFindIndoorWalkablePlane workaround.

Per-call shape comparison (BP5 hit#1):
  Retail: plane=(0,0,1) d=-0.0, sphere=(0.0046,10.31,-0.27) r=0.48,
          mvmt=(0,-0,-0.75), winterp=1.0
  Acdream: plane=(0,0,1) d=-0.0, sphere=(-0.43,11.02,0.46) r=0.48,
           mvmt=Z-down, winterp 1.0→0.96 (small adjust applied)
Identical operation SHAPE (ground plane + vertical step-down probe
+ same radius). XY positions differ because walks were independent.

Scenario 1 complete. Remaining 8 scenarios deferred per user
direction. Python hex→float decoder + A6.P1 handoff doc to follow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 20:02:08 +02:00
Erik
180b4a5010 capture(research): A6.P1 scen1 — retail.log with real hex-bits floats
v4 cdb probe captured paired field data for the Holtburg inn
doorway walk. 13,552 BP hits in ~2 sec of walking. Distribution:
  - BP1 transitional_insert: 7,686 (sub-step loop)
  - BP4 find_collisions:      5,818 (per cell per sub-step)
  - BP5 adjust_sphere_to_plane: 12 (the over-correction suspect)
  - BP6 check_walkable:        12
  - BP7 set_contact_plane:     18

Smoking-gun verification:
  BP6 threshold_h=0x3F2A0751 ≈ 0.664 = PhysicsGlobals.FloorZ
  BP5 plane normal = (0,0,1), movement = (0,-0,-0.75) — classic
       step-down probe against the ground polygon
  BP5 sphere radius = 0x3EF5C28F ≈ 0.480 m — player foot sphere

All hex-bits floats decode cleanly via Python struct.unpack('<f').
Decoder script TBD as part of the handoff.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 19:59:33 +02:00
Erik
2d841cb615 fix(cdb): A6.P1 — a6-probe.cdb v4 hex-bits floats
v3 with @@c++(*(float*)..) STILL produced 0.000000 across the board.
Conclusion: cdb's .printf %f is unreliable for our use case — possibly
doesn't handle the float-to-double promotion in varargs the way C
printf does, or has a deeper limitation we don't have time to debug.

Pivoting to: print all floats as 32-bit hex bits via %08X, reinterpret
in the Python analysis pipeline via struct.unpack('<f', bytes.fromhex(...))
to recover IEEE 754 single-precision values.

This bypasses cdb's float formatting entirely. Integer reads (which
work — substeps, insertType, collide flag, isWater) stay as %d.

The smoking gun: BP6's check_walkable threshold should be 0.0871556997
(cos 85°) per the decomp call site at acclient_2013_pseudo_c.txt:273202.
v4's BP6 should output threshold_h=0x3DB283D7. If it does, the
infrastructure is sound and we can proceed to all 9 scenarios.

v3 capture preserved as retail-v3-cpp-zero-floats.log audit trail.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 19:55:48 +02:00
Erik
1b6d49ea57 fix(cdb): A6.P1 — a6-probe.cdb v3 with C++ float reads
v2 dry-run produced correct hit counts but all %f field values
printed as 0.000000 — including BP6 threshold which the decomp says
must be 0.0871556997f (cos 85°). Root cause: cdb's MASM evaluator
returns dwo(addr) as a 32-bit integer; .printf %f expects a 64-bit
double; passing the integer to %f produces formatted-zero garbage.

Fix: switch all float-reading expressions to @@c++(*(float*)addr).
The C++ evaluator dereferences memory as a float pointer, returning
a proper float that .printf %f formats correctly. Integer reads (%d)
still use MASM dwo() — that works.

For double-indirect (pointer args), the form is
  @@c++(*(float*)(*(unsigned int*)(@esp+N)+offset))
which reads the pointer at [esp+N], adds the offset, and treats the
result as a float pointer.

v2 capture preserved as retail-v2-zero-floats.log audit trail.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 19:50:11 +02:00
Erik
d0c8c54d96 fix(cdb): A6.P1 — v1 dry-run lessons + v2 prep tooling
Dry-run of scenario 1 (retail-v1-broken-offsets.log preserved as
audit trail) surfaced three issues with the v1 cdb script:

1. STACK-ARG OFFSETS WRONG: BP actions used arbitrary registers
   (@edx, @edi) to read function args, but __thiscall puts non-this
   args on the stack ([esp+N] after the return address). All 12 BP5
   "adjust_sphere" hits printed Nx=0.0 Ny=0.0 ... — fields not read.
   Fixed by writing a type dumper (a6-types-dump.cdb + runner) that
   uses cdb's `dt` command against the loaded PDB to get authoritative
   struct offsets. v2 probe script (to be written next) will use
   double-indirect reads (dwo(poi(@esp+N)+offset)) with correct
   offsets from the dump.

2. TEE-OBJECT UTF-16 ENCODING: PowerShell's default Tee-Object writes
   UTF-16 LE with BOM, making logs unparseable by grep without
   conversion. Runner now uses Out-File -Encoding ASCII. Sacrifices
   live console echo; use `Get-Content -Tail 50 -Wait` in a separate
   shell if live monitoring is needed.

3. BP6 SYMBOL NOT FOUND: `acclient!CTransition::validate_walkable`
   doesn't exist in the PDB. Decomp at line 272811 has
   `CTransition::check_walkable` — likely the actual name. To be
   verified + fixed in v2.

The BP hit-count distribution from v1 is still meaningful diagnostic
data (14,318 transitional_insert + 16,558 find_collisions + 40
set_contact_plane + 12 adjust_sphere + 1 step_up + 1 set_collide in
a 2-second walk through the inn doorway). Preserved as a baseline
sanity-check the v2 distribution can be diffed against.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 19:38:31 +02:00
Erik
22e341faf6 docs(research): A6.P1 — refresh PDB verification to MATCH
User swapped in the correct Sept 2013 EoR build acclient.exe.
GUID {9e847e2f-777c-4bd9-886c-22256bb87f32}, linker UTC
2013-09-06T00:17:56 — exact match for refs/acclient.pdb.
T15 captures are unblocked.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 18:51:34 +02:00
Erik
260c60f8f5 docs(research): A6.P1 — capture directory structure + findings stub
Creates the 9 per-scenario capture directories (gitkeep stubs) and
the findings doc stub at docs/research/2026-05-21-a6-cdb-capture-findings.md.
A6.P1 fills the capture log slots (Task 15, user-driven); A6.P2
fills the analysis tables and findings section.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 18:46:56 +02:00
Erik
0e21f22fc5 docs(research): A6.P1 — record retail-PDB match verification (MISMATCH)
Audit trail for the A6.P1 capture session: the retail binary at
C:\Turbine\Asheron's Call\acclient.exe is the 2015-06-12 build
(GUID {08e25c14-e2a1-46d5-b056-92b2e43a7234}), not the Sept 2013
EoR build that pairs with refs/acclient.pdb
(expected GUID {9e847e2f-777c-4bd9-886c-22256bb87f32}).

BP-driven A6 captures cannot proceed until the matching binary is
installed. User needs acclient.exe v11.4186 (linker timestamp
2013-09-06) to match our PDB.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 18:45:32 +02:00
Erik
b9c111b80d docs(O): O-T1 audit shipped + Phase O spec amended
O-T1 audit (REPORT-ONLY) maps acdream's transitive closure on WorldBuilder:
33 files / ~7.7K LOC across Chorizite.OpenGLSDLBackend (28 files) and
WorldBuilder.Shared (5 files). Verdict on O-Q1 (thread-model): SAFE —
adapters run render-thread only; no worker-thread access to WB code.

Spec amendments incorporated via brainstorm:

- O-D7: Refactor ObjectMeshManager to take DatCollection directly (not
  via adapter). T4 safety check — fall back to thin adapter if call-site
  count >20.
- O-D8: Drop LandSurfaceManager, EnvCellRenderManager, PortalRenderManager,
  TerrainRenderManager from the extract list — audit confirmed not reachable
  (we have our own ports or never used them).
- O-D9: Promote 3 internal types in Chorizite to public on extraction
  (EmbeddedResourceReader, TextureFormatExtensions, BufferUsageExtensions).
- O-D10: Strip [MemoryPackable] from TerrainEntry (we don't serialize).
- O-D11: Namespace AcDream.Core.Rendering.Wb.* for extracted code.
- O-D12: Drop ResolveId + [indoor-upload] NULL_RESULT diagnostic block.

Task breakdown: T6 (EnvCell/portal) eliminated; T5 (stateless helpers)
shrinks to 0.5d; T4 (mesh + refactor) grows to 2.5d. Net effort estimate
holds at ~7.75d.

All originally-open spec questions are now closed (Q1/Q2/Q3/Q4) or
deferred to T3 with an explicit verify step (Q5: SixLabors.ImageSharp
reachability).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 14:35:38 +02:00
Erik
e702dec7a3 docs(O-T1): session handoff prompt for Phase O Task 1 (WB usage audit)
Self-contained prompt for a fresh Claude Code session. The next session
reads it once, has all the context it needs, produces the WB-usage
closure audit at docs/research/2026-05-21-phase-o-t1-wb-audit.md,
and stops before any extraction. Investigation-only (the /investigate
skill applies). User reviews the audit before T2 begins.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 14:01:06 +02:00
Erik
f02bd1fb4d docs(handoff): M1.5 kickoff — pickup prompt + workaround inventory
Comprehensive session-end handoff for picking up M1.5 in a fresh
session. Includes:
  - Today's 11 shipped commits (table with retail oracle anchors)
  - Visual verification status at Holtburg inn + cottages
  - Open issues tagged M1.5 scope (#80, #81, #83, #88, #90, #93, #94)
  - Workaround inventory with A6.P4 removal criteria:
    - #90 sphere-overlap stickiness (PhysicsEngine.ResolveCellId:285)
    - TryFindIndoorWalkablePlane synthesis (TransitionTypes.cs:1294)
  - A6.P1 cdb probe spike methodology + 9 capture scenarios
  - Pasteable session-start prompt
  - Anti-patterns from today's session (especially: don't ship
    workarounds without flagging them upfront — #90 was a slip)
  - Code anchors + retail decomp anchors for A6 brainstorming

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 11:12:46 +02:00
Erik
6d18d879a2 docs(milestones): promote indoor work to M1.5 — primary focus
Continued indoor testing through 2026-05-20 surfaced a deep family
of physics + lighting bugs that span buildings AND dungeons. Today's
session shipped 5 surgical fixes (A4 + #89 + #90 + #91 + #92) that
close the user-visible "walls walk through at Holtburg inn" symptom,
but #90 specifically is a CLAUDE.md-rules workaround (sphere-overlap
stickiness on top of point-only cell containment) added without prior
approval. The underlying issue (BSP push-back distance probably
diverges from retail) hasn't been measured. Plus the umbrella #83
(indoor multi-Z walking) has been open since 2026-05-19 with multiple
aborted fix attempts; plus indoor lighting (#80 + #81 + new #93 +
#94) has been deferred as "M7 polish" but is actually part of the
same indoor-experience problem.

Promoting to a milestone of its own forces the work to be central,
retail-anchored, and complete — not another whack-a-mole patch.

Milestone M1.5 — "Indoor world feels right":
  Demo: enter Holtburg Sewer through the in-town portal, navigate
  through 5-7 rooms with stairs + a multi-Z chamber, exit back to
  town. Walls block. Stairs work. Items block. Lighting reads
  correctly. Cell transitions smooth.

  Phases:
    A6 — Indoor physics fidelity (cdb-driven)
    A7 — Indoor lighting fidelity (RenderDoc + retail-decomp driven)

  Issues in scope: #80, #81, #83, #88, #90 (workaround removal),
  #93 (new lighting umbrella), #94 (held-item spotlight),
  + TryFindIndoorWalkablePlane synthesis removal.

M2 ("Kill a drudge") deferred until M1.5 lands.

This commit updates:
  - docs/plans/2026-05-12-milestones.md (M1.5 block inserted, M2 moved
    to deferred status)
  - docs/plans/2026-04-11-roadmap.md (A6 + A7 sub-pieces detailed)
  - CLAUDE.md (Currently working toward updated to M1.5, M2 paragraph
    marked deferred, M1.5 baseline shipped paragraph added)
  - docs/ISSUES.md (#80, #81, #83, #88, #90 tagged M1.5 scope;
    new #93 indoor lighting umbrella + #94 held-item spotlight filed)
  - docs/research/2026-05-21-open-items-pickup-prompt.md (landscape
    table reorganized around M1.5 phases)

A6 + A7 specs to be drafted in the next session(s).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 10:32:24 +02:00
Erik
1534990102 docs(roadmap): A4 shipped + #90 cell-tracking ping-pong filed
Phase A4 (multi-cell BSP iteration) ships in three commits (e6369e2,
493c5e5, 691493e — with revert 3add110 + reapply during visual
verification that proved A4 is not the cause of the issue surfaced).
1139 + 8 baseline maintained. 10 new unit tests pass. Wires retail's
CTransition::check_other_cells (acclient_2013_pseudo_c.txt:272717-272798)
into Transition.FindEnvCollisions.

Visual verification at the Holtburg inn vestibule surfaced a separate,
pre-existing M2 blocker (filed as #90): CellId ping-pongs between
outdoor 0xA9B40022 and indoor 0xA9B40164 on every wall push-back
because the push-back exits the indoor CellBSP volume, causing the
resolver to flip back to outdoor and bypass walls on outdoor ticks.
Indoor BSP results (Collided/Adjusted/Slid all firing) prove walls ARE
detected when the player is indoor; the aggregate "walls walk through"
appearance comes from CellId classification instability, not from
collision detection.

Bug reproduces fully with A4 reverted (launch-revert2.log captured 18
cell-id flips between 0xA9B40022 ↔ 0xA9B40164, 11 inside=True
building-transit events, 61 indoor-bsp queries firing the full
result distribution). A4 is correct and tested but dormant in
practice until #90 is fixed.

Updates:
  - docs/research/2026-05-20-phase-a4-shipped-cell-pingpong-finding.md (new)
  - docs/plans/2026-04-11-roadmap.md (A4 shipped row added)
  - CLAUDE.md (Indoor walking Phase A4 paragraph + next-step pointer
    to #90 with retail oracle anchor at acclient_2013_pseudo_c.txt:308742-308783)
  - docs/ISSUES.md (#90 filed, HIGH severity, M2-blocker)
  - docs/research/2026-05-21-open-items-pickup-prompt.md (landscape
    table updated — A4 closed, #90 promoted to top blocker)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 20:10:29 +02:00
Erik
fd9daddb37 docs(handoff): strategic pickup prompt for all 6 open items
After the 2026-05-21 session merged A1/A1.5/A1.6/A1.7 to main, six
discrete items remain. This doc maps them as a landscape rather than
single-phase:

- Collision (M2 critical path): A4 multi-cell BSP iteration → verify
  stairs → A2 PHSP inversion → A3 synthesis removal
- Rendering (M7 polish): indoor lighting + spotlight-projection bugs

The recommended order is A4 first (biggest user payoff, unblocks A3),
then stairs verification, A2 + A3 paired, lighting in a separate
session. A3 must NOT ship before A4 — that's the Bug A regression
from 2026-05-20.

Includes a pasteable session-start prompt that the user can box into
a fresh Claude Code session.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 15:10:23 +02:00
Erik
f80b53763f docs(handoff): update pickup prompt to point at main + fresh worktree
After merging the 2026-05-21 session into main (56d2b5e), update the
pickup prompt so the next session starts from main and creates its
own worktree for the A4 work — not the now-merged lucid-goldberg
branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 15:05:07 +02:00
Erik
56d2b5e4a1 docs(physics): handoff for 2026-05-21 collision-fix session
Captures everything that shipped in the session — A1, A1.5, A1.6,
A1.7 plus the walk-miss probe spike — and what's still open:

- A4 (multi-cell BSP iteration) — the next big architectural fix,
  closes the "walls walk-through-able in vestibule cells" gap
- A2 (PHSP inversion) — small fix, but only meaningful paired with A3
- A3 (synthesis removal) — needs A4 in place first to avoid
  reverting back to Bug A's free-fall regression
- Lighting bugs (indoor lighting + spotlight projection) — M7 polish,
  separate session

Includes per-fix commit SHAs, code anchors, retail decomp anchors,
probe + launch reference, anti-patterns, and a fresh-session pickup
prompt for boxing into Claude Code.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 14:42:47 +02:00
Erik
bb1e919ef2 docs(physics): spec + plan + findings for ISSUES #83 walk-miss probe spike
Three docs from the indoor walk-miss probe spike landed in commits
27c7284..a2e7a87:

- Spec: design of the [walk-miss] + [floor-polys] diagnostic emissions
  with the H1/H2/H3 disambiguation matrix.
- Plan: 3-task TDD implementation plan (flag, aggregator, emissions).
- Findings: live-capture analysis showing H3 (walkable_hits_sphere /
  adjust_sphere_to_plane synthesis rejection) is the dominant defect.
  817 of 876 ground-contact misses (93%) cluster at dz~0.48 m, while
  the 7 HITs all sit at dz~0.46 m — a 2 cm boundary between working
  and broken that points at the sphere-overlap math, not the probe
  distance. H1 (multi-cell iteration missing) is real but only 3%
  of misses, secondary. H2 (probe distance) ruled out.

Next step: line-by-line decomp comparison of FindWalkableInternal /
walkable_hits_sphere / adjust_sphere_to_plane against retail at
acclient_2013_pseudo_c.txt:322032 / :323006 / :326793, then design
the fix in a follow-up session.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 11:00:11 +02:00
Erik
d258334573 docs(handoff): pickup prompt for indoor walking doorway investigation
Companion to the Bug A wrong-scope handoff (35c266a). Provides the
boxed copy-paste prompt for a fresh session + quick reference for the
user and the helper:
- Branch state + KEEP/REMOVE recommendation
- Anti-patterns to avoid (don't repeat Bug A, validate risks with
  probe data, stop at three failed verifications)
- Code anchors for Mechanisms A/B/C in our code
- Retail decomp anchors for the doorway investigation
- Probe + diagnostic env var menu
- 5-scenario visual verification list
- Launch command with UTF-8 conversion step

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 09:55:47 +02:00
Erik
35c266a800 docs(handoff): indoor walking Bug A wrong-scope handoff
Bug B (indoor BSP world-origin fix) shipped today at de8ffde.
Bug A (delete per-frame walkable-plane synthesis) attempted and
reverted at 0a7ce8f. Real bug is deeper than scoped:

Indoor cell floor polys don't cover the player's full XY range when
crossing thresholds (doorways). Step-down probes miss past the floor
edge, Mechanism C (post-OK step-down) can't catch the player,
ContactPlane invalidates, gravity pulls them through the void.

We have all three retail CP retention mechanisms (A, B, C). The
defect is geometry, not retention. Either dat-decoder missing some
floor polys, or cell-transition timing too late, or some retail
mechanism we haven't traced.

Handoff includes:
- State of every commit on this branch + KEEP/REMOVE recommendation
- Bug B evidence and recommendation to ship to main
- Bug A failure analysis with probe data
- Mechanisms A/B/C location in our code vs retail decomp anchors
- 5 prioritized investigation targets for fresh session
- Anti-patterns to avoid (don't repeat Bug A approach)
- Lessons learned (probe-first discipline, risk-as-falsification,
  3-fails-in-a-session stop signal, Matrix4x4.Decompose idiom,
  binary-timestamp paranoia)

Recommendation: merge Bug B alone, leave the rest for fresh session.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 09:38:13 +02:00
Erik
7034be9294 docs(handoff): ContactPlane retention investigation pickup prompt
Self-contained fresh-session prompt that points at the BSP-port
shipped-handoff, summarizes the foundation work to keep vs delete,
notes the retail decomp anchors for CTransition::transitional_insert /
last_known_contact_plane, and includes the session-lesson reminder:
probe-first, design-second.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 07:06:11 +02:00
Erik
c6b3fd6ebf docs: indoor walkable-plane BSP port partial-ship handoff
Foundation work (6 commits ff548b9..f845b22) landed but visual
verification 2026-05-19 FAILED to fix the user-reported indoor bugs.
Documenting the deeper diagnosis + the next phase target without
reverting the foundation work.

What landed (kept):
- BSPQuery.FindWalkableInternal gained ref ushort hitPolyId (Task 1).
- New public BSPQuery.FindWalkableSphere wrapper over the existing
  retail-faithful walkable finder (Task 2).
- Transition.TryFindIndoorWalkablePlane refactored through it,
  PointInPolygonXY deleted (Task 3).
- [indoor-walkable] runtime-toggleable probe (Task 4).
- 5 new tests + 9 updated existing tests, all green; build clean.

What didn't fix: cellar descent FAIL, 2nd-floor walking FAIL
(intermittent falling-stuck), single-floor cottage REGRESSION (was
stable, now intermittent falling-stuck), phantom collisions PERSIST.

Probe evidence: 1443 MISS / 2 HIT over 1445 calls. Smoking gun:
foot-sphere-tangent-to-floor case fails PolygonHitsSpherePrecise's
|dist| > radius - epsilon check by ~0.0002. The BSP walker is
correct; the caller (TryFindIndoorWalkablePlane) is misusing it.

Root cause (deeper than originally diagnosed): TryFindIndoorWalkablePlane
exists only as a Phase 2 commit eb0f772 stop-gap. Retail doesn't
synthesize a ContactPlane per frame — retail RETAINS the previous
frame's plane when the BSP says no collision. Retail's find_walkable
only runs inside step_sphere_down (a sweep), never as a standing-still
query.

Next phase target: port retail's ContactPlane retention so the
resolver retains state across frames. Likely eliminates the per-frame
TryFindIndoorWalkablePlane call entirely. Foundation work (BSP walker
+ probe + tests) remains useful regardless.

ISSUES #83 remains OPEN with the deeper diagnosis.
Roadmap header updated to reflect partial-ship status.
Handoff at docs/research/2026-05-19-indoor-walkable-plane-bsp-port-shipped-handoff.md.

Spec: docs/superpowers/specs/2026-05-19-indoor-walkable-plane-bsp-port-design.md
Plan: docs/superpowers/plans/2026-05-19-indoor-walkable-plane-bsp-port.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 07:03:14 +02:00
Erik
d942ff73c0 docs(handoff): retarget pickup prompt — indoor/collision/physics/dungeons only
User explicitly redirected the next-phase track: no M2 (kill-a-drudge), stay
on indoor walking issues, collision, physics, and dungeons. Update the
pickup prompt to reflect this:

- Drop M2 from the candidate list entirely.
- Add #83 (walking up stairs) as the recommended next phase — pure
  indoor/physics, unblocks both multi-floor cottages AND dungeons.
- Add a "dungeon stress test" candidate (Path B) — verify Phase 2's
  portal traversal works on multi-cell indoor spaces.
- Move indoor lighting from "recommended" to Path E with a note that it
  depends on stairs (#83) landing first to be testable.
- Update the helper section with concrete file pointers per path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 19:52:17 +02:00
Erik
a5d6bb3536 docs(handoff): indoor walking Phase 2 pickup prompt for fresh session
After merging Phase 1 + Phase 2 to main at 1af49b7, file a pickup prompt
that orients a fresh Claude Code session: what shipped, what's open, the
four ranked candidate next phases (indoor lighting / M2 / #88 vibration /
triage), and key file pointers for whichever path is chosen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 19:35:47 +02:00
Erik
1af49b710e Merge branch 'claude/competent-robinson-dec1f4' — Indoor walking Phase 1 + Phase 2
Cluster A (Indoor walking Phase 1 — BSP cluster):
- WorldPicker cell-BSP occlusion → #86 closed
- CellId promotion via AABB containment (partial Phase D fix)
- Diagnostic infrastructure: [indoor-bsp], [cell-cache] probes

Indoor walking Phase 2 (Portal-based cell tracking):
- CellBSP + Portals wired into CellPhysics
- CellTransit static class: FindTransitCellsSphere + AddAllOutsideCells + FindCellList
- ResolveCellId rename + sphereRadius plumbing
- BuildingPhysics + CheckBuildingTransit (outdoor→indoor entry)
- Foot-sphere center fix (made portal tracking actually work in production)
- Indoor walkable-plane synthesis (closes the falling-stuck bug)

Closes ISSUES.md #84, #85, #86, #87.
Files new issues #88 (indoor object vibration) + #89 (port SphereIntersectsCellBsp).

Spec: docs/superpowers/specs/2026-05-19-indoor-portal-cell-tracking-design.md
Handoff: docs/research/2026-05-19-indoor-walking-phase2-shipped-handoff.md
2026-05-19 19:34:13 +02:00
Erik
a9c74d153a docs(phase): Indoor walking Phase 2 — Portal-based cell tracking shipped
Closes ISSUES.md #87 + #85 + the remaining wall-pass-through portion of
#84 (fully closes #84). Portal-graph cell traversal replaces Phase D's
AABB containment. Walking through doors promotes/demotes CellId correctly
via portal traversal; walls block from inside indoor cells; indoor walkable
plane is synthesized from the cell's floor poly so the resolver tracks
walkability correctly during indoor movement.

Files two new issues: #88 (indoor static objects vibrate — pre-existing,
spotted during Phase 2 testing) and #89 (BSPQuery.SphereIntersectsCellBsp
— follow-up to make CheckBuildingTransit retail-faithful; currently uses
radius-less PointInsideCellBsp as a documented approximation).

ISSUES.md: #87, #85, #84 moved to DONE. #88 + #89 filed.
Roadmap: Indoor walking Phase 2 added to shipped table.
CLAUDE.md: recent-phase paragraph updated to reflect Phase 2 shipped.
New handoff: docs/research/2026-05-19-indoor-walking-phase2-shipped-handoff.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 19:31:22 +02:00
Erik
f0900ebe12 docs(phase): Cluster A — partial ship + handoff
Cluster A's investigation pinned #86 (picker) as structural and closed
it (Phase B). #84 and #85 both pinned on missing indoor cell tracking;
Phase D promoted CellId via AABB containment which un-stuck the
spawn-in-building case (closes #84 partially) but proved too tight for
threshold/doorway cells to keep CellId indoor during normal walking.
The proper fix is retail's portal-based cell traversal; filed as a
new ISSUES.md issue (see body) for the follow-up phase. Phase E
diagnostic infrastructure ([cell-cache] + extended [indoor-bsp]) stays
in place as scaffolding for that work.

ISSUES.md: #86 → Recently closed. #84 status updated to PARTIAL with
resolution paragraph. #85 status update note added. New issue #87 filed
for portal-based indoor cell tracking.

Roadmap: Cluster A added to Recently shipped with partial-ship note.
Forward entry added for the portal-traversal follow-up under Phase G.

CLAUDE.md: current-phase paragraph updated to reflect Cluster A partial
ship. Next phase deferred to Claude's choice in a future session.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 16:12:24 +02:00
Erik
eeb45e16e3 docs: file #87 — retire WB fork band-aid via geom-only API
Phase 2's one-line WB patch (Setup-prefix guard at ObjectMeshManager.cs:1230)
fixed the symptom but is structurally a band-aid. CLAUDE.md's
no-workarounds rule says we should retire it.

The proper fix is switching our EnvCell rendering from the
general-purpose PrepareMeshDataAsync entry point (which iterates
static-object parts + emitters we don't need + triggers the buggy
TryGet<Setup> call) to WB's narrower PrepareEnvCellGeomMeshDataAsync
API at ObjectMeshManager.cs:386. That function only builds cell
room mesh — which is the only thing we use WB for at the cell
level. Static objects are already hydrated separately, particle
scripts already run via our own EntityScriptActivator.

#87 is the issue tracking that refactor. When it lands the WB fork
returns to pristine state (no acdream-specific commits on the
acdream branch for this file).

Handoff doc updated to flag the patch as a known band-aid pending #87.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 14:31:14 +02:00
Erik
b04ad448fa docs(handoff): indoor follow-up brief + fresh-session kickoff prompt
After Phase 1+2 (indoor cell rendering — missing floors fixed),
9 follow-up issues (#78-#86 in docs/ISSUES.md) need their own
phases. This handoff doc gives the next session everything it needs
to start cold: probe infrastructure status, issue cluster groupings,
suggested phase order, and the verification approach that worked
for Phase 1+2.

Companion prompt file is a self-contained kickoff that can be pasted
into a fresh Claude Code session to start work on the cluster.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 13:36:18 +02:00
Erik
73288657fd docs(research): Phase 2 verification — floor renders, fix landed
User visually confirmed floors render in Holtburg Inn after the WB
TryGet<Setup> guard. Probe re-capture: 0 [wb-error] lines (was 385),
0 NULL_RESULT (was 55), Holtburg 123/123 cells complete (was 97/123).

Documents the nine pre-existing indoor bugs the user observed during
verification (see-through floor, indoor collision, stairs, walls,
clicking, indoor lighting artifacts, stabs-don't-react-to-atmospheric-
lighting, slope terrain lighting). All pre-existing; filed for follow-up
phases via docs/ISSUES.md.

Phase 2 complete.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 13:12:53 +02:00
Erik
b838eccb38 feat(wb): ConsoleErrorLogger + cause report — H1 swallowed-exception confirmed
Phase 2 diagnostic chain identified the EXACT cause of 26/123 Holtburg
cells silently failing in WB's PrepareEnvCellMeshData:
ArgumentOutOfRangeException thrown from Setup.Unpack inside
DatReaderWriter when WB calls TryGet<Setup>(stab.Id, ...) on a stab id
whose prefix is GfxObj (0x01xxxxxx), not Setup (0x02xxxxxx).
DatReaderWriter finds the file in Portal's tree (GfxObjs and Setups
share tree-lookups), attempts to parse GfxObj bytes as Setup format,
throws OOR. Exception bubbles to PrepareMeshData's outer try/catch
which silently swallows + returns null. Entire cell fails to upload.

This commit lands the diagnostic infrastructure that surfaced the bug:

- WbMeshAdapter: replaced NullLogger<ObjectMeshManager> with a small
  Console-backed ConsoleErrorLogger<T> private class. Filters to
  LogLevel.Error+. WB's existing _logger.LogError(ex, ...) at the
  swallow site now writes [wb-error] lines with type + message + top 5
  stack frames. Bridges WB's intentional log point to acdream's console.
- WbMeshAdapter: extended [indoor-upload] NULL_RESULT probe with
  reader-divergence diagnostic (ourCellDb.TryGet, wbResolveId.Count,
  wbSelectedType, wbDbIsPortal, wbDbTryGet<EnvCell>, hadRenderData).
  Made it possible to rule out cache-hits and reader-divergence as
  causes before identifying the real one.
- Cause report at docs/research/2026-05-19-indoor-cell-rendering-cause.md
  documents the full chain: 55 ArgumentOutOfRangeException stack traces
  captured in one launch, all from PrepareEnvCellMeshData line 1223.

The fix itself (1-line guard at WB's TryGet<Setup> call site) is applied
to references/WorldBuilder/.../ObjectMeshManager.cs — which is a git
submodule. Will be committed separately to the WB submodule after
visual verification.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 13:00:18 +02:00
Erik
25f009140a docs(research): Phase 1 indoor probe capture — H1 confirmed
Captured at Holtburg landblock 0xA9B4 with ACDREAM_PROBE_INDOOR_ALL=1.

Result: 123 EnvCells in Holtburg get [indoor-upload] requested but
ONLY 97 get a matching [indoor-upload] completed. 26 cells silently
fail in WB's PrepareEnvCellMeshData / PrepareMeshData. The first
interior cell 0xA9B40100 — likely the inn entry or another major
building anchor — is among the failures, exactly matching the
user's "floor missing" symptom.

Other hypotheses ruled out:
- H2 (empty batches): completed cells have cellGeomVerts=14-86.
- H3 (cull bug): walk probe confirms cells pass all visibility filters.
- H4 (double-spawn): partCount values match expected SetupParts.
- H5 (transform double-apply): xform probe shows composedT==meshRefT;
  no double-apply.
- H6 (MeshRefs structure): lookup probe shows isSetup=True and
  partsHit≈partCount for uploaded cells.

Phase 2 plan: wrap PrepareMeshDataAsync with our own catch-and-log
in WbMeshAdapter so the swallowed exception (most likely cause of
the 26 silent failures, per WB ObjectMeshManager.cs:589) becomes
visible. Once we know the actual failure reason, target the fix.

Also flags IsEnvCellId false-positives on GfxObj IDs whose lower 24
bits ≥ 0x0100 — tightening recommended in Phase 2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 12:03:25 +02:00
Erik
a54cd7bef6 fix(lighting): match retail indoor ambient (0.20 neutral, not 0.10/0.09/0.08 warm)
Indoor cells rendered "almost black" because the hardcoded ambient at
GameWindow.cs:8342-8345 was an early-2026 guess (0.10, 0.09, 0.08 — half
retail brightness, warm-tinted) rather than the retail value. The named
retail decomp (acclient.pdb, Sept 2013 EoR build) shows
CellManager::ChangePosition @ 0x004559B0 calls
SmartBox::SetWorldAmbientLight(0.2f, 0xFFFFFFFF) whenever the player's
CObjCell::seen_outside flag is 0 — a flat 0.20 white floor, not a
dungeon-tone warm color.

Investigation also confirmed:
- EnvCell.dat does NOT carry inline lights — CEnvCell::UnPack reads
  numVisibleCells where Binary Ninja's heuristic decomp inferred
  "num_lights". Retail's CObjCell.light_list is populated at runtime via
  add_light() calls from neighbouring cell light registrations + per-cell
  static-object Setup.Lights, NOT from the dat byte stream.
- Setup.Lights from indoor static objects (entity.SourceGfxObjOrSetupId
  prefix 0x02xxxxxx) DO flow through LightInfoLoader.Load (line 5765)
  and reach LightManager via LightingHookSink. The wire is intact; the
  per-frame Tick + UBO upload chain (line 6865-6867) is intact.
- Retail's particle system does NOT emit lights from particles themselves.
  The light comes from the owning Setup's LightInfo records.

Pre-existing failures in DispatcherToMovementIntegrationTests, BSPStepUpTests,
and MotionInterpreterTests are on the branch already and unrelated to this
change (verified by stashing + retesting).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 10:14:25 +02:00
Erik
677266d477 docs(research): handoff for issue #77 — close-range auto-walk + pickup overshoot
Self-contained handoff doc for a follow-up focused session that fixes #77.
Captures: the two bugs (NPC at walking range never auto-walks; pickup at
walking range overshoots and snaps back), the trace evidence from Step 2's
verification run (cmd=0x0005 speed=-1.84 from ACE), four ranked
root-cause hypotheses (H1 missing BeginServerAutoWalk fire / H2
walk-run-threshold misclassification / H3 negative ForwardSpeed
sign-interpretation / H4 arrival predicate firing too early), the
reproduction recipe with ACDREAM_PROBE_AUTOWALK=1, acceptance criteria,
and the "don't workaround, fix root cause" guardrail.

The auto-walk diagnostic infrastructure already exists from Phase B.6
work — the next session just turns it on and reads the trace.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 08:38:31 +02:00
Erik
2950cd5740 docs: rewrite handoff — M2 redirected from "kill a drudge" to indoor walkability
User chose fundamentals-first after M1 landed: indoor walking is
untested (M1 only verified outdoor + doorway), indoor lighting is
broken, camera clips into walls indoors. Better to fix indoor
foundations before stacking combat on top.

Three sub-phases proposed for the new M2:
  1. Camera correctness (~1 day)
  2. Indoor collision audit (~1 week)
  3. Indoor lighting basics (~1-2 weeks)

Combat ("Kill a drudge") slides to M3.

Next session opens with superpowers:brainstorming to scope the
demo scenario + agree on sub-phase boundaries + update the
milestones doc + CLAUDE.md to formalize the reorder.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 21:01:30 +02:00
Erik
5d79dd3b88 docs: session handoff 2026-05-16
Captures M1 landing, Phase B.6 architectural details, new rules
(no-workarounds, no-demo-videos, graceful-shutdown), M2 next steps,
and test baselines for fresh-session pickup.

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