docs(#186): handoff — connector grey flap narrowed to the doorway-flap flood/pick family

Retail SEAMLESS at the same spot (user-confirmed) => real acdream bug: the eye seats
in a sparse 5-poly connector cell 0xF6820118 looking back at the player's room 0116,
and acdream drops 0116 (back-portal side-culled) so the doorway aperture shows the fog
clear color = grey; retail keeps 0116 drawn. RULED OUT: null-root/AD-20/AD-21 (root
valid, eyeInRoot=Y); the color-clear gating (retail's gated DrawCells Clear is
depth/stencil, post-LScape::draw). Next step = retail cdb trace (viewer_cell +
cell_draw_list at the grey pose) to pin viewer-cell PICK vs portal FLOOD, then a careful
frozen-render fix. Full handoff + apparatus + DO-NOT-RETRY + code/decomp sites in the doc.
Keeps the offline cell-geometry inspection test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-07-08 14:04:23 +02:00
parent cd42369581
commit 9d35a9786f
3 changed files with 266 additions and 11 deletions

View file

@ -0,0 +1,149 @@
# Handoff: #186 — indoor GREY flap at a top-floor connecting room (new house type)
**Date:** 2026-07-08 · **Status:** ROOT CAUSE NARROWED (report-only) — fix NOT started. 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.000.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.180.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 <local>`
(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`.