Commit graph

19 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
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
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
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