# Phase U.4c — flap FIXED (root visibility at the player's cell) + residuals handoff (2026-05-31) ## TL;DR The **U.4c doorway "flap" is fixed.** Root cause (converged on live evidence, after two wrong turns): indoor portal visibility was rooted at the **3rd-person camera EYE**, which drifts out of the player's cell (through interior walls into AABB gaps); `FindCameraCell` then returned a **stale cell for 3 grace frames**, and from that stale root the doorway portal was culled as "behind" the eye → the exit cell + terrain + building shells all dropped → grey-void collapse. **Fix (`0ee328a`):** root indoor visibility (cell resolution + portal-side test) at the **PLAYER's cell** (retail `CellManager::ChangePosition` tracks `curr_cell` by the player; acdream already roots lighting at the player for the same chase-cam reason). The eye still drives the per-frame projection. Visual gate (2026-05-31): the user confirmed **"flap seems gone."** But the gate also revealed the indoor pipeline is **not yet seamless** — three **separate, largely-known** residuals remain (terrain not gated inside #78, camera-displacement / camera-collision, U.5). Those are a **broader new scope** than the flap; user chose to **checkpoint + hand off**. M1.5 "indoor world feels right" needs them. ## The fix that shipped (`0ee328a`) `GameWindow.OnRender`: introduce `visRootPos = (_playerMode && _playerController is not null) ? _playerController.Position : camPos`, and feed it to BOTH `_cellVisibility.ComputeVisibility(...)` and `PortalVisibilityBuilder.Build(clipRoot, visRootPos, …)`. The camera eye (`camPos` / `envCellViewProj`) still drives the per-frame projection and `_envCellRenderer.PrepareRenderBatches` frustum/render cull. Removed the earlier synthetic builder flap test (it modeled the disproven side-test hypothesis; the fix is integration-level). App tests 151/151. Two-stage Opus review: clean (no Critical/Important; downstream-consumer audit — sky/weather/fog/sun/entity-cull/cache — all internally consistent under player-rooting; `cameraInsideCell` is now a harmless misnomer → cosmetic rename suggested as a follow-up). ## The hypothesis journey (DO NOT re-walk these — all evidence-disproven) - **H1 (PVS / stab_list grounding)** — the spec's leading hypothesis. The live evidence showed the flap is a *root* problem, not a *set-membership* problem; force-keeping cells would diverge from retail's walk-reached `cell_draw_list`. The Layer-1 data (`LoadedCell.VisibleCells` + `SeenOutside`, `639f20f`) was plumbed and is harmless/available but is NOT what fixed the flap. - **H2 (port the dat `PortalSide` into the side test)** — I pivoted here on a raw 0/1 anti-correlation in `A8CellAudit`. **DISPROVEN** (`b5f2bf2`): under the `(Flags&2)==0` boolean convention + our normal winding, the dat `PortalSide` sense is **byte-identical** to our centroid `InsideSide` at every pose — the swap is a literal no-op. The implementer subagent caught this via the real-data validation loop and refused to ship the no-op. - **"eye crosses the plane while inside the cell"** — **REFUTED** (`8941d1e`): `0171`'s local AABB is y∈[-7.65,1.15]; the `0171→0170` portal plane is at y=-7.65 (the cell's *min* boundary), so an eye genuinely inside `0171` always has side-test D≤0 → always traverses. The eye must be *outside* the cell for the cull — i.e. a ROOT problem (grace-stale), confirmed by the moving capture. The decisive apparatus was `ACDREAM_PROBE_FLAP` (per-frame `[flap]`/`[flap-cam]` lines): flap frames were uniformly `res=Grace eyeInRoot=n terrain=Skip`; good frames `eyeInRoot=Y`. See `docs/research/2026-05-31-u4c-flap-characterization.md` (top banner = the converged reading). ## Residuals revealed at the visual gate (the NEXT work — NOT the flap) User observations (inside a Holtburg cottage, this session's cells `0xA9B40162/0174/0175`) mapped to cause: 1. **"Floor shows outdoor ground / cellar floor transparent / I see the world from below."** → **Issue #78 family** (already known; CLAUDE.md flags it: *"outdoor terrain mesh visible inside cottage cellars … indoor-cell visibility culling not gating outdoor terrain"*). Terrain isn't occluded by the interior. The flap fix made it MORE visible because terrain now draws again (it was Skipped during the flap). **Fix direction:** gate outdoor terrain by indoor-cell visibility when the camera/player is in a cell — port retail `CEnvCell::find_visible_child_cell` (`acclient_2013_pseudo_c.txt:311397`) / the `seen_outside` landscape-keep logic; relate #78, #100 secondary finding. 2. **"Outer walls transparent when looking out."** → the per-frame clip is projected from the **displaced eye** (outside the player's cell **79%** of frames: 3049 `eyeInRoot=n` vs 795 `=Y`). The OutsideView over-includes (scissor AABB fallback fired 320×; portals project far off-screen e.g. `ndc=(-9.8,0.7)(-9.4,-10.5)`) → terrain bleeds past the window opening. **Fix direction:** camera collision — retail `SmartBox::update_viewer` keeps the viewer inside the cell so the clip stays consistent (`eyeInRoot` ≈ always Y). Reopens the parked camera scope. 3. **"On the stairs: everything grey, I see doors/NPCs/particles from all around (other houses')."** → transition-zone cell resolution on the stairwell (player straddling cells) + the displaced eye seeing un-occluded entities. Same camera-displacement + terrain-gating family. (Stairs/cellar is the #98-saga area — complex geometry.) 4. **"Outside looking in: no interior walls."** → **U.5** (outdoor-camera → building-interior peering), already deferred. **Net:** M1.5 "indoor world feels right" needs THREE more pieces beyond the flap — **#78 terrain gating inside**, **camera collision (keep the eye in the player's cell)**, and **U.5**. The camera-displacement root (residuals 2+3) is the highest-leverage: with the eye constrained to the player's cell, the eye-projected clips become consistent and both 2 and much of 3 resolve. ## Apparatus (kept — gated/inert when off; available for the residual work) - **`ACDREAM_PROBE_FLAP=1`** (`RenderingDiagnostics.ProbeFlapEnabled`) — per-frame `[flap]` (root, eye, localEye, per-portal D/side/proj/clip/NDC, outPolys, vis) + `[flap-cam]` (res, eyeInRoot, eye, player, terrain mode, outdoorVisible). `CellVisibility.LastCameraCellResolution` + `CameraCellResolution` enum added. Per-frame (not cell-change-throttled) so it catches flicker at a stable root. Throwaway — strip when the residual indoor work is done. - **`tools/A8CellAudit/Program.cs` `portals` subcommand** — per-portal plane / centroid-`InsideSide` / dat `PortalSide` / local AABB / swept-pose O-vs-A-vs-B sense comparison. The tool that disproved H2. `PortalFlags.PortalSide = 0x2`, `ExactMatch = 0x1`. ## Commit list (all on `claude/thirsty-goldberg-51bb9b`, UNPUSHED) ``` 0ee328a fix(render): U.4c — root indoor visibility at the player's cell (THE FLAP FIX) 8941d1e research: refute eye-crosses-plane; correct stale H2 note (+ AABB dump) b5f2bf2 research: DISPROVE the side-test fix (PortalSide port is a no-op) 13d58ca research: characterize the flap on real dat evidence + probe commits (ProbeFlapEnabled, NDC/clip probe), the spec (31f265d) + plan (211350b), Layer-1 data plumbing (639f20f), the RED-apparatus (cd3ffe3, removed in 0ee328a). ``` (Plus the converged-note update + this handoff.) ## Git state - Branch `claude/thirsty-goldberg-51bb9b`, **UNPUSHED** (push decision pending with the user). - Two `git stash` entries preserved (#98/#101 WIP) — do NOT drop. ## Next-session pickup prompt ``` U.4c doorway flap is FIXED (commit 0ee328a — root indoor visibility at the player's cell, not the camera eye; reviewed clean). READ docs/research/2026-05-31-u4c-flap-fixed-and-residuals-handoff.md FIRST. The visual gate revealed three SEPARATE residuals (NOT the flap): (1) #78-family — outdoor terrain not gated inside (floor shows ground / see world from below); (2) camera displacement — the chase eye is outside the player's cell 79% of frames so the eye-projected clip over-includes (transparent outer walls), fixed by camera collision (retail SmartBox::update_viewer keeping the eye in the cell); (3) U.5 outside-looking-in (deferred). Highest leverage = camera collision (fixes 2 and much of 3). Apparatus ACDREAM_PROBE_FLAP + A8CellAudit are committed + ready. Brainstorm the camera-collision-vs-terrain-gating ordering before coding. Do NOT re-try H1 (PVS grounding) or H2 (PortalSide side-test) — both evidence-disproven (see the handoff). NO workarounds. ```