# Indoor Render — Session Handoff: HANG fixed + interior SEALS; the FLAP is next — 2026-06-07 > Worktree `thirsty-goldberg-51bb9b`, branch `claude/thirsty-goldberg-51bb9b`. PowerShell on Windows; > launch logs UTF-16; build before launch; acceptance is the user's eyes. Live ACE `127.0.0.1:9000`, > `testaccount`/`testpassword`, char `+Acdream` (spawns near the Holtburg / "Arcanum" cottage — > landblock `0xA9B4`, cottage cells `0xA9B4016F–0175`). Do NOT branch/worktree, push, or `git stash`/`gc`. ## TL;DR The two-week indoor-render **HANG is FIXED** and the interior **SEALS** (walls/floor/ceiling draw, textured) — both committed this session and live-verified by the user ("Ok now it runs!"). A structured live test pinned the remaining dominant visible issue, the **FLAP at transitions**, as **viewer-cell metastability**: the render roots at the camera-eye cell, which oscillates outdoor↔indoor as the 3rd-person boom drifts across the doorway plane. **The flap is a SEPARATE, already-designed fix — it is NOT the verbatim DrawCells port; finishing the port will not fix it.** Next session: **fix the flap** (camera-boom stability + viewer-cell dead-zone). Tracked follow-ups: #78 terrain gating, look-in-from-inside sealing, look-in FPS, L-spotlight. ## What shipped this session (committed — see `git log` on this branch) ### 1. The HANG fix (the blocker) Indoor frames froze (`AppHangB1`; not a crash — captured the spinning managed stack via a `dotnet-stack` hang-watcher). Root cause: `PortalVisibilityBuilder.Build`'s portal-visibility flood **did not terminate** for real cottage geometry. Two layers, two fixes (both kept): - **A — drift-tolerant `CellView.Add` dedup** (`src/AcDream.App/Rendering/PortalView.cs`). The flood re-queues a cell every time its view GROWS; growth only stops when the dedup recognises a re-clipped region as a duplicate. The faithful `ProjectToClip` near-side clip drifts per round, so the old exact index-by-index match (eps 1e-4) never caught the near-duplicate → unbounded growth → O(n²) CPU-spin in `CellView.Add`. Fix: key each polygon by its vertices **snapped to a 1e-3 NDC grid**, consecutive-dedup'd, **canonically rotated** to a lex-min start → finite key space → convergence. Tests: `tests/AcDream.App.Tests/Rendering/CellViewDedupTests.cs` (3). - **B — bounded re-enqueue** (`src/AcDream.App/Rendering/PortalVisibilityBuilder.cs`). A alone did not fully converge (the spin relocated to `ScreenPolygonClip.ClipByEdge` — bounded loops — inside the still-non-terminating BFS). Restored the **`MaxReprocessPerCell = 16`** hard cap that Phase U.2a deleted ("fixpoint termination" left the loop with NO bound). **Kept the re-enqueue** — it is load-bearing for late-slice propagation (`Build_ViewGrowthAfterDoneCell_PropagatesNewSlicesToExit`). Pure enqueue-once was tried and **broke that test**, so re-enqueue is kept and merely bounded. - Deep diagnosis + the reassessment that led to B: `docs/research/2026-06-06-indoor-render-hang-rootcause.md`. - **Verified:** clean exit (255→0); runs indoors with no freeze; the indoor flood converges in ~1 round/cell at normal positions (measured 3–5 pops/frame, 1 view-poly/cell). The cap only bites at the metastable doorway. ### 2. The SEAL (verbatim DrawCells port — Task 2) `RetailPViewRenderer.DrawEnvCellShells` now iterates `IndoorDrawPlan.ShellPass(pvFrame)` — **every** visible cell's shell draws (was gated on `ClipFrameAssembler`'s slot filter → cells without a slot were silently dropped → grey clear-color void). Verified: interior seals + textured. (Task 1 `IndoorDrawPlan` + its test committed earlier as `bff1955`.) ### 3. Look-in FPS `GameWindow` exterior-look-in candidate cells limited to the player's landblock **±1** (was **all ~81 loaded landblocks** iterated every outdoor frame just to discard them via the 48 m seed cutoff). Provably no behavior change (excluded cells are >48 m, already culled). Outdoor FPS improved but still **~110 fps / ~9 ms (was ~200)** — `DrawPortal` still draws ~12 building interiors/frame (see follow-up). ## Baselines (must hold at next session start) - `dotnet build -c Debug` **0 errors**. - App.Tests **210/210** (205 baseline + IndoorDrawPlanTests 2 + CellViewDedupTests 3). - Core.Tests **1331 pass / 4 fail / 1 skip** — the 4 are pre-existing Physics door/step-up, unrelated. ## Structured live test — findings (Holtburg/Arcanum cottage, 2026-06-07) User walked a 6-step protocol (inside-still → camera-pan → doorway-threshold → just-outside → looking-at-cottage → cellar) and reported 8 behaviours; `ACDREAM_PROBE_FLAP` `[render-sig]` correlated each. | # | Observed | Cause | Bucket | |---|---|---|---| | 2,3,6,8 | walls briefly transparent / window+entrance "covered by the world background" / abrupt "teleport" through the doorway — all **at transitions (camera crossing a threshold)** | **THE FLAP** | viewer-cell stability (NEXT) | | 1 | outdoor grass covers the cellar-entrance hole (steady, looking in from outside) | outdoor terrain not gated over the indoor floor opening | **#78** terrain gating | | 7 | from inside, a building seen through the doorway has transparent walls (world-bg shows); pops back when you step outside | look-out shows other buildings unsealed | look-in/look-out completeness | | 5 | spotlight blobs on textures from the ceiling lamp (always been there) | point-light artifact | **L-spotlight** (separate) | | FPS | inside very high; outside **110 fps / ~9 ms** (was ~200) | `DrawPortal` draws ~12 interiors/frame | look-in cost | | 4 | cellar transitions **stable** ✓ | vertical transition doesn't cross the outdoor boundary | — | ### The FLAP — pinned (render-sig evidence) `[render-sig]` over the doorway shows the render branch + the cell it roots at flip-flopping while the **player cell stays inside**: ``` 50× branch=OutdoorRoot viewer=0xA9B40031 (outdoor) player=0xA9B40171 (indoor) gate=in 16× branch=RetailPViewInside viewer=0xA9B40170 (indoor) player=0xA9B40171 gate=in 113× branch=RetailPViewInside viewer=0xA9B40171 (indoor) player=0xA9B40171 gate=in ... oscillates 0x0031 ↔ 0x0170 ↔ 0x0171 frame-to-frame ... ``` **Mechanism:** the render roots at the **viewer (camera-eye) cell** (`clipRoot = viewerRoot`, Phase W "one viewpoint"). The 3rd-person boom drifts the eye across the doorway plane; acdream re-resolves the viewer cell fresh each frame with **no hysteresis** → it flips between outdoor `0x0031` and indoor `0x0170/0x0171` → the render flips `OutdoorRoot`↔`RetailPViewInside` → the indoor seal drops (walls transparent, outdoor world/grass shows) then re-seals → **flapping**. This is exactly the 2026-06-05 viewer-cell-flicker diagnosis, now confirmed against the live render branch. ## RECOMMENDED NEXT WORK — fix the FLAP (separate, already-designed) Per `docs/research/2026-06-05-viewer-cell-flicker-rootcause-and-fix-plan-handoff.md`, 3 retail-faithful parts: 1. **Viewer-cell dead-zone (do this first)** — ±0.2 mm cell hysteresis so a sub-mm eye drift can't flip the cell (`PhysicsCameraCollisionProbe.SweepEye`; retail `point_inside_cell_bsp` 0x53c1f0). Highest leverage — likely kills most of the flap on its own. 2. **Camera-boom stability** — stop the boom drifting at rest (`RetailChaseCamera.UpdateCamera`; retail `UpdateCamera` 0x456660). 3. **w-space (w=0) portal clip** — close-portal projection degeneracy (`PortalProjection` / `PortalVisibilityBuilder`; retail `GetClip` 0x5a4320 / `polyClipFinish` 0x6b6d00). Lower priority. Apparatus ready: `ACDREAM_PROBE_FLAP` emits `[render-sig]` (branch/viewer/player/gate per frame), `[flap]`, `[flap-cam]`, `[flap-sweep]` — light enough to launch with (the heavy `ACDREAM_PROBE_SHELL` firehose is what previously caused an I/O stall; avoid it). ## Tracked follow-ups (logged; not yet fixed) - **#78 terrain gating** — outdoor terrain (grass) draws over the indoor cellar-entrance hole (and likely other indoor floors). Decomp anchor `CEnvCell::find_visible_child_cell` (`acclient_2013_pseudo_c.txt:311397`). - **Look-in-from-inside** — buildings seen through your door/window from inside render unsealed (transparent walls); the look-out pass doesn't draw other buildings' shells. DrawCells port Task 5/7 territory (or R2 "outside-looking-in"). - **Look-in FPS** — `DrawPortal` draws ~12 building interiors every outdoor frame (~110 vs ~200 fps). Optimize: only look into buildings whose exit portals are frustum-visible; skip when no door is in view. - **L-spotlight** — ceiling-lamp point light makes spotlight blobs on textures. Pre-existing, separate. ## verbatim DrawCells port — remaining tasks (deferred) Plan: `docs/superpowers/plans/2026-06-06-verbatim-retail-indoor-render-port.md`. Task 1 + Task 2 done. **Task 3** (objects no-clip) is effectively already satisfied (objects draw membership-gated with no clip; no half-characters observed). **Tasks 4–8** (per-slice trim, look-out, delete `ClipFrameAssembler`, look-in, final) are **cleanup with no current visible payoff** — the seal works and there is **no visible bleed** (the "glitches between cells" were the FLAP, not bleed). **Task 4 (trim) is intricate** (its per-slice `_clipFrame.Reset()` is coupled with the landscape/particle passes that still read `clipAssembly` slots) and **risks re-slicing the working seal** — do it carefully, fresh, and only when clean architecture is the priority. ## DO NOT re-litigate - The HANG fix (A drift-dedup + B bounded re-enqueue) is correct + verified. **Do NOT try pure enqueue-once** — it breaks `Build_ViewGrowthAfterDoneCell_PropagatesNewSlicesToExit` (late-slice propagation needs the re-enqueue; the cap, not removal, is the termination guarantee). - The grey was the `drawableCells` / `ClipFrameAssembler` slot filter; Task 2 fixed it. The clip math is faithful — do not "harden the w-clip". - **The FLAP is NOT the DrawCells port.** It is viewer-cell metastability (camera/membership). Tasks 4–8 will NOT fix it. - The render roots at the VIEWER (camera-eye) cell intentionally (Phase W "one viewpoint"). The flap fix is to STABILISE the viewer cell (dead-zone + boom), NOT to re-root at the player cell (superseded). ## Copy-paste pickup prompt (next session) ``` Pick up the indoor-render work in worktree thirsty-goldberg-51bb9b (branch claude/thirsty-goldberg-51bb9b). PowerShell; launch logs UTF-16; build before launch; acceptance is the user's eyes. Do NOT branch/worktree, push, git stash/gc, or revert the dirty tree. Read first: docs/research/2026-06-07-indoor-render-session-handoff.md (state, what shipped, the FLAP diagnosis, do-not-relitigate). Then docs/research/2026-06-05-viewer-cell-flicker-rootcause-and-fix-plan-handoff.md (the flap fix plan). Confirm baselines: build 0 errors; App.Tests 210/210; Core.Tests 1331 pass / 4 fail (pre-existing) / 1 skip. The indoor HANG is fixed and the interior SEALS (shipped + committed last session). The remaining dominant visible issue is the FLAP at transitions — viewer-cell metastability: the render roots at the camera-eye cell, which oscillates outdoor↔indoor as the 3rd-person boom drifts across the doorway (no hysteresis), confirmed in [render-sig]. FIX THE FLAP, starting with the viewer-cell dead-zone (PhysicsCameraCollisionProbe.SweepEye; retail point_inside_cell_bsp 0x53c1f0), then camera-boom stability (RetailChaseCamera.UpdateCamera; retail UpdateCamera 0x456660). Launch with ACDREAM_PROBE_FLAP only (NOT ACDREAM_PROBE_SHELL — it stalls on I/O). Gate on the user's eyes at the cottage doorway. Do NOT: retry pure enqueue-once (breaks late-slice propagation); re-root render at the player cell (viewer-cell rooting is intentional); finish DrawCells port Tasks 4-8 expecting it to fix the flap (it won't). Tracked follow-ups (not the flap): #78 terrain gating (grass over cellar hole), look-in-from- inside sealing, look-in FPS (DrawPortal ~12 interiors/frame), L-spotlight. ```