Commit graph

312 commits

Author SHA1 Message Date
Erik
5be784eee3 file: issue #95 — dungeon portal-graph visibility blowup
Filed during A6.P1 scen5 capture (Town Network as substitute for the
nonexistent Holtburg Sewer). After portal teleport, visibleCells per
cell explodes from ~4 to 135-145, with cells from multiple disconnected
landblocks loaded simultaneously — direct cause of the user-observed
"see through walls / other dungeons rendering" failure across all
portal-accessed dungeons.

This is what "dungeons are broken" means as a coherent failure mode.
Scen6-9 (sewer corridor/chamber/stair) as originally scripted couldn't
have produced clean physics-only captures because the dungeon would
have been visually unusable from the moment of portal entry. The A6.P1
scenario script was written before the visibility bug was characterized.

Evidence is in scen5's acdream.log (already committed at 35d5c58).
Scen6-9 are not captured — the visibility bug blocks the scenarios.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 21:08:48 +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
0bdd5c7fca docs(plan): Phase A6.P1 — cdb probe spike implementation plan
15-task TDD plan covering the three pieces of A6.P1:
  Phase A — Build the [push-back] acdream probe (Tasks 1-9):
    toggle + 3 helpers + 3 emission sites in BSPQuery/Transition,
    DebugVM mirror, CLAUDE.md env-var docs.
  Phase B — Build the cdb infrastructure (Tasks 10-12):
    7-BP cdb script, PowerShell runner, README.
  Phase C — Execute 9 captures + findings stub (Tasks 13-15):
    PDB-match verify, capture dir + findings stub, scenario captures.

API surface verified against current code: ResolvedPolygon has no
Id property (probe omits poly attribution; cross-ref via time-
adjacent [push-back-cell] line). CheckOtherCells locals are
sp.CheckCellId + cellId + result (verified at TransitionTypes.cs
lines 1418-1473). SpherePath has Collide/InsertType/WalkInterp,
ObjectInfo has State (verified).

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

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 18:07:37 +02:00
Erik
f9214433c3 docs(spec): Phase A6 — Indoor physics fidelity (cdb-driven) — design
Brainstormed + approved 2026-05-21 for M1.5 milestone work. Designs
the cdb probe spike methodology (7 retail breakpoints + new
[push-back] probe) to capture retail's per-tick BSP collision
response state at 9 indoor scenarios (4 buildings + 5 dungeon sites)
and compare against acdream. Working hypothesis: BSPQuery.AdjustSphereToPlane
or its callers over-correct vs retail, producing the family of
indoor symptoms (walls walk through, ping-pong, vibration, multi-Z
falling) plus driving the existing #90 + TryFindIndoorWalkablePlane
workarounds. A6 ships in 4 slices: P1 probe spike, P2 analysis,
P3 surgical fixes, P4 workaround removal + acceptance.

Phase O (DatPath Unification) pre-empted M1.5 and shipped 2026-05-21;
A6 resumes from Phase O state. Phase O only touched rendering/dat
code; indoor physics design is unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 17:57:48 +02:00
Erik
2256006cb7 ship(O): Phase O — DatPath Unification — SHIPPED
ONE thing touches the DATs. WB code lives in our repo:
- src/AcDream.Core/Rendering/Wb/ — pure helpers (5 files, ~782 LOC)
- src/AcDream.App/Rendering/Wb/ — GL infra + mesh pipeline (~27 files, ~7K LOC)

Project references to WorldBuilder.Shared + Chorizite.OpenGLSDLBackend
dropped from AcDream.App.csproj and AcDream.Core.csproj.
references/WorldBuilder/ remains in-tree as read-reference only.

DefaultDatReaderWriter eliminated; DatCollection is the only dat reader.
WbMeshAdapter consumes our DatCollection via DatCollectionAdapter
(O-D7 fallback adapter; ObjectMeshManager has 26 _dats.X call sites,
exceeding the 20 refactor threshold).

Visual side-by-side passed: Holtburg town, inn interior, dungeon all
render identically to pre-O.

Doc updates:
- CLAUDE.md: rewrote WB integration cribs to point at extracted code.
  Code Structure Rules rule 2 updated to remove stale seam names.
  "Currently working toward" flipped from Phase O to M1.5 resumption.
- docs/architecture/worldbuilder-inventory.md: Phase O banner added.
  Status/integration model updated to post-O ownership. Workflow
  section updated to reference our extracted tree, not WB project ref.
- docs/plans/2026-04-11-roadmap.md: Phase O moved to shipped table.
  Phase O "ahead" block collapsed to SHIPPED note. M1.5 block updated
  to ACTIVE (Phase O shipped; resuming from 2026-05-20 baseline).
- docs/plans/2026-05-12-milestones.md: M1.5 heading updated to ACTIVE;
  Phase O ship writeup prepended to the M1.5 block.

Phase O ship closes Tasks O-T1..O-T7 shipped across this session.
Specs + audit + plan: docs/superpowers/{specs,plans}/2026-05-21-phase-o-*

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 17:41:15 +02:00
Erik
ff4164247a plan(O): Phase O implementation plan + spec layer-placement fix
Plan: 7 tasks decomposing spec T2..T9 with bite-sized TDD-style steps,
exact file paths, commit-message templates, and a T4 safety-check
branch (refactor in place if ObjectMeshManager._dats call sites <=20;
fall back to thin adapter otherwise).

Spec fix: §4.1 mesh-pipeline files now correctly placed under
src/AcDream.App/Rendering/Wb/ instead of Core (ObjectMeshManager uses
Silk.NET.OpenGL types from Managed* wrappers, and CLAUDE.md forbids
Core depending on GL). §4.2's layer split (TextureHelpers in Core,
rest in App) was already correct.

Plan task order: T2 (setup) -> T5 (Core helpers, lowest risk) ->
T3 (App GL infra) -> T4 (App mesh pipeline + dat-shim) -> T7 (drop
refs + cleanup) -> T8 (visual verification) -> T9 (ship). T5 moved
earlier than spec order to validate the namespace migration flow on
small-blast-radius files before the load-bearing T4.

Self-review: all 12 spec decisions (O-D1..O-D12) mapped to plan tasks;
placeholders intentional + explained (MIT license body fetched at T2
step 4; commit-message parameters filled at task close).

Spec: docs/superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 14:49:19 +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
0d85fe1f10 plan(O): Phase O — DatPath Unification — filed + active (pre-empts M1.5)
Phase O extracts the WB pieces we actually use (mesh pipeline, texture
decode, GL state, scenery, terrain blending, EnvCell/portal decode —
roughly 3-5K LOC) into src/AcDream.Core/Rendering/Wb/, swaps their
dat dependency from DefaultDatReaderWriter to our DatCollection, and
drops the WorldBuilder.Shared + Chorizite.OpenGLSDLBackend project
references. WB stays in references/ as a read reference, not as a
project dependency. MIT attribution in NOTICE.md.

Tagline: ONE thing touches the DATs.

Discipline: verbatim copy first, no "improvements" while extracting.
Refactors land in follow-up phases. Out of scope: re-porting from
retail decomp; perf optimization; API cleanup.

User direction 2026-05-21: pre-empts M1.5. M1.5 paused at its
2026-05-20 baseline; A6/A7 don't touch dat infrastructure so no
rework needed when it resumes.

Files:
- docs/superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md (new, full spec)
- docs/plans/2026-04-11-roadmap.md (Phase O block inserted before M1.5; M1.5 marked PAUSED)
- CLAUDE.md (Currently-working-toward line updated; M1.5 block marked paused)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 13:59:33 +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
a8a0366eb1 docs(plan): Phase A4 — multi-cell BSP implementation plan
Five tasks: pre-flight baseline → CellTransit.FindCellSet (3 tests + impl
+ commit) → Transition.CheckOtherCells (6 tests + impl + commit) →
FindEnvCollisions wire-up (1 integration test + commit) → visual verify
at Holtburg inn vestibule → roadmap + handoff doc update.

Each implementation task is TDD: write failing tests, verify red,
implement, verify green, run baseline, commit. Three commits land
A4 in the codebase, fourth commit lands the docs.

Spec: docs/superpowers/specs/2026-05-20-phase-a4-multi-cell-bsp-design.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 15:59:54 +02:00
Erik
b100d54829 docs(spec): Phase A4 — multi-cell BSP iteration design
Port retail's CTransition::check_other_cells (acclient_2013_pseudo_c.txt
:272717-272798) into Transition.FindEnvCollisions so the foot-sphere
sees walls in EVERY cell it overlaps, not just the one cell the player's
center is in. Closes the Holtburg inn vestibule wall walk-through
(cell 0xA9B40164 has only 4 polys; adjacent 0xA9B40157 has 23 walls
that are never queried today).

Architecture: new CellTransit.FindCellSet overload (preserves the
candidate HashSet that FindCellList currently discards), new private
Transition.CheckOtherCells method (direct port of the retail loop),
one wire-up in FindEnvCollisions between the existing primary-cell BSP
return and the synthesis fall-through. ~380 LOC total.

Out of scope: FindObjCollisions (already landblock-radius broadphased),
synthesis multi-cell search (A3's job), var_4c re-target (defer).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 15:50:03 +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
5f2b545979 fix(physics): skip mesh-AABB-fallback cylinder for landblock stabs
ISSUES #83 Phase A1. Landblock stabs (entity.Id 0xC0XXYY00+n per
LandblockLoader.cs:55) were being registered with TWO collision
shadows: the correct per-part BSP at `entity.Id*256 + partIdx`, AND a
redundant mesh-AABB-fallback cylinder at `entity.Id`. The fallback
clamped to 1.5m radius, centered at the building's mesh origin,
producing user-reported "thin air" collisions inside cottages and
within 2m of building exteriors.

The fallback was originally designed for canopy-only-BSP procedural
scenery (0x80XXYY00+n) — trees whose BSP covers the canopy but not
the trunk. Landblock stabs have full BSP coverage and don't need it.

Probe evidence (launch-thinair capture):
- 0xC0A9B479 cylinder fallback (Holtburg cottage): 104 hits in a
  short capture session, all inside the cottage main room
  (cell=0xA9B4013F), ~2m from the building's mesh origin.
- 0xA9B47900 BSP (the actual cottage walls): 52 legitimate hits.

Fix: one new bool _isLandblockStab + one clause in the existing
mesh-AABB-fallback gate.

Spec: docs/superpowers/specs/2026-05-21-cylinder-fallback-dedup-design.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 11:42:13 +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
686f27f227 docs(plan): remove per-frame indoor walkable-plane synthesis (Bug A)
Six-task plan for Bug A slice (spec 2026-05-20):
1. Replace synthesis call site with return TransitionState.OK
2. Delete Transition.TryFindIndoorWalkablePlane method + constant
3. Delete IndoorWalkablePlaneTests.cs + TransitionTypesTests.cs
4. Run physics suite, confirm baseline holds
5. Single commit per spec
6. User visual verification (5 scenarios)

Net delta: ~-480 lines. BSPQuery.FindWalkableSphere + its 5 unit tests
retained as the underlying retail-faithful walkable-finder API.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 08:53:58 +02:00
Erik
3bec18f0e4 docs(spec): remove per-frame indoor walkable-plane synthesis (Bug A)
Slice 2 of 2 in the indoor ContactPlane retention phase. Deletes
Transition.TryFindIndoorWalkablePlane + the per-frame synthesis call
+ outdoor-terrain fallthrough + 9 tests. Replaces with bare
return TransitionState.OK; matching retail's BSPTREE::find_collisions
OK path (acclient_2013_pseudo_c.txt:323938). ContactPlane is retained
via the per-tick seed at PhysicsEngine.ResolveWithTransition:583
(init_contact_plane equivalent) or refreshed by BSP Path 3 / Path 4.

Predecessor: de8ffde (Bug B, BSP world-origin fix).
Evidence: launch-cp-probe-postfix-v2.log shows 3150 MISS / 3154
indoor-walkable calls (99.87% miss rate) after Bug B, with user-visible
"stuck falling when brushing upper floor edge" symptom unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 08:32:17 +02:00
Erik
56816fcbe4 docs(plan): indoor BSP world-origin fix implementation plan
Five-task plan for Bug B slice (spec 2026-05-20). Tasks:
1. Regression test in BSPQueryTests.cs (BSPQuery API contract)
2. Apply Decompose + arg-pass at TransitionTypes.cs:1442
3. Run physics suite, confirm 8-failure baseline holds
4. Commit
5. User visual + probe-equivalence verification

Bug A explicitly deferred to a future slice.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 07:45:24 +02:00
Erik
865634f450 docs(spec): indoor BSP world-origin / world-rotation fix (Bug B)
Single-call-site defect in TransitionTypes.cs:1442 — the indoor cell
BSP query invokes BSPQuery.FindCollisions without passing the cell's
world rotation or world origin. Path 3 step-down + Path 4 land write
ContactPlanes with D ≈ 0 instead of the cell's world floor Z.
320 corrupt CP writes per Holtburg session per the [cp-write] probe
capture 2026-05-20.

Fix: decompose cellPhysics.WorldTransform once, pass rotation +
translation. Mirrors the existing correct pattern at :1808 (object
BSP via FindObjCollisions).

This is slice 1 of 2 for the indoor ContactPlane retention phase.
Slice 2 (Bug A — TryFindIndoorWalkablePlane removal) deferred
pending Bug B retest.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 07:38:27 +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
e62d076f33 docs(plan): indoor walkable-plane BSP port — implementation plan
Five tasks (TDD throughout):
1. Extend FindWalkableInternal signature with ref ushort hitPolyId
   (mechanical, two existing callers pass discards).
2. Add BSPQuery.FindWalkableSphere wrapper + 4 unit tests
   (two-floors-foot-between, only-upper-floor-foot-above, no-walkable-
   in-range, steep-poly-rejected).
3. Refactor TryFindIndoorWalkablePlane through BSPQuery.FindWalkableSphere
   with sphereRadius thread + WalkableAllowance save/restore, delete
   PointInPolygonXY, update single callsite, integration test for
   two-overlapping-floors + allowance-preservation.
4. Add [indoor-walkable] probe line on existing PhysicsDiagnostics flag.
5. Visual verification by user + roadmap + ISSUES #83 close.

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

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 21:18:11 +02:00
Erik
165f67add4 docs(spec): indoor walkable-plane BSP port — design
Brainstormed spec for resolving the cellar-descent / 2nd-floor /
invisible-obstacle indoor collision regressions reported post-Phase 2.

Root cause: Phase 2 commit eb0f772 introduced TryFindIndoorWalkablePlane
as a stop-gap walkable-plane synthesis when the indoor BSP returns OK.
Its body does a linear first-match XY scan over cellPhysics.Resolved with
no Z-proximity test, so multi-Z indoor geometry (cellars, 2nd floors,
balconies) collapses to wrong-floor selection. Walking UP stairs works
because step_up routes through DoStepDown → TransitionalInsert(5) →
BSPQuery.FindCollisions Path 3 (StepSphereDown) which already uses
FindWalkableInternal — the retail-faithful BSP walkable-finder. The
linear scan only fires in the OK-no-wall branch.

Fix: route TryFindIndoorWalkablePlane through the existing
FindWalkableInternal via a thin new BSPQuery.FindWalkableSphere wrapper.
Extends FindWalkableInternal's signature to expose the hit polyId
(dictionary key, since ResolvedPolygon doesn't carry its own id). Threads
the foot-sphere radius through TryFindIndoorWalkablePlane's signature
(was hardcoded to nothing — used the localFootCenter alone). Deletes
the now-dead PointInPolygonXY helper.

Awaiting user spec review before plan.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 21:08:49 +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
b282c69f28 docs(plan): Indoor portal-based cell tracking implementation plan
11-task plan: data wiring (PortalInfo + extend CellPhysics + extend
CacheCellStruct) → CellTransit port (FindTransitCellsSphere +
AddAllOutsideCells + FindCellList) → ResolveCellId integration (rename +
plumb sphereRadius + delete AABB containment) → BuildingPhysics for
outdoor→indoor → capture + docs.

Task 0 verifies DatReaderWriter exposes CellStruct.CellBSP and
LandBlockInfo.Buildings before any code touches them. The CellBSP
property name is the one known unknown.

Spec: docs/superpowers/specs/2026-05-19-indoor-portal-cell-tracking-design.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 16:40:03 +02:00
Erik
48f0b26f62 docs(spec): Indoor portal-based cell tracking design
Brainstormed spec for the follow-up to Cluster A: port retail's portal-graph
cell traversal to replace Phase D's AABB containment shortcut. Closes
ISSUES.md #87 and the remaining wall-collision parts of #84 + #85 — indoor
walking with walls that block from inside, walking through doors that
updates CellId.

Scope: all three transition types (indoor↔indoor, indoor↔outdoor,
outdoor→indoor). AABB containment deleted entirely; portal traversal is the
only path.

Key data references: docs/research/acclient_indoor_transitions_pseudocode.md
(2026-04-13) has the entire algorithm already documented from ACE source
cross-referenced against the retail header. BSPQuery.PointInsideCellBsp is
already wired (just unused).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 16:32:21 +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