# Handoff: #186 — indoor GREY flap at a top-floor connecting room (new house type) > **✅ RESOLVED 2026-07-08 · fix `8257b9ba` · live gate PASSED.** The retail cdb trace (§3, run via > `tools/cdb/issue186-connector-decider.cdb`) OVERTURNED both hypotheses below. Retail roots at the > connector `0118` at the grey pose (same as acdream — so NOT the PICK fork) AND still DRAWS the player > room `0116` from that root — because retail's `PView::InitCell` side test reads the dat **`PortalSide` > bit**, while acdream's render path reconstructed the portal's interior side from the cell **AABB > centroid**, which mis-sides a THIN connector cell (`0118`, 5 render polys → centroid on the wrong side > of the `0118→0116` doorway → back-portal cull → grey). Fix = derive `InsideSide` from the dat bit in > `GameWindow.BuildLoadedCell`, matching retail + acdream's own physics (`CellTransit.cs:190`). Surgical > (dat diagnostic: agrees with the old centroid on every portal but the one #186 broke). **The §2/§3 > "acdream drops the room" read was right; the "acdream over-switches the root / flood-epsilon" mechanism > was WRONG — it was the InsideSide *source*, not the pick or the epsilon.** Everything below is the > pre-fix investigation, kept for the trail. **Date:** 2026-07-08 · **Status:** ~~ROOT CAUSE NARROWED (report-only) — fix NOT started.~~ RESOLVED (see banner). It lives in the FROZEN portal-flood / viewer-cell render code (the doorway-FLAP family that cost weeks), so it needs a **retail cdb trace** to pin the last fork BEFORE any code, then a careful change. Milestone: M1.5. **Read these first, in order:** 1. This handoff. 2. `claude-memory/project_render_pipeline_digest.md` — the render/doorway-FLAP SSOT + DO-NOT-RETRY table ("root at VIEWER cell"; the deleted EyeInsidePortalOpening rescue; PortalSideEpsilon root-lag). 3. `docs/architecture/retail-divergence-register.md` rows **AD-17** (≤8 clip-plane / union-AABB scissor), **AD-20** (camera-sweep fallback seeds eye cell from PLAYER cell), **AD-21** (null-clipRoot outdoor fallback). 4. `docs/ISSUES.md` #186. --- ## 0. Symptom (user, live) Top floor of a **house type not tried before**; a connecting opening/room between two parts of the house. Passing through → a brief **GREY** flap (the framebuffer clear color). Stopping at the precise spot → the WHOLE screen is grey and stays; **turning the camera clears it, turning back → grey again** (camera-DIRECTION dependent, player stationary). A jump is NOT involved (that's #185). **Retail is SEAMLESS at the same spot (user-confirmed) → this is a real acdream bug, not inherent.** ## 1. The cells + the grey pose (ACDREAM_PROBE_FLAP capture) Landblock `0xF682`. Three cells, all envId `0x0365`, cell-struct origin `(132,84,62)`: | cell | render polys | physics polys | portals | role | |---|---|---|---|---| | `0xF6820116` | **15** | 12 | →`0117,0118,0113` | player's room (closed) | | `0xF6820117` | **10** | 6 | →`0118,0116,0115,0119` | next room (closed) | | `0xF6820118` | **5** | 3 | →`0117,0116` | **thin connector** (root when grey) | Grey-pose facts (from `[flap]`/`[flap-cam]`/`[flap-sweep]`, 88,249 frames; the grey frames are `vis=1` `root=0xF6820118`, ~6,725 frames): - `[flap-cam]`: `root=viewerCell=0xF6820118 playerCell=0xF6820116 eyeInRoot=Y playerInRoot=n terrain=Skip outVisible=False eye=(134.81,83.59,69.07) player=(137.30,83.32,66.82)`. - `[flap]` grey line: `root=0118 | p0->0x0117 D=0.14 TRV proj=4 clip=0 | p1->0x0116 D=0.18 CULL proj=5 clip=4 || outPolys=0 vis=1`. (Dominant D to 0117 ≈ 0.14; ALWAYS positive 0.00–0.50 → eye is genuinely INSIDE 0118, never across the plane.) - `[flap-sweep]`: `ok=True resolved=Y pulledIn=-0.00` (the chase boom reaches FULL distance, NO collision pull-in, seating the eye in the connector). **Read of the geometry:** the eye sits in the sparse connector `0118`, in the doorway threshold, looking BACK toward the player's room `0116`. `p1->0116` is ON-screen (proj=5 clip=4) but side-CULLED (eye ~0.18–0.31 on `0116`'s side of the p1 plane — a back-portal). `p0->0117` is genuinely OFF-screen (`clip=0` with proj≥3 = off-screen per the builder's own probe doc, `PortalVisibilityBuilder.cs:818-821`). So no neighbour floods, only the 5-poly connector draws, and its shell doesn't cover the forward view → the framebuffer clear color (fog, `GameWindow.cs:8968`) shows through = GREY. Turning re-aims off the back-portal → a room floods → picture. ## 2. What is CONFIRMED / RULED OUT (workflow `wf_362bbeda-933`, 4 tracers + synthesis, + my own re-checks) - ❌ **NOT null viewer-root / AD-21** — the render root is a valid indoor cell on 88,248/88,249 frames (one lone `[outdoor-node] root=OUT`). (This refutes THE FIRST hypothesis in this investigation.) - ❌ **NOT viewer-cell root-lag / AD-20** — `eyeInRoot=Y` at every grey frame: the eye is geometrically INSIDE `0118`'s BSP. The root is the fresh eye-derived swept `curr_cell` (`PhysicsCameraCollisionProbe.cs:92`), NOT a stale player-seeded fallback. (Refutes the workflow synthesis's own leading "31 cm gap" theory — the eye is NOT across the boundary; I checked the `eyeInRoot` flag directly.) - ❌ **NOT the flood-admission being unfaithful** — the empty-clip cull (`PortalVisibilityBuilder.cs:320-324`) and the back-portal side-CULL (`:292-298`, `CameraOnInteriorSide :857-863`) are faithful ports: retail `PView::ClipPortals` gates neighbour processing on `if (ecx_8 != 0)` (pc 005a5660) and `AddViewToPortals` on `view_count != 0` (pc 005a5357). #177 already proved acdream's flood is retail-identical. - ❌ **NOT the color clear (the tempting "3-line fix" — DEAD END, do not revisit).** Retail's DrawCells (`0x005a4840`, decomp line 432709) has its only `Clear` INSIDE `if (outside_view.view_count > 0)` (line 432715), right AFTER `LScape::draw` (the landscape draw, 432719). The Clear is `Clear(4, &RGBAColor_Black, 1.0f)` (`0x820fc0 = RGBAColor_Black`) — but clearing COLOR *after* drawing the landscape would erase it, so that `4` flag is **depth/stencil, not color**. So retail's color clear is NOT gated here, and matching "don't color-clear a sealed interior" would be WRONG. (This is why I did the cdb prep — it killed a wrong fix before shipping.) - ✅ **It IS a real acdream bug** (retail seamless) in the **doorway-FLAP flood/pick family**: acdream drops the room the camera looks back at (`0116`) from the connector root; **retail keeps it drawn.** ## 3. THE ONE OPEN FORK → the retail cdb trace (do this FIRST in the new session) Retail is seamless, so retail either (A) **roots at the player's room `0116`, not the connector** (a viewer-cell PICK divergence — acdream over-switches to `0118`), or (B) **roots at `0118` but its flood still admits `0116`** (a FLOOD divergence — retail keeps the back-portal / uses an on-screen sliver acdream drops). Static decomp cannot decide; observe retail. **Toolchain (CLAUDE.md "Retail debugger toolchain"; binary CONFIRMED pairs with `refs/acclient.pdb` via `py tools/pdb-extract/check_exe_pdb.py "C:/Turbine/Asheron's Call/acclient.exe"` → MATCH).** - User: launch retail, connect to local ACE, walk to the SAME connecting room, and **HOLD the grey pose** (so most captured frames are it). - `bp acclient!SmartBox::update_viewer` (`0x00453ce0`) → dump `viewer_cell` (a `CObjCell*`, `acclient.h:35194`) and its cell id. AND/OR `bp acclient!PView::DrawCells` (`0x005a4840`) → dump `cell_draw_num` + `cell_draw_list.data[]` ids (`acclient.h:45939-45940`) — the drawn cell set. - Decide: retail `viewer_cell == 0116` → **PICK fix** (acdream's viewer-cell pick/hysteresis over-retains the connector — `CellTransit.cs:862-916 BuildCellSetAndPickContaining`, or the camera boom coasting the eye into the connector at `pulledIn=-0.00` where retail's PathClipped `viewer_sphere` sweep hard-stops in `0116`). retail `viewer_cell == 0118` but `cell_draw_list` ⊇ `0116` → **FLOOD fix** (retail admits the back-portal from the connector; acdream's side-CULL / the deleted EyeInsidePortalOpening rescue is the site). - cdb watchouts (CLAUDE.md): `qd` ONLY at a top-level threshold, never inside a conditional bp action (strands cdb → kills retail); `dt acclient!Class @ecx field` for reads (`@@c++` is broken here); `.sympath ` (no `srv*`, the `;` splits); spaced `.if ()` lets `qd` detach. ## 4. Apparatus (re-create in the new session — temp files won't persist) - **Flap capture:** launch acdream with `ACDREAM_PROBE_FLAP=1` (canonical live cmd, CLAUDE.md). Emits per frame: `[flap]` (root + per-portal proj/clip/side, `PortalVisibilityBuilder.cs:790`), `[flap-cam]` (`root/viewerCell/playerCell/eyeInRoot/playerInRoot/terrain/outVisible`, `GameWindow.cs:11142-11152`), `[flap-sweep]` (`ok/resolved/pulledIn/viewerCell`, `PhysicsCameraCollisionProbe.cs:74-87`), `[outdoor-node]` (`root=IN/OUT`). ⚠️ heavy (per-frame); output goes to the raw background-task `.output`, not the Tee'd log. Have the user HOLD the grey pose + turn to clear + turn back a couple times. - **Offline cell geometry dump:** `tests/AcDream.Core.Tests/Physics/Issue186ConnectorCellGeometryInspectionTests.cs` (`Dump_ConnectorCells_ShellAndCollision`) loads `0116/0117/0118` from the dat and prints render/physics poly counts + normal-axis buckets. No live launch needed. (That's how the §1 table was produced.) - **Decomp workflow:** `wf_362bbeda-933` (this session) — the 4-tracer + synthesis analysis; results in its journal under the session's `subagents/workflows/` dir. ## 5. DO-NOT-RETRY (each already refuted or a known trap) - Do NOT re-open the **null-root / AD-20 / AD-21** angle — root is valid, `eyeInRoot=Y`. - Do NOT implement the **color-clear gating** — retail's gated `Clear` is depth/stencil (post-`LScape::draw`); matching it would add non-retail stale-content. - Do NOT re-add the deleted **`EyeInsidePortalOpening` rescue** (`PortalVisibilityBuilder.cs:314-319`) blindly — it caused the `0171↔0173` flood-cycle churn (the ORIGINAL doorway flap). - Do NOT widen **`PortalSideEpsilon`** (`:38-56`) — a documented root-lag band, NOT retail (F_EPSILON=0.0002); BR-4 refuted tightening/widening without eye-exact tracking. - Do NOT "fix" the flood by propagating an empty/edge-on portal — the empty-clip cull is retail-faithful. - The sparse connector geometry (`0118` = 5 render / 3 physics polys) is FAITHFUL to the dat (retail draws the same). It is context, not the bug — the bug is that `0116` isn't drawn, not that `0118` is thin. ## 6. Key code sites - Frame-top clear (unconditional color+depth+stencil to fog): `GameWindow.cs:8968-8983`. **NOT the fix.** - Viewer-cell resolution: `GameWindow.cs:9137-9148` (`viewerCellId = _retailChaseCamera.ViewerCellId`). - Camera boom sweep: `PhysicsCameraCollisionProbe.cs` (`SweepEye`, PathClipped, `pulledIn`, `r.CellId=curr_cell`). - Viewer-cell PICK: `CellTransit.cs:862-916` (`BuildCellSetAndPickContaining`, center-based `point_in_cell`, current-cell-first hysteresis, port of `find_cell_list` pseudo_c:308788-308825). - Portal FLOOD: `PortalVisibilityBuilder.cs` (`Build`; side-test `CameraOnInteriorSide :857-863`; empty-clip cull `:320-324`; `PortalSideEpsilon :38-56`; the off-screen-vs-sliver note `:818-821`). ## 7. Retail decomp anchors - `PView::DrawCells` `0x005a4840` (decomp line 432709): Clear gated on `outside_view.view_count>0` (432715), Clear at 432732 (depth/stencil, post-`LScape::draw`); cells drawn unconditionally after (432815+). - `SmartBox::update_viewer` `0x00453ce0`: `viewer_cell = sphere_path.curr_cell` (the render root = swept cell). - `PView::ClipPortals` empty-clip gate `if(ecx_8!=0)` pc 005a5660; `AddViewToPortals` `view_count!=0` pc 005a5357. - `PView::InitCell` side test decomp:432962. `RGBAColor_Black` @ `0x820fc0`. ## 8. Acceptance - No grey flap passing through OR stopping in the connecting room at any camera angle; matches retail's seamless. - Regression: the outside→inside doorway + other multi-room houses + the #177/#181 render-flap suites unchanged. ## 9. Context: this session also FIXED #185 (unrelated, DONE + gated) The outdoor-stairs "invisible wall" (`07c5b832`) — a shadow part-id uint32 overflow dropping stair collision. Unrelated to #186 (that was collision/registration; #186 is render/visibility). See `docs/superpowers/specs/2026-07-08-185-outdoor-stairs-fix-design.md`.