The live retail cdb trace decided it: retail roots at the connector 0118 at the grey pose (NOT the PICK fork) AND still draws the player room 0116 from that root, because retail's InitCell side test reads the dat PortalSide bit where acdream's render path reconstructed the interior side from the cell AABB centroid (mis-sides a thin connector). ISSUES #186 -> CLOSED; handoff gets a RESOLVED banner. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
12 KiB
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 viatools/cdb/issue186-connector-decider.cdb) OVERTURNED both hypotheses below. Retail roots at the connector0118at the grey pose (same as acdream — so NOT the PICK fork) AND still DRAWS the player room0116from that root — because retail'sPView::InitCellside test reads the datPortalSidebit, 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 the0118→0116doorway → back-portal cull → grey). Fix = deriveInsideSidefrom the dat bit inGameWindow.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:
- This handoff.
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).docs/architecture/retail-divergence-register.mdrows 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).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=Yat every grey frame: the eye is geometrically INSIDE0118's BSP. The root is the fresh eye-derived sweptcurr_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 theeyeInRootflag 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: retailPView::ClipPortalsgates neighbour processing onif (ecx_8 != 0)(pc 005a5660) andAddViewToPortalsonview_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 onlyClearINSIDEif (outside_view.view_count > 0)(line 432715), right AFTERLScape::draw(the landscape draw, 432719). The Clear isClear(4, &RGBAColor_Black, 1.0f)(0x820fc0 = RGBAColor_Black) — but clearing COLOR after drawing the landscape would erase it, so that4flag 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) → dumpviewer_cell(aCObjCell*,acclient.h:35194) and its cell id. AND/ORbp acclient!PView::DrawCells(0x005a4840) → dumpcell_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 atpulledIn=-0.00where retail's PathClippedviewer_spheresweep hard-stops in0116). retailviewer_cell == 0118butcell_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):
qdONLY at a top-level threshold, never inside a conditional bp action (strands cdb → kills retail);dt acclient!Class @ecx fieldfor reads (@@c++is broken here);.sympath <local>(nosrv*, the;splits); spaced.if ()letsqddetach.
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) loads0116/0117/0118from 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'ssubagents/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
Clearis depth/stencil (post-LScape::draw); matching it would add non-retail stale-content. - Do NOT re-add the deleted
EyeInsidePortalOpeningrescue (PortalVisibilityBuilder.cs:314-319) blindly — it caused the0171↔0173flood-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 that0116isn't drawn, not that0118is 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-basedpoint_in_cell, current-cell-first hysteresis, port offind_cell_listpseudo_c:308788-308825). - Portal FLOOD:
PortalVisibilityBuilder.cs(Build; side-testCameraOnInteriorSide :857-863; empty-clip cull:320-324;PortalSideEpsilon :38-56; the off-screen-vs-sliver note:818-821).
7. Retail decomp anchors
PView::DrawCells0x005a4840(decomp line 432709): Clear gated onoutside_view.view_count>0(432715), Clear at 432732 (depth/stencil, post-LScape::draw); cells drawn unconditionally after (432815+).SmartBox::update_viewer0x00453ce0:viewer_cell = sphere_path.curr_cell(the render root = swept cell).PView::ClipPortalsempty-clip gateif(ecx_8!=0)pc 005a5660;AddViewToPortalsview_count!=0pc 005a5357.PView::InitCellside 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.