docs: reconcile M1.5 tracking with git reality — G.3 dungeons SHIPPED, not "broken"

The milestones doc, roadmap, CLAUDE.md banner, and #138's ISSUES cross-ref had
drifted ~4 weeks behind shipped work — they still claimed "dungeons don't work
AT ALL / terrain-less landblocks unsupported (#133)", which sent this session
chasing a phantom door "regression" for an hour. Git reality (and tonight's live
dungeon session) refute it:

- #133 teleport-into-dungeon DONE (G.3a hold-until-hydrate FSM 7947d7a/2ce5e5c8/47ae237e);
  the terrain-less premise was refuted at design time (90786c19: dungeon LandBlock
  is flat-terrain, streams via the existing pipeline).
- #95 portal-graph visibility blowup RESOLVED (9.1M→~39k instances, a40c38e8).
- #137 dungeon collision CLOSED (0efa7ed2); doors #187/#188; A7 lighting #79/#93/#80/#154 CLOSED.
- #138 teleport-OUT ESSENTIALLY RESOLVED in code — the remaining M1.5 gap is one
  RECORDED end-to-end round-trip user gate, not broken code.

Also fixes a numbering trap: #138's body cited "#146/#147" for its collision
residuals, but those numbers are D.2b inventory-polish issues in this worktree;
the real residuals are #152 (portal-in walls, DONE 49d743f) + #151 (far-town
walls, FIXED 9743537). Sole live residual now surfaced: far-town teleport-OUT
arrival cascade (#145-residual, REOPENED — capture-harness-first).

Historical working-notes annotated [SUPERSEDED] rather than deleted.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-07-10 09:45:05 +02:00
parent 217a4bad69
commit 5e271dd719
4 changed files with 75 additions and 43 deletions

View file

@ -109,9 +109,11 @@ movement queries.
## Current state ## Current state
**Currently working toward: M1.5 — Indoor world feels right** (building/cellar demo **Currently working toward: M1.5 — Indoor world feels right** (building/cellar demo
DONE + gated; **#137 dungeon collision CLOSED, A7 dungeon lighting #79/#93 CLOSED DONE + gated; dungeons now navigable end-to-end — **#133/#137/#95/#79/#93/#80 all
2026-07-09** — two root causes fixed + user-reverified 2nd-floor case; REMAINING CLOSED** (A7 lighting fixed, user-reverified). REMAINING = one recorded dungeon
critical path = **#138 teleport-OUT**). One **user-report-driven round-trip user gate (**#138 ESSENTIALLY RESOLVED** in code — see
`docs/ISSUES.md`); sole live residual = far-town teleport-OUT arrival cascade
(**#145-residual, REOPENED** — capture-harness-first). One **user-report-driven
parity track** still interleaves at the issue level (NOT a milestone; see the M1.5 parity track** still interleaves at the issue level (NOT a milestone; see the M1.5
note in the milestones doc): **D.2b retail UI** (next: container-switching — note in the milestones doc): **D.2b retail UI** (next: container-switching —
`claude-memory/project_d2b_retail_ui.md`). The **R5 movement-manager arc is DONE** `claude-memory/project_d2b_retail_ui.md`). The **R5 movement-manager arc is DONE**

View file

@ -2064,9 +2064,11 @@ See divergence register **AP-59**.
**Status:** ✅ ESSENTIALLY RESOLVED — position-desync FIXED via #145; server-object re-hydrate **Status:** ✅ ESSENTIALLY RESOLVED — position-desync FIXED via #145; server-object re-hydrate
SHIPPED (`LandblockEntityRehydrator`, AP-48); avatar-vanish (symptom B) ACTUAL root found + SHIPPED (`LandblockEntityRehydrator`, AP-48); avatar-vanish (symptom B) ACTUAL root found +
FIXED + user-confirmed (`afd5f2a`, 2026-06-24 — skip the per-frame `RelocateEntity` while FIXED + user-confirmed (`afd5f2a`, 2026-06-24 — skip the per-frame `RelocateEntity` while
`PortalSpace`). Remaining collision residuals split out to **#146** (Holtburg portal-in `PortalSpace`). Remaining collision residuals split out to **#152** (Holtburg/building portal-in
building walls) + **#147** (Arwic far-town frame) — those are the live follow-ups, not #138 walls, DONE `49d743f`) + **#151** (Arwic far-town frame, FIXED `9743537`) — those are the live
itself. See the 2026-06-24 update at the bottom of this entry. follow-ups, not #138 itself. (⚠️ Numbering trap: #146/#147 in this file are unrelated D.2b
inventory-polish issues — see below — not the collision follow-ups; the correct numbers are
#151/#152.) See the 2026-06-24 update at the bottom of this entry.
**Severity:** MEDIUM (breaks the dungeon→outdoor transition; collision + visuals wrong after exit) **Severity:** MEDIUM (breaks the dungeon→outdoor transition; collision + visuals wrong after exit)
**Filed:** 2026-06-14 **Filed:** 2026-06-14
**Component:** streaming — dungeon collapse↔expand (the #133/#135 collapse) + teleport-arrival **Component:** streaming — dungeon collapse↔expand (the #133/#135 collapse) + teleport-arrival
@ -2113,7 +2115,7 @@ Both are the **entity-lifecycle/render path across a teleport**, NOT the streami
**Implementation (B — player vanish, candidate):** `GpuWorldState.RemoveLandblock` rescued persistent entities only from `_loaded`, silently dropping a persistent entity sitting in the **pending bucket** (the player is re-injected via `AppendLiveEntity` every frame; right after a teleport its landblock hasn't streamed yet → pending; if that landblock is then unloaded mid-churn the player was dropped → "vanishes after a couple round-trips"). Fix: rescue persistent pending entries too. Tests `RemoveLandblock_RescuesPersistentEntity_FromPendingBucket` (+ negative). This is a provable correctness fix for the "persistent ⇒ survives unload" invariant; it is the leading candidate for symptom B but needs user re-verification to confirm it is the complete cause. **Implementation (B — player vanish, candidate):** `GpuWorldState.RemoveLandblock` rescued persistent entities only from `_loaded`, silently dropping a persistent entity sitting in the **pending bucket** (the player is re-injected via `AppendLiveEntity` every frame; right after a teleport its landblock hasn't streamed yet → pending; if that landblock is then unloaded mid-churn the player was dropped → "vanishes after a couple round-trips"). Fix: rescue persistent pending entries too. Tests `RemoveLandblock_RescuesPersistentEntity_FromPendingBucket` (+ negative). This is a provable correctness fix for the "persistent ⇒ survives unload" invariant; it is the leading candidate for symptom B but needs user re-verification to confirm it is the complete cause.
**UPDATE 2026-06-24 — symptom B (avatar vanish) ACTUAL root found + FIXED (`afd5f2a`, user-confirmed + trace-verified):** the pending-bucket rescue above was necessary but NOT the whole cause. The real culprit: the per-frame avatar-sync (`GameWindow` ~:8018) calls `GpuWorldState.RelocateEntity` using the player controller's cell, which stays the **FROZEN SOURCE cell** until `PlaceTeleportArrival` materializes the destination. Mid-transit it dragged the avatar — which the teleport's `DrainRescued` re-inject had correctly placed at the destination center — back into the now-UNLOADED source landblock's pending bucket, where nothing recovers it (`RelocateEntity` only scans `_loaded`). Fix: skip the per-frame relocate while `_playerController.State == PortalSpace`. Verified by a new env-gated avatar-lifecycle probe (`ACDREAM_PROBE_ENT` / `EntityVanishProbe`): pre-fix `[ent] APPEND lb=0x0007FFFF(source) -> PENDING -> DRAWSET ABSENT` never recovered; post-fix every teleport goes `RESCUE -> ABSENT -> APPEND(destination) -> PRESENT` and stays drawn. **The remaining #138 collision residual split into #146 (Holtburg portal-in building walls) + #147 (Arwic far-town frame).** **UPDATE 2026-06-24 — symptom B (avatar vanish) ACTUAL root found + FIXED (`afd5f2a`, user-confirmed + trace-verified):** the pending-bucket rescue above was necessary but NOT the whole cause. The real culprit: the per-frame avatar-sync (`GameWindow` ~:8018) calls `GpuWorldState.RelocateEntity` using the player controller's cell, which stays the **FROZEN SOURCE cell** until `PlaceTeleportArrival` materializes the destination. Mid-transit it dragged the avatar — which the teleport's `DrainRescued` re-inject had correctly placed at the destination center — back into the now-UNLOADED source landblock's pending bucket, where nothing recovers it (`RelocateEntity` only scans `_loaded`). Fix: skip the per-frame relocate while `_playerController.State == PortalSpace`. Verified by a new env-gated avatar-lifecycle probe (`ACDREAM_PROBE_ENT` / `EntityVanishProbe`): pre-fix `[ent] APPEND lb=0x0007FFFF(source) -> PENDING -> DRAWSET ABSENT` never recovered; post-fix every teleport goes `RESCUE -> ABSENT -> APPEND(destination) -> PRESENT` and stays drawn. **The remaining #138 collision residual split into #152 (Holtburg/building portal-in walls, DONE `49d743f`) + #151 (Arwic far-town frame, FIXED `9743537`).** (#146/#147 in this file are unrelated D.2b inventory-polish issues.)
**Acceptance:** portal out of the 0x0007 dungeon → full outdoor world streams (trees/scenery present), **server objects (doors/NPCs/portals) render**, **own avatar renders across repeated round-trips**, collision works, position tracks (no avatar-vs-camera desync). **Acceptance:** portal out of the 0x0007 dungeon → full outdoor world streams (trees/scenery present), **server objects (doors/NPCs/portals) render**, **own avatar renders across repeated round-trips**, collision works, position tracks (no avatar-vs-camera desync).

View file

@ -7,6 +7,11 @@
## Current program: Phase W — Unified Cell Graph (UCG) ## Current program: Phase W — Unified Cell Graph (UCG)
**[SUPERSEDED — G.3 dungeon work shipped; M1.5 next step = #138 round-trip
gate + #145-residual. See the shipped table + `docs/plans/2026-05-12-milestones.md`
M1.5 section for current status. This section's W1/W2a history below is kept
for record.]**
**Pivot (2026-06-02).** The render-pipeline reset stalled because the indoor "world from **Pivot (2026-06-02).** The render-pipeline reset stalled because the indoor "world from
below" is a cell-**membership** disagreement between the render-side `CellVisibility` and below" is a cell-**membership** disagreement between the render-side `CellVisibility` and
the physics-side `ResolveCellId` — not any single draw gate (pixel-grounded evidence: the physics-side `ResolveCellId` — not any single draw gate (pixel-grounded evidence:
@ -99,6 +104,10 @@ W1 plan: [`docs/superpowers/plans/2026-06-02-unified-cell-graph-stage1.md`](../s
| Indoor walking Phase 2 — Portal-based cell tracking | 2026-05-19. Portal-graph traversal replaces Phase D's AABB containment. Six commits: `1969c55` CellBSP+Portals wired into CellPhysics; `aad6976` CellTransit.FindCellList + FindTransitCellsSphere + AddAllOutsideCells + ResolveCellId rename; `069534a` BuildingPhysics + CheckBuildingTransit for outdoor→indoor entry; `702b30a` code-review polish; `3ffe1e4` pass foot-sphere center to ResolveCellId (critical fix — was passing CheckPos instead of GlobalSphere[0].Origin, causing PointInsideCellBsp to return false at floor level); `eb0f772` TryFindIndoorWalkablePlane synthesizes walkable plane from cell floor poly so the resolver doesn't fall through to outdoor SampleTerrainWalkable. **Closes #87, #85, and the wall-pass-through portion of #84 (fully closes #84).** Files #88 (indoor static object vibration — pre-existing) and #89 (BSPQuery.SphereIntersectsCellBsp — approximation in CheckBuildingTransit). `[cell-transit]`, `[indoor-bsp]`, `[check-bldg]`, `[cell-cache]` probes stay in place. Handoff: [`docs/research/2026-05-19-indoor-walking-phase2-shipped-handoff.md`](../research/2026-05-19-indoor-walking-phase2-shipped-handoff.md). | Live ✓ | | Indoor walking Phase 2 — Portal-based cell tracking | 2026-05-19. Portal-graph traversal replaces Phase D's AABB containment. Six commits: `1969c55` CellBSP+Portals wired into CellPhysics; `aad6976` CellTransit.FindCellList + FindTransitCellsSphere + AddAllOutsideCells + ResolveCellId rename; `069534a` BuildingPhysics + CheckBuildingTransit for outdoor→indoor entry; `702b30a` code-review polish; `3ffe1e4` pass foot-sphere center to ResolveCellId (critical fix — was passing CheckPos instead of GlobalSphere[0].Origin, causing PointInsideCellBsp to return false at floor level); `eb0f772` TryFindIndoorWalkablePlane synthesizes walkable plane from cell floor poly so the resolver doesn't fall through to outdoor SampleTerrainWalkable. **Closes #87, #85, and the wall-pass-through portion of #84 (fully closes #84).** Files #88 (indoor static object vibration — pre-existing) and #89 (BSPQuery.SphereIntersectsCellBsp — approximation in CheckBuildingTransit). `[cell-transit]`, `[indoor-bsp]`, `[check-bldg]`, `[cell-cache]` probes stay in place. Handoff: [`docs/research/2026-05-19-indoor-walking-phase2-shipped-handoff.md`](../research/2026-05-19-indoor-walking-phase2-shipped-handoff.md). | Live ✓ |
| Indoor walking Phase A4 — Multi-cell BSP iteration | 2026-05-20. Ports retail's `CTransition::check_other_cells` (`acclient_2013_pseudo_c.txt:272717-272798`). After the primary cell's BSP returns OK, every other cell the foot-sphere overlaps is queried via `BSPQuery.FindCollisions`. Halt on first Collided/Adjusted/Slid; Slid clears the contact-plane fields. Three commits land the slices: `e6369e2` `CellTransit.FindCellSet` overload (refactor `FindCellList` to expose the candidate set); `493c5e5` `Transition.CheckOtherCells` + `ApplyOtherCellResult` combine helper; `691493e` (orig `967d065`, then `3add110` revert, then this reapply) wires `CheckOtherCells` into `FindEnvCollisions`. 10 new unit tests; 1139 + 8 baseline maintained. **Visual verification surfaced a separate, pre-existing M2 blocker:** at the Holtburg inn doorway the CellId ping-pongs between outdoor `0xA9B40022` and indoor vestibule `0xA9B40164` rapidly because indoor BSP push-back exits the indoor CellBSP volume → ResolveCellId reclassifies as outdoor → wall checks bypassed on outdoor ticks. Bug reproduces fully with A4 reverted (`launch-revert2.log`), confirming A4 is not the cause. A4 is correct and tested but **dormant in practice** until the ping-pong is fixed. Handoff: [`docs/research/2026-05-20-phase-a4-shipped-cell-pingpong-finding.md`](../research/2026-05-20-phase-a4-shipped-cell-pingpong-finding.md). Spec: [`docs/superpowers/specs/2026-05-20-phase-a4-multi-cell-bsp-design.md`](../superpowers/specs/2026-05-20-phase-a4-multi-cell-bsp-design.md). Plan: [`docs/superpowers/plans/2026-05-20-phase-a4-multi-cell-bsp.md`](../superpowers/plans/2026-05-20-phase-a4-multi-cell-bsp.md). | Live ✓ (dormant pending cell-tracking fix) | | Indoor walking Phase A4 — Multi-cell BSP iteration | 2026-05-20. Ports retail's `CTransition::check_other_cells` (`acclient_2013_pseudo_c.txt:272717-272798`). After the primary cell's BSP returns OK, every other cell the foot-sphere overlaps is queried via `BSPQuery.FindCollisions`. Halt on first Collided/Adjusted/Slid; Slid clears the contact-plane fields. Three commits land the slices: `e6369e2` `CellTransit.FindCellSet` overload (refactor `FindCellList` to expose the candidate set); `493c5e5` `Transition.CheckOtherCells` + `ApplyOtherCellResult` combine helper; `691493e` (orig `967d065`, then `3add110` revert, then this reapply) wires `CheckOtherCells` into `FindEnvCollisions`. 10 new unit tests; 1139 + 8 baseline maintained. **Visual verification surfaced a separate, pre-existing M2 blocker:** at the Holtburg inn doorway the CellId ping-pongs between outdoor `0xA9B40022` and indoor vestibule `0xA9B40164` rapidly because indoor BSP push-back exits the indoor CellBSP volume → ResolveCellId reclassifies as outdoor → wall checks bypassed on outdoor ticks. Bug reproduces fully with A4 reverted (`launch-revert2.log`), confirming A4 is not the cause. A4 is correct and tested but **dormant in practice** until the ping-pong is fixed. Handoff: [`docs/research/2026-05-20-phase-a4-shipped-cell-pingpong-finding.md`](../research/2026-05-20-phase-a4-shipped-cell-pingpong-finding.md). Spec: [`docs/superpowers/specs/2026-05-20-phase-a4-multi-cell-bsp-design.md`](../superpowers/specs/2026-05-20-phase-a4-multi-cell-bsp-design.md). Plan: [`docs/superpowers/plans/2026-05-20-phase-a4-multi-cell-bsp.md`](../superpowers/plans/2026-05-20-phase-a4-multi-cell-bsp.md). | Live ✓ (dormant pending cell-tracking fix) |
| Phase O — DatPath Unification | 2026-05-21. ONE thing touches the DATs. Extracted ~33 WB files / ~7.7K LOC into `src/AcDream.{Core,App}/Rendering/Wb/`. Dropped project references to `WorldBuilder.Shared` + `Chorizite.OpenGLSDLBackend` from `AcDream.App.csproj` and `AcDream.Core.csproj`. `DefaultDatReaderWriter` eliminated; `DatCollection` is the only dat reader. `WbMeshAdapter` consumes it via `DatCollectionAdapter` (O-D7 fallback; `ObjectMeshManager` has 26 internal `_dats.*` call sites exceeding the 20-site inline-swap threshold). `references/WorldBuilder/` stays in-tree as read-reference. **Visual side-by-side passed**: Holtburg town, inn interior, dungeon all render identically to pre-O. `NOTICE.md` includes WB MIT attribution. Spec: [`docs/superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md`](../superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md). Audit: [`docs/superpowers/specs/2026-05-21-phase-o-t1-wb-audit.md`](../superpowers/specs/2026-05-21-phase-o-t1-wb-audit.md). Plan: [`docs/superpowers/plans/2026-05-21-phase-o-plan.md`](../superpowers/plans/2026-05-21-phase-o-plan.md). | Live ✓ | | Phase O — DatPath Unification | 2026-05-21. ONE thing touches the DATs. Extracted ~33 WB files / ~7.7K LOC into `src/AcDream.{Core,App}/Rendering/Wb/`. Dropped project references to `WorldBuilder.Shared` + `Chorizite.OpenGLSDLBackend` from `AcDream.App.csproj` and `AcDream.Core.csproj`. `DefaultDatReaderWriter` eliminated; `DatCollection` is the only dat reader. `WbMeshAdapter` consumes it via `DatCollectionAdapter` (O-D7 fallback; `ObjectMeshManager` has 26 internal `_dats.*` call sites exceeding the 20-site inline-swap threshold). `references/WorldBuilder/` stays in-tree as read-reference. **Visual side-by-side passed**: Holtburg town, inn interior, dungeon all render identically to pre-O. `NOTICE.md` includes WB MIT attribution. Spec: [`docs/superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md`](../superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md). Audit: [`docs/superpowers/specs/2026-05-21-phase-o-t1-wb-audit.md`](../superpowers/specs/2026-05-21-phase-o-t1-wb-audit.md). Plan: [`docs/superpowers/plans/2026-05-21-phase-o-plan.md`](../superpowers/plans/2026-05-21-phase-o-plan.md). | Live ✓ |
| G.3 — Dungeon render/stream | Dungeon support shipped: the dungeon `LandBlock` is flat-terrain and streams through the existing pipeline (premise that terrain-less landblocks were unsupported was refuted at design time). Portal-graph visibility blowup closed (#95 RESOLVED, 9.1M→~39k instances, `a40c38e8`). | Live ✓ |
| G.3a — Teleport-into-dungeon | Hold-until-hydrate FSM: teleport-in no longer Resolves before the dungeon landblock hydrates. Closes #133. Commits `7947d7a` / `2ce5e5c8` / `47ae237e`. | Live ✓ |
| #137 — Dungeon collision | Dungeon BSP/collision fidelity fix. CLOSED 2026-07-08, user re-gate `0efa7ed2`. | Live ✓ |
| A7.L1 — Dungeon lighting | Indoor/dungeon lighting fidelity (#79/#93/#80/#154). CLOSED 2026-07-09, commits `d275ed55` / `9ebb2060` (see also `44cced71`). | Live ✓ |
Plus polish that doesn't get its own phase number: Plus polish that doesn't get its own phase number:
- FlyCamera default speed lowered + Shift-to-boost - FlyCamera default speed lowered + Shift-to-boost
@ -525,7 +534,7 @@ Research: R9 + R12 + R13.
- **✓ SHIPPED — G.1 — Sky + weather + day-night.** Deterministic client-side from Portal Year time. Sky dome geometry + keyframe gradients + rain/snow particles. See `r12-weather-daynight.md`. Full data + visual stack shipped: Region dat loader, keyframe interp, WeatherSystem with 5-kind PDF + transitions + storm flashes, WorldSession→WorldTimeService sync via ConnectRequest+TimeSync, SkyRenderer with sky-object arcs + UV scroll, rain/snow billboard renderer, F7/F10 debug cycle keys. - **✓ SHIPPED — G.1 — Sky + weather + day-night.** Deterministic client-side from Portal Year time. Sky dome geometry + keyframe gradients + rain/snow particles. See `r12-weather-daynight.md`. Full data + visual stack shipped: Region dat loader, keyframe interp, WeatherSystem with 5-kind PDF + transitions + storm flashes, WorldSession→WorldTimeService sync via ConnectRequest+TimeSync, SkyRenderer with sky-object arcs + UV scroll, rain/snow billboard renderer, F7/F10 debug cycle keys.
- **✓ SHIPPED — G.2 — Dynamic lighting.** 8-light D3D-style fixed pipeline. Hard-cutoff at Range, no attenuation inside. Cell ambient. Shader UBO per frame. See `r13-dynamic-lighting.md`. SceneLightingUbo std140 at binding=1 feeds terrain + mesh + mesh_instanced + sky shaders. LightingHookSink auto-registers Setup.Lights at entity stream-in, flips IsLit on SetLightHook, unregisters on landblock unload. - **✓ SHIPPED — G.2 — Dynamic lighting.** 8-light D3D-style fixed pipeline. Hard-cutoff at Range, no attenuation inside. Cell ambient. Shader UBO per frame. See `r13-dynamic-lighting.md`. SceneLightingUbo std140 at binding=1 feeds terrain + mesh + mesh_instanced + sky shaders. LightingHookSink auto-registers Setup.Lights at entity stream-in, flips IsLit on SetLightHook, unregisters on landblock unload.
- **Indoor portal-based cell tracking (follow-up to Indoor walking Phase 1 / issue #87).** Replace `PhysicsDataCache.TryFindContainingCell` AABB containment with retail's `CObjMaint::HandleObjectEnterCell` portal traversal. When the player crosses a cell portal boundary, `CellId` propagates through the `CEnvCell` portal connectivity graph. Prerequisite for wall collision from outside (#85) and the remaining #84 threshold symptom. PDB symbols and `acclient.h` `CCellStructure` refs are in place (see #87). **Unblocks G.3.** - **Indoor portal-based cell tracking (follow-up to Indoor walking Phase 1 / issue #87).** Replace `PhysicsDataCache.TryFindContainingCell` AABB containment with retail's `CObjMaint::HandleObjectEnterCell` portal traversal. When the player crosses a cell portal boundary, `CellId` propagates through the `CEnvCell` portal connectivity graph. Prerequisite for wall collision from outside (#85) and the remaining #84 threshold symptom. PDB symbols and `acclient.h` `CCellStructure` refs are in place (see #87). **Unblocks G.3.**
- **G.3 — Dungeon streaming + portal space.** `EnvCellStreamer`, portal-visibility BFS, `PlayerTeleport (0xF751)` handling with `LoginComplete` re-send, "pink bubble" loading state. **Blocked on indoor portal-based cell tracking above** (and previously on L.2e) for trustworthy indoor/outdoor portal transit, adjacent-cell ownership, and building entry/exit collision boundaries. See `r09-dungeon-portal-space.md`. - **✓ SHIPPED — G.3 — Dungeon streaming + portal space.** `EnvCellStreamer`, portal-visibility BFS, `PlayerTeleport (0xF751)` handling with `LoginComplete` re-send, "pink bubble" loading state. Dungeons render, stream, teleport-in, collide, light, and their doors work — see the shipped-table rows (G.3, G.3a, #137 collision, A7.L1 lighting) below and the M1.5 section of `docs/plans/2026-05-12-milestones.md` for current gate status (one recorded end-to-end round-trip user gate outstanding; live residual = far-town teleport-OUT arrival cascade, #145-residual REOPENED). See `r09-dungeon-portal-space.md`.
**Acceptance:** walk outside at dusk, see the sky gradient + sun moving; enter a torch-lit dungeon via portal; leave back to daylight. **Acceptance:** walk outside at dusk, see the sky gradient + sun moving; enter a torch-lit dungeon via portal; leave back to daylight.
@ -1318,7 +1327,7 @@ hitch and follows.
| Combat doesn't show in the chat log | **I.7 FIXED** ✓ | | Combat doesn't show in the chat log | **I.7 FIXED** ✓ |
| Accented character names show as `?` or garbled | **I.5 FIXED** ✓ (Windows-1252 codec) | | Accented character names show as `?` or garbled | **I.5 FIXED** ✓ (Windows-1252 codec) |
| No sound | **Phase E.2** | | No sound | **Phase E.2** |
| Dungeons / foundry interior missing | **Phase G.3** after **L.2e** cell/building ownership | | Dungeons / foundry interior missing | **Phase G.3 — SHIPPED** (render/stream/teleport-in/collide/light/doors all closed; M1.5 next step = #138 round-trip gate + #145-residual) |
| Can't fight monsters | **Phase F.3** (combat math + damage) | | Can't fight monsters | **Phase F.3** (combat math + damage) |
| Can't cast spells | **Phase F.4** | | Can't cast spells | **Phase F.4** |
| No inventory panel | **Phase F.2 + F.5** | | No inventory panel | **Phase F.2 + F.5** |

View file

@ -3,12 +3,18 @@
**Status:** Living document. Created 2026-05-12. **Status:** Living document. Created 2026-05-12.
**Sits above:** [`docs/plans/2026-04-11-roadmap.md`](2026-04-11-roadmap.md) (the strategic phase index). **Sits above:** [`docs/plans/2026-04-11-roadmap.md`](2026-04-11-roadmap.md) (the strategic phase index).
**Currently working toward:** **M1.5 — Indoor world feels right.** The **Currently working toward:** **M1.5 — Indoor world feels right.** The
building/cellar demo is DONE + user-gated, but M1.5 was EXTENDED 2026-06-13 building/cellar demo is DONE + user-gated. M1.5 was EXTENDED 2026-06-13 to
to include **dungeon support (full Phase G.3)** — dungeons don't work yet include **dungeon support (full Phase G.3)** — dungeons initially didn't
(terrain-less dungeon landblocks aren't supported by the streaming/load work at all (issue #133), but that premise was refuted at design time
pipeline; issue #133). M1.5 does NOT land until dungeons work. M2 stays (commit `90786c19`: the dungeon LandBlock is flat-terrain and streams via
deferred. (Correction: M1.5 was briefly marked landed 2026-06-13; the user the existing pipeline), and G.3 has since **SHIPPED**: dungeons render,
reverted that — the indoor world isn't done while dungeons are broken.) stream, teleport-in, collide, light, and their doors work — each piece
individually user-gated (#133/#137/#95/#79/#93/#80 all CLOSED). M1.5's
dungeon-demo scenario is functionally met; M1.5 stays 🔵 ACTIVE /
not-yet-landed, awaiting ONE recorded end-to-end dungeon round-trip user
gate. The sole remaining live-code residual is the far-town teleport-OUT
arrival cascade (**#145-residual, REOPENED** — capture-harness-first). M2
stays deferred.
--- ---
@ -193,19 +199,25 @@ close range and the player sees "You pick up the X." in chat.
### M1.5 — "Indoor world feels right" — 🔵 ACTIVE (building/cellar demo DONE; EXTENDED 2026-06-13 to include dungeon support / Phase G.3) ### M1.5 — "Indoor world feels right" — 🔵 ACTIVE (building/cellar demo DONE; EXTENDED 2026-06-13 to include dungeon support / Phase G.3)
**EXTENDED 2026-06-13 — dungeons pulled into M1.5 scope.** The **EXTENDED 2026-06-13 — dungeons pulled into M1.5 scope; G.3 SHIPPED.** The
building/cellar demo (below) is DONE + user-gated, but attempting the building/cellar demo (below) is DONE + user-gated. Attempting the dungeon
dungeon demo surfaced that dungeons don't work AT ALL: terrain-less demo initially surfaced that dungeons didn't work AT ALL — terrain-less
dungeon landblocks aren't supported anywhere in the streaming/load/ dungeon landblocks appeared unsupported anywhere in the streaming/load/
render/physics pipeline (`LandblockLoader.Load` returns null with no render/physics pipeline (`LandblockLoader.Load` returning null, no terrain
`LandBlock` terrain record; the streamer fails with no terrain mesh; the mesh, teleport snap Resolving before hydration — issue #133). That premise
teleport snap Resolves before hydration — issue #133). The user decided was refuted at design time (commit `90786c19`: the dungeon `LandBlock` is
M1.5 is NOT done while the indoor world excludes dungeons, and chose the flat-terrain and streams via the existing pipeline). The user chose the
FULL Phase G.3 scope (dungeon streaming + portal-space loading screen + FULL Phase G.3 scope (dungeon streaming + portal-space loading screen +
multi-landblock dungeon LOD + `PlayerTeleport` handling). Design in multi-landblock dungeon LOD + `PlayerTeleport` handling), and it has since
progress (`docs/superpowers/specs/` — dungeon-support spec). M1.5 lands **SHIPPED**: teleport-into-dungeon (G.3a hold-until-hydrate FSM closes
when: building/cellar demo (DONE) + dungeon demo (enter via portal, #133), dungeon collision (#137 CLOSED), the #95 portal-graph visibility
navigate 3-5 rooms, walls block, smooth transitions) both pass. blowup (RESOLVED), dungeon doors (#187/#188), and A7 dungeon lighting
(#79/#93/#80/#154, all CLOSED). M1.5's dungeon-demo scenario (enter via
portal, navigate 3-5 rooms, walls block, smooth transitions) is
functionally met; M1.5 now awaits ONE recorded end-to-end dungeon
round-trip user gate. The sole remaining live-code residual is the
far-town teleport-OUT arrival cascade (**#145-residual, REOPENED** — see
below).
**Building/cellar demo — DONE + user-gated.** The indoor world reads as **Building/cellar demo — DONE + user-gated.** The indoor world reads as
solid. Across the solid. Across the
@ -222,8 +234,8 @@ closed); cell transitions are smooth (the doorway "flap" family killed —
#119/#128, #112, #113, #124, #129/#130/#131/#132, #108-residual, #127 all #119/#128, #112, #113, #124, #129/#130/#131/#132, #108-residual, #127 all
closed with user gates). The #90-stickiness + `TryFindIndoorWalkablePlane` closed with user gates). The #90-stickiness + `TryFindIndoorWalkablePlane`
synthesis workarounds were removed by A6.P4. Remaining feel-level debt is synthesis workarounds were removed by A6.P4. Remaining feel-level debt is
tracked (#116 slide-response, partial Ghidra fix shipped; A7 indoor tracked (#116 slide-response, partial Ghidra fix shipped). A7 indoor/
lighting fidelity not yet done — folded forward). dungeon lighting fidelity CLOSED 2026-07-09 (#79/#93/#80/#154).
**Interleaved parity tracks (reconciled 2026-07-04 — NOT milestones).** Two **Interleaved parity tracks (reconciled 2026-07-04 — NOT milestones).** Two
work streams have run alongside M1.5, both driven by the user's live work streams have run alongside M1.5, both driven by the user's live
@ -259,16 +271,21 @@ as ad-hoc rework. Rule 2's freeze still binds all NON-MP work. MP runs in
dedicated side-track sessions; the M1.5 critical path wins every conflict, dedicated side-track sessions; the M1.5 critical path wins every conflict,
and MP4 is hard-queued behind #137. and MP4 is hard-queued behind #137.
**Still OPEN in M1.5 — dungeon support (Phase G.3, issue #133).** Dungeons **Dungeon support (Phase G.3, issue #133) — SHIPPED.** The original
don't work: the streaming/load/render/physics pipeline was built entirely premise here (terrain-less dungeon landblocks unsupported anywhere in the
around outdoor landblocks (terrain + scattered buildings) and has no path streaming/load/render/physics pipeline) was refuted at design time
for terrain-less indoor-only dungeon landblocks. Confirmed gaps: (commit `90786c19`: the dungeon `LandBlock` is flat-terrain and streams
`LandblockLoader.Load` returns null with no `LandBlock` record; the via the existing pipeline). Full G.3 scope (streaming + portal-space
streamer fails with no terrain mesh; the teleport-arrival snap Resolves loading screen + multi-landblock LOD + `PlayerTeleport` handling) has
before the dungeon hydrates → places the player in the old frame over shipped: teleport-into-dungeon (G.3a hold-until-hydrate FSM,
ocean. Full G.3 scope chosen by the user 2026-06-13 (streaming + portal- `7947d7a`/`2ce5e5c8`/`47ae237e`) closed #133; dungeon collision closed
space loading screen + multi-landblock LOD + `PlayerTeleport` handling). #137 (`0efa7ed2`); the #95 portal-graph visibility blowup RESOLVED
Spec under `docs/superpowers/specs/`. (9.1M→~39k instances, `a40c38e8`); dungeon doors shipped (#187
`e2e285b8`, #188 `3284dd0a`); A7 dungeon lighting closed
(#79/#93/#80/#154, `44cced71`/`d275ed55`/`9ebb2060`). Remaining: one
recorded end-to-end dungeon round-trip user gate, plus the far-town
teleport-OUT arrival cascade residual (**#145-residual, REOPENED** —
capture-harness-first).
--- ---
@ -302,8 +319,9 @@ M1.5's planned phases (A6 + A7) are unaffected.
"enter the Holtburg Sewer dungeon" but that location doesn't exist on "enter the Holtburg Sewer dungeon" but that location doesn't exist on
this ACE server (discovered during A6.P1 capture session). Additionally, this ACE server (discovered during A6.P1 capture session). Additionally,
A6.P1 surfaced **issue #95** (portal-graph visibility blowup) which makes A6.P1 surfaced **issue #95** (portal-graph visibility blowup) which makes
ANY dungeon visually unusable on entry. Demo scenario revised in two ANY dungeon visually unusable on entry. **[SUPERSEDED — #95 RESOLVED
parts: (9.1M→~39k instances, `a40c38e8`); dungeons ship; see M1.5 section
above.]** Demo scenario revised in two parts:
**Building/cellar demo (achievable after A6.P3 lands):** **Building/cellar demo (achievable after A6.P3 lands):**
Walk into the Holtburg inn, climb to the 2nd floor, walk around without Walk into the Holtburg inn, climb to the 2nd floor, walk around without
@ -317,7 +335,8 @@ through. Throughout:
artifacts, static decorations participate in env lighting. artifacts, static decorations participate in env lighting.
**Dungeon demo (blocked on issue #95 fix; promote to post-M1.5 if **Dungeon demo (blocked on issue #95 fix; promote to post-M1.5 if
the visibility bug isn't addressed in M1.5 scope):** the visibility bug isn't addressed in M1.5 scope):** **[SUPERSEDED — #95
RESOLVED, dungeons ship; see M1.5 section above.]**
Enter any dungeon via portal (substitute for "Holtburg Sewer"). Navigate Enter any dungeon via portal (substitute for "Holtburg Sewer"). Navigate
~3-5 rooms without rendering corruption (no see-through-walls, no ~3-5 rooms without rendering corruption (no see-through-walls, no
other-dungeons-rendered-inside). Walls block, stairs work, items block, other-dungeons-rendered-inside). Walls block, stairs work, items block,
@ -352,7 +371,7 @@ patch.
- **#83** — Indoor multi-Z walking broken (cellars, 2nd floors, intermittent falling-stuck) - **#83** — Indoor multi-Z walking broken (cellars, 2nd floors, intermittent falling-stuck)
- **#88** — Indoor static objects vibrate (suspected sub-step state corruption — A6.P2 maps to Finding 2 family) - **#88** — Indoor static objects vibrate (suspected sub-step state corruption — A6.P2 maps to Finding 2 family)
- **#90** — CellId ping-pong (workaround in place; remove during A6.P4) - **#90** — CellId ping-pong (workaround in place; remove during A6.P4)
- **#95** — Portal-graph visibility blowup (filed 2026-05-21; **blocks the dungeon half of the M1.5 demo** but is NOT in A6 scope; either add a dedicated phase inside M1.5 to fix it OR promote the dungeon demo to post-M1.5) - **#95** — Portal-graph visibility blowup (filed 2026-05-21; **blocks the dungeon half of the M1.5 demo** but is NOT in A6 scope; either add a dedicated phase inside M1.5 to fix it OR promote the dungeon demo to post-M1.5) **[SUPERSEDED — RESOLVED, 9.1M→~39k instances, `a40c38e8`; dungeons ship; see M1.5 section above.]**
- **L-indoor** — Lighting indoors broken (file as new # during M1.5 kickoff) - **L-indoor** — Lighting indoors broken (file as new # during M1.5 kickoff)
- **L-spotlight** — Items projecting spotlight on walls (file as new # during M1.5 kickoff) - **L-spotlight** — Items projecting spotlight on walls (file as new # during M1.5 kickoff)
- **Stairs walk-through** — captured + characterized by A6.P2 (Finding 2 family); fix in A6.P3 - **Stairs walk-through** — captured + characterized by A6.P2 (Finding 2 family); fix in A6.P3