acdream/docs/superpowers/specs/2026-06-13-dungeon-support-design.md
Erik 6680fd42b2 spec: G.3 dungeon support design (M1.5 exit-gate) — phased, retail-faithful
Brainstorm outcome for #133/G.3. Grounds the corrected root cause (dungeon
landblock = flat terrain + EnvCells, streams via the existing pipeline; the
blocker is the teleport-arrival snap firing BEFORE the dest landblock hydrates)
against the current code (5 verified seams) and lays out Approach C:

  G.3a  core teleport-into-dungeon: hold-until-hydration on the arrival path
        (reuse #107 IsSpawnCellReady + IsSpawnClaimUnhydratable) + #111
        validated-claim EnvCell placement + dest-ready streaming query +
        dest-coord validation + timeout safety + decouple EnvCell
        physics/visibility hydration from the render-mesh guard.  -> VISUAL GATE
  G.3b  #95 stab_list bounding — CONDITIONAL on the gate showing the blowup
        (its repro is stale, from the T4-deleted WB path; the current flood is
        landblock-confined + enqueue-once, so #95 is likely superseded).
  G.3c  faithful TeleportAnimState portal-tunnel FSM (decomp 004d6300 /
        219405-219774); the TAS_TUNNEL hold-exit gates on G.3a's same readiness
        predicate (the tunnel IS the hold's visual form).
  G.3d  recall game-actions (/ls etc.) — same arrival flow; doubles as the test
        lever.

Supersedes the §12 port-plan of r09 (most of it already shipped); r09 stays the
wire/format/recall contract reference. Resolves the handoff's 4 open questions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 16:43:39 +02:00

24 KiB
Raw Blame History

Phase G.3 — Dungeon Support (Design Spec)

Status: APPROVED design (brainstorm 2026-06-13). Next: writing-plans. Milestone: M1.5 ("Indoor world feels right"). G.3 is the remaining M1.5 exit-gate. M2 (CombatMath) stays deferred until this lands. Issue: #133 (teleport-into-dungeon snaps to ocean) + #95 (dungeon portal-graph visibility blowup — re-assessed below). Supersedes the §12 port-plan of docs/research/deepdives/r09-dungeon-portal-space.md: most of R9's "new types" (EnvCell loader/renderer/physics, PortalVisibility BFS, multi-cell transit) already shipped and power the building/cellar demo. r09 stays the retail contract reference for the wire formats, the EnvCell/CellPortal layout, and the recall taxonomy.


0. TL;DR

Dungeons don't work because of one timing+placement gap on one code path, not a terrain-less-pipeline rewrite. A dungeon landblock (e.g. 0x0125, the Holtburg-area meeting hall) is a flat-terrain landblock (LandBlock present, all-zero heights) + 71 EnvCells + no buildings — it already streams, renders, and collides through the existing pipeline. The teleport-arrival handler snaps the player before that landblock has streamed in, so Resolve falls back to the resident Holtburg blocks and lands the player in ocean.

The fix is retail's own shape: hold the player in portal space until the destination cell is hydrated, then place into the EnvCell — reusing the #107/#111 login machinery — and then layer retail's portal-tunnel visual (TeleportAnimState) on top. We ship it in four installments, gated by one visual acceptance test.


1. Corrected root cause (verified)

1.1 The "terrain-less landblock" framing is WRONG (dat-verified)

A prior research pass assumed dungeon landblocks have no LandBlock record, so LandblockLoader.Load returns null and the whole streaming/render/physics pipeline needs terrain-less support. A direct dat probe (DungeonLandblockDatProbeTests, committed) refutes that:

0x0125 (dungeon):  LandBlock 0x0125FFFF PRESENT, Height[81] allZero=True (flat)
                   LandBlockInfo: NumCells=71, Buildings=0, Objects=0
                   EnvCells 0x0100.. present (the 71 dungeon rooms)
0xA9B4 (Holtburg): LandBlock PRESENT, heights non-zero; NumCells=123, Buildings=12, Objects=114

A dungeon landblock is a flat-terrain landblock (lowest/"ocean" terrain height index) plus its EnvCells, no buildings/objects. LandblockLoader.Load returns a valid flat landblock; the terrain mesh builds a flat plane; PhysicsEngine.AddLandblock gets a valid flat TerrainSurface. The existing pipeline already streams a dungeon landblock. This matches ACE's IsDungeon (all heights 0 + NumCells > 0 + no buildings — Landblock.cs:575) and the single-landblock rule (Player_Tick.cs:548-560 forbids moving between dungeon landblocks without a teleport — so "multi-landblock dungeon LOD" is moot).

1.2 The real blocker: teleport TIMING + PLACEMENT

OnLivePositionUpdated (src/AcDream.App/Rendering/GameWindow.cs:4877-4961) detects teleport arrival as any player position update while in PortalSpace (correct, per #107), then unconditionally:

  1. Recenters streaming to the destination landblock (_liveCenterX/Y, :4908-4925).
  2. Immediately calls _physicsEngine.Resolve(destPos, destCell, …) to snap the player (:4927-4931) — before the destination landblock has streamed in.
  3. Snaps entity + controller (:4935-4939), exits PortalSpace (:4950), sends LoginComplete (:4953-4959).

Because the dungeon landblock isn't resident yet, Resolve can't find the destination cell, falls back to an outdoor scan against the still-resident Holtburg landblocks, and snaps to 0xA9B3000E (Holtburg's south edge — local (30,60) maps into the block south of the A9B4 spawn). Streaming then shifts the frame out from under the player → they slide south into ocean. ACE logs the matching failed transition for +Acdream from 0x01250126 … to 0xA9B0000E … chain (captured in launch-dungeon-diag.log).

There is no hold-until-hydration on the teleport-arrival path. The #107 login path directly above it (GameWindow.cs:1010-1024) HAS exactly this gate; the teleport path doesn't.


2. Grounded seam facts (the design rests on these)

All five verified against current code this session (high confidence).

2.1 Teleport-arrival + PortalSpace FSM

  • OnTeleportStarted (GameWindow.cs:~4971-4976) — on PlayerTeleport (0xF751) sets _playerController.State = PlayerState.PortalSpace, freezing movement.
  • PlayerMovementController.Update (PlayerMovementController.cs:840-854) returns a zero-movement result while State == PortalSpacePortalSpace already doubles as the input-freeze. It can equally serve as the hydration-wait gate.
  • Exit is only via the arrival detection in OnLivePositionUpdated (:4880). No timeout, no cell-hydration gate today.

2.2 #107/#111 login machinery (directly reusable)

  • PhysicsEngine.IsSpawnCellReady(cellId) (PhysicsEngine.cs:468-472): outdoor (cellId & 0xFFFF < 0x0100) → always ready; indoor → DataCache.GetCellStruct(cellId) is not null (the cell's physics BSP has hydrated).
  • IsSpawnClaimUnhydratable(claim) (GameWindow.cs:11728-11748): fetches the dat LandBlockInfo at (lb & 0xFFFF0000) | 0xFFFE; a claim whose low word is >= 0x0100 + NumCells (or NumCells==0) can never hydrate → reject fast (distinguishes a bogus claim from a not-yet-streamed one).
  • #107 login hold (GameWindow.cs:1010-1024): isSpawnGroundReady waits for terrain AND (claim outdoor OR IsSpawnCellReady OR IsSpawnClaimUnhydratable). No timeout today (login can afford to wait forever; teleport cannot — see §5).
  • #111 validated-claim placement (PhysicsEngine.cs:626-646): when snapDiag (zero-delta) && adjustedFound && indoor, place via WalkableFloorZNearest (:383-406) — projects Z onto the claim cell's own physics walkable polygons (normal.Z >= PhysicsGlobals.FloorZ, 0.6642), cell-local, nearest to the reference Z. Returns null if the cell isn't hydrated → falls through to the legacy bestCell scan (the ocean bug).
  • The teleport-arrival Resolve call is already the same shape as login entry. The gate only needs to sit in front of it; no change to Resolve or WalkableFloorZNearest. (Both already key on the full prefixed cell id + indoor/outdoor.)

2.3 Streaming far recenter (works as-is)

  • StreamingRegion.RecenterTo (StreamingRegion.cs:180-283) recomputes the near/far Chebyshev window from scratch around the new center — a 42 km jump is treated identically to a 1-step move. No incremental-movement assumption.
  • Drain: StreamingController applies ≤ MaxCompletionsPerFrame (default 4) results/frame; ApplyLoadedTerrainLocked (GameWindow.cs:5941-6150) does GPU upload + cell-visibility registration + AABB + PhysicsEngine.AddLandblock + EnvCell/portal registration. Estimate: ~7-8 frames (~120-130 ms) to hydrate a 5×5 near window; physics ready +1-2 frames.
  • Recenter keeps the old neighborhood until hysteresis unload (NearRadius+2 demote, FarRadius+2 unload), so the player isn't instantly stranded.
  • New code needed: a "destination landblock applied" query + dest-coord validation (reject out-of-world coords — a malformed portal dest would otherwise leave the player in an invisible, unloadable landblock).

2.4 EnvCell hydration coupling (latent landmine — decouple)

  • In BuildInteriorEntitiesForStreaming (GameWindow.cs:5564-5651), both BuildLoadedCell (the portal-visibility node) and _physicsDataCache.CacheCellStruct (the physics BSP) sit inside the render guard if (cellSubMeshes.Count > 0) (:5602). A cell whose render mesh is empty (CellMesh.Build returns nothing — e.g. all-untextured/Stippling.NoPos polys) silently gets no visibility node and no collision, even if it has walkable physics polygons. CellTransit.FindTransitCellsSphere then GetCellStruct → null → continue (silently skips it) → fall-through-floor.
  • A normal dungeon room has textured walls → non-empty submeshes → the guard passes, so this is probably not the meeting-hall blocker — but it is a real correctness landmine for any geometry-less collision cell, and decoupling is cheap and retail-correct (physics/visibility do not depend on visible geometry). Fix: gate CacheCellStruct on cellStruct.PhysicsBSP != null and BuildLoadedCell on cellStruct != null, independent of the render submesh count. (CacheCellStruct already early-returns on null BSP internally — PhysicsDataCache.cs:172 — so moving it out is safe.)

2.5 #95 — dungeon portal-graph visibility blowup (RE-ASSESSED: likely superseded)

  • ISSUES.md #95 (888-913): on a 2026-05-21 A6.P1 scen5 (Town Network hub) trace, visibleCells per cell exploded to 135-145 with spurious cells from landblocks 0x020A/0x0408 (other dungeons). Its "Files" point at the WB EnvCellRenderManager/VisibilityManager + the Streaming cell-cache.
  • That code path was DELETED by the T1-T6 render rewrite (2026-06-11) (T4: "per-frame ACME BFS deleted… InteriorRenderer/DrawPortal deleted"). The current flood, PortalVisibilityBuilder.Build, (a) confines neighbors to the camera cell's landblock (lbMask = cameraCell.CellId & 0xFFFF0000, :131) and (b) has enqueue-once termination (queued HashSet, :165 — "at most N cells are ever processed"). Since AC dungeons are single-landblock, that confinement is correct, and the cross-landblock 135-cell blowup structurally cannot reproduce: a single-landblock flood visits ≤ NumCells distinct cells (71 for the meeting hall).
  • Verdict: #95's evidence is stale, from a deleted path; the current pipeline is bounded. Treat #95 as likely superseded, unverified. The meeting-hall demo (71 cells, one landblock) IS its empirical test. Do not pre-build the stab_list bounding port against a dead repro (G.3b is conditional — §3.2).

3. The plan (Approach C — phased full-G.3)

Each installment lands a complete retail behavior (the BR-2 half-port lesson). The visual gate sits as early as possible, right after the core.

3.1 G.3a — Core teleport-into-dungeon (the blocker)

Goal: teleporting into the meeting-hall dungeon lands the player standing in the dungeon cell, on the floor, with walls blocking — no ocean, no ACE failed transition spam.

New component — TeleportArrivalController (src/AcDream.App/World/):

  • Owns a small phase: Idle / Holding / Placing, plus _pendingArrival (destPos, destCellId, deadline).
  • Lives outside GameWindow (Code Structure Rule 1: no new feature bodies in the god-object). GameWindow.OnLivePositionUpdated hands the arrival to it and calls its per-frame Tick; GameWindow keeps only the wiring.
  • Unit-testable in isolation (no GL, fake readiness predicate + fake Resolve).

Control flow (replaces the unconditional snap at GameWindow.cs:4927-4950):

  1. On arrival update in PortalSpace: validate destCellId's landblock coords are in-world; recenter streaming + prioritize-load the dest landblock (existing path); stash _pendingArrival; enter Holding. Re-send LoginComplete immediately (holtburger-conformant — messages.rs:434; do not wait for assets to send it).
  2. Each frame in Holding, evaluate the readiness predicate:
    • IsSpawnClaimUnhydratable(destCell) → impossible claim: stop holding, place via the safety-net demote (loud log), exit PortalSpace.
    • now > deadline (timeout, ~10 s) → force-snap via safety-net demote + loud log, exit PortalSpace. (See §5 — failure-surfacing, not symptom-masking.)
    • IsLandblockApplied(destLb) && IsSpawnCellReady(destCell) → ready: go to 3.
    • else stay frozen, retry next frame.
  3. Placing: call the existing Resolve(destPos, destCell, Vector3.Zero, …). Because the cell is now hydrated, Resolve takes the #111 validated-claim branch → WalkableFloorZNearest grounds the player on the EnvCell floor. Snap entity
    • controller (existing :4935-4939 code), exit PortalSpace, resume input.

New streaming query — IsLandblockApplied(uint landblockId) (on StreamingController / GpuWorldState): true once the landblock's terrain has been applied (AABB set in ApplyLoadedTerrainLocked) and AddLandblock has run into physics. Gate the hold on this, not on the GPU mesh alone.

Dest-coord validation: in OnLivePositionUpdated, reject a destination whose (lbX, lbY) is out of the world grid before recenter; log + abort the teleport hold rather than recenter to a phantom block.

Hydration decouple (§2.4): move BuildLoadedCell + CacheCellStruct out of the cellSubMeshes.Count > 0 guard in BuildInteriorEntitiesForStreaming. Gate each on its own non-null precondition.

Acceptance (G.3a): the visual gate in §6. This gate also empirically settles #95 (does the flood blow up?) and the hydration coupling (does collision work?).

3.2 G.3b — #95 visibility bounding (CONDITIONAL)

Trigger: only if the G.3a visual gate shows the see-through-walls / other-dungeon-geometry blowup at the meeting hall.

If triggered: port retail CEnvCell::grab_visible_cells (:311878) — a cell with seen_outside == 0 loads ZERO terrain and walks ONLY its stab_list of adjacent EnvCells; the portal graph is bounded by the dungeon's own cell adjacency, never a radius. Verify the dat carries the stab_list and acdream's EnvCell loader parses it before relying on it.

If NOT triggered: close #95 as superseded (deleted WB path; current flood bounded) with a one-line ISSUES.md note. No speculative build.

3.3 G.3c — Portal-tunnel loading visual (faithful TeleportAnimState)

Goal: the retail portal-space transition, ported faithfully (user decision 2026-06-13). Reconciles the older r09 §6 ("there is no loading screen") with the named-retail decomp where this FSM actually lives.

Oracle: gmSmartBoxUI::BeginTeleportAnimation (004d6300, named-retail line 218888) + the per-frame FSM (219405-219774). States: TAS_WORLD_FADE_OUT → TAS_TUNNEL_FADE_IN → TAS_TUNNEL / TAS_TUNNEL_CONTINUE → TAS_TUNNEL_FADE_OUT → TAS_WORLD_FADE_IN → (off). m_pPortalSpace is a UIElement_Viewport rendering the tunnel scene (creature-mode objects + DISTANT_LIGHT + smartbox FOV; SetVisible(1) on enter, SetVisible(0) on the TAS_TUNNEL_FADE_OUT → TAS_WORLD_FADE_IN edge at 219742-219747).

Key architectural unification: the TAS_TUNNEL/TAS_TUNNEL_CONTINUE hold state's exit gates on the same readiness predicate as G.3a — retail's loading visual and the hold-until-hydration gate are one mechanism (the tunnel is the visual form of the hold). G.3a ships the bare PortalSpace freeze; G.3c wraps it in the tunnel viewport + the fade FSM, exit-gated identically.

Port workflow: grep-named → decompile BeginTeleportAnimation + the FSM → pseudocode (durations, fade math, viewport scene construction) → port → test. Detail deferred to the G.3c implementation phase; this spec fixes the design (states, transitions, the readiness-gated hold) + the oracle pointers.

3.4 G.3d — Recall game-actions

Outbound zero-payload game-action builders (r09 §7.1): TeleToLifestone 0x0063, TeleToHouse 0x0262, TeleToMansion 0x0278, TeleToMarketPlace 0x028D, RecallAllegianceHometown 0x02AB, TeleToPkArena 0x0027. The client only sends the request; the server validates, plays the recall animation, then drives the same PlayerTeleport → UpdatePosition arrival flow.

Value: (1) doubles as the easy test lever for G.3a/G.3c — /ls triggers a teleport with no portal-click choreography; (2) completes the recall UX (keybinds exist; the wire sends + return handling did not). Wire through the existing command bus.


4. Data flow (the teleport happy path)

1. PlayerTeleport(0xF751)        → OnTeleportStarted: enter PortalSpace, freeze input
                                   [G.3c: BeginTeleportAnimation(TAS_WORLD_FADE_OUT)]
2. fake UpdatePosition(destCell) → validate dest coords → recenter streaming to dest lb
                                   → prioritize-load dest lb → re-send LoginComplete
3. HOLD (TeleportArrivalController.Tick, each frame in PortalSpace):
     ready = IsLandblockApplied(destLb) && IsSpawnCellReady(destCell)
     - not ready  → stay frozen, retry   [G.3c: tunnel holds in TAS_TUNNEL/_CONTINUE]
     - impossible → IsSpawnClaimUnhydratable → safety-net demote + loud log
     - timeout    → force-snap + loud log + leave PortalSpace
4. READY → Resolve(destPos, destCell)  → #111 validated-claim branch
         → WalkableFloorZNearest places on the EnvCell floor
         → SetPosition(entity + controller) → exit PortalSpace, resume input
                                   [G.3c: TAS_TUNNEL_FADE_OUT → TAS_WORLD_FADE_IN → off]

(ACE server send-order, for reference — Player_Location.Teleport:686: PlayerTeleport(seq) → fake UpdatePosition (start client load) → DoTeleportPhysicsStateChanges (hidden / ignoreCollisions) → real UpdatePositionOnTeleportComplete after CreateWorldObjectsCompleted.)


5. Error handling

Failure Handling No-workaround rationale
Impossible / poisoned claim (cell id ∉ [0x0100, 0x0100+NumCells), or no struct + no surface) IsSpawnClaimUnhydratable → safety-net demote (PhysicsEngine.Resolve head, :536-570) + loud log; never hold forever Reuses the validated #107/#111 reject; no new masking
Dest LB fails to stream (worker crash / corrupt dat / OOB coords) Timeout ceiling (~10 s) → force-snap + loud log + leave PortalSpace Surfaces the failure (visible bad placement + log), does not freeze the client or silence the cause; gets a divergence-register row
Mid-hold entity-rescue race Already serialized by _datLock during recenter (verified, seam-3) No change

The timeout is the one judgment call: holding forever on a never-hydrating landblock would soft-lock the client. The chosen behavior fails loudly and visibly (force-snap + log), which is the opposite of a symptom-masking grace period — it makes a broken teleport obvious rather than hiding it. It is recorded as a deliberate adaptation (retail loads synchronously; async streaming has no direct analog).


6. Testing & acceptance

6.1 Headless / unit

  • TeleportArrivalController FSM: Idle → Holding → Placing happy path; impossible-claim immediate reject; timeout force-snap; ready-predicate gating (fake IsLandblockApplied / IsSpawnCellReady).
  • Hydration-decouple test: a geometry-less EnvCell (empty render mesh, non-empty physics BSP) still gets CacheCellStruct + BuildLoadedCell.
  • TeleportFlowTests: fake PlayerTeleport + UpdatePosition wire → controller phase transitions + input-gate flips.
  • DungeonLandblockDatProbeTests (exists): pins 0x0125 = flat + 71 cells.
  • G.3c: TeleportAnimState FSM transition test (state sequence + the readiness-gated TAS_TUNNEL hold-exit).
  • G.3d: recall-builder byte tests (opcode + empty payload, per builder).

6.2 Visual gate (the acceptance test — after G.3a)

Teleport into the meeting-hall dungeon via the portal:

  • Player stands in the dungeon cell, on the floor (not ocean, not falling).
  • The dungeon renders; navigate 35 rooms; walls block movement.
  • No ocean / no ACE failed transition spam.
  • (Implicitly) the portal flood does not blow up (#95 check) and collision works in every room (hydration-coupling check).

ACDREAM_PROBE_CELL=1 + ACDREAM_PROBE_VIEWER=1 + ACDREAM_WB_DIAG=1 + the always-on [snap] / live: teleport lines capture the chain (the launch-dungeon-diag.log protocol from this session).

6.3 Per-installment build/test gates

Each installment: dotnet build green + dotnet test green (App / Core / UI / Net suites) before it's "done"; G.3a additionally requires the visual gate.


7. Retail divergence register impact

  • G.3a timeout force-snap → NEW row (adaptation: async streaming hold has no synchronous-retail analog; retail loads the cell set synchronously before SetPositionInternal).
  • Hydration decouple → NO row (bug fix retiring an incidental render↔physics coupling; restores retail-correct independence).
  • G.3c → only a row if a faithful asset can't be reproduced (e.g. the tunnel viewport scene) and a documented courtesy substitute is shipped.
  • #95 close-as-superseded (if G.3b not triggered) → ISSUES.md note only.

8. Component boundaries (what each unit does / depends on)

Unit Location Does Depends on
TeleportArrivalController AcDream.App/World/ Owns the Idle/Holding/Placing phase + _pendingArrival; decides hold-vs-place each frame readiness predicate (injected), Resolve (injected), PortalSpace state
readiness predicate StreamingController + PhysicsEngine IsLandblockApplied(lb)IsSpawnCellReady(cell); IsSpawnClaimUnhydratable(cell) dat LandBlockInfo, DataCache
hydration decouple GameWindow.BuildInteriorEntitiesForStreaming BuildLoadedCell + CacheCellStruct gated on cellStruct/BSP, not render mesh cellStruct, PhysicsBSP
TeleportAnimState FSM (G.3c) AcDream.App UI/render Portal-tunnel fade FSM; hold-exit gated on the readiness predicate m_pPortalSpace viewport, the readiness predicate
recall builders (G.3d) AcDream.Core/Network/Actions Zero-payload outbound game actions command bus

AcDream.Core gains no GL/window dependency. The controller + FSM live in AcDream.App; the readiness predicate's physics half lives in AcDream.Core (pure), its streaming half in AcDream.App.


9. References cited

  • Current code (verified this session): GameWindow.cs 4877-4961 (arrival), ~4971-4976 (OnTeleportStarted), 1010-1024 (#107 login gate), 11728-11748 (IsSpawnClaimUnhydratable), 5564-5651 (EnvCell hydration guard), 5941-6150 (ApplyLoadedTerrainLocked); PhysicsEngine.cs 468-472 (IsSpawnCellReady), 626-646 (#111 validated claim), 383-406 (WalkableFloorZNearest), 536-570 (Resolve safety net); StreamingRegion.cs 180-283 (RecenterTo); StreamingController.cs 120-149 (drain); PortalVisibilityBuilder.cs 131 (lbMask), 165 (enqueue-once); CellTransit.cs 515-516 (null-skip); PhysicsDataCache.cs 172 (null-BSP early-return).
  • Decomp (named-retail): BeginTeleportAnimation 004d6300 (line 218888) + the TeleportAnimState FSM 219405-219774; m_pPortalSpace viewport 218829/219363; CEnvCell::grab_visible_cells :311878 (G.3b stab_list).
  • holtburger: messages.rs:434 (client re-sends LoginComplete on teleport).
  • ACE: Player_Location.Teleport:686 (send order); Landblock.cs:575 (IsDungeon); Player_Tick.cs:548-560 (single-landblock dungeons); recall handlers + Portal.ActOnUse/AdjustDungeon.
  • r09 deepdive: docs/research/deepdives/r09-dungeon-portal-space.md (EnvCell / CellPortal wire layout, recall taxonomy, the retail contract).
  • Issues: #133, #95.
  • Digests (DO-NOT-RETRY tables apply): project_render_pipeline_digest, project_physics_collision_digest.

10. Open questions (resolved here; revisit only if the gate disagrees)

  1. Loading visual now or later? Faithful TeleportAnimState in G.3c (user decision). Unified with the G.3a hold (the tunnel IS the hold's visual).
  2. Hold timeout/failure? Reject impossible claims instantly (IsSpawnClaimUnhydratable); hold plausible-but-slow with a ~10 s ceiling; on timeout force-snap + loud log (fail visibly, never freeze).
  3. Big-jump streaming? Verified to work (Chebyshev recenter). Add only IsLandblockApplied + dest-coord validation.
  4. EnvCell placement vs flat terrain? The #111 WalkableFloorZNearest EnvCell path (identical to the cellar path that already works); the flat terrain renders below. The gate guarantees the cell is hydrated before Resolve runs.
  5. (New, deferred to G.3b/implementation) Does the dat carry a parsed stab_list for grab_visible_cells bounding? Only matters if the gate shows the #95 blowup.