docs: SHELL-SEALING / wrong-flood-root handoff (cellar floor + interior walls grey)

The user's primary symptom (interior walls + cellar floor render as grey clear-color
with dynamic objects / outdoor slices showing through; flicker at room/cellar
transitions) is the KNOWN R1-completion problem: the PView flood roots at the CAMERA
cell (viewer cell), and when the camera is in a different interior cell than the player
(room 0171 vs cellar 0174), the flood does not seal the player's cell. Decisive
evidence: flap-cam root=0xA9B40171 playerCell=0xA9B40174. This handoff separates the
two problems I conflated, lists the disproven causes, gives the next diagnostic step
(shell/flap/vis probes in the cellar), and a kickoff prompt. HEAD 2b7f5a1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-05 17:04:52 +02:00
parent 2b7f5a16c6
commit 8116d101bc

View file

@ -0,0 +1,239 @@
# Handoff — Indoor SHELL-SEALING / wrong-flood-root (cellar floor + interior walls drop to grey) — 2026-06-05 (PM/eve)
> **For the next session/model. Read this FIRST.** The previous model (me) spent this session on the
> "viewer-cell flicker" 3-part plan, shipped two real-but-partial fixes, and **conflated two distinct
> problems**. The user's PRIMARY pain — **interior walls + cellar floor render as grey background with
> dynamic objects / outdoor slices showing through, plus flicker when changing rooms / entering the
> cellar** — is the **KNOWN, already-documented "R1-completion" problem**, NOT the boom and NOT the
> branch I touched. Get a SCREENSHOT + `[shell]` evidence EARLY; do not re-litigate the disproven causes
> below.
>
> **Tree:** branch `claude/thirsty-goldberg-51bb9b`, worktree
> `C:/Users/erikn/source/repos/acdream/.claude/worktrees/thirsty-goldberg-51bb9b`. HEAD `2b7f5a1`.
> Do NOT branch/worktree. Do NOT push without asking. NEVER `git stash`/`gc`. PowerShell on Windows;
> launch logs are UTF-16 (use `Select-String`/`Get-Content`, they handle it).
---
## 0. The honest TL;DR
- The user reports (with a screenshot): standing **inside** the cottage, the **floor draws but the walls
are grey** (the time-of-day clear color), with NPCs/doors/chests and a slice of the **outdoor world**
floating in the grey. In the **cellar**, the **floor is missing** (just grey). It **flickers** when
changing rooms / entering the cellar. This persisted through both of this session's fixes.
- **Decisive evidence (live, this session):**
```
[flap-cam] root=0xA9B40171 viewerCell=0xA9B40171 playerCell=0xA9B40174 eyeInRoot=Y
eye=(153.46,6.66,94.92) player=(153.58,8.88,92.76) terrain=Planes outVisible=True
[flap] root=0xA9B40171 ... | p0->0170 proj=4 | p1->0173 proj=0 | p2->0175 proj=5 || outPolys=1 vis=4
```
The render **roots the visibility flood at the CAMERA's cell `0171`** (the room, z≈95), but the
**PLAYER is in `0174`** (the cellar, z≈93). The flood reaches `vis=4` cells **from the room**; the
player's actual cell `0174` is **not sealed** by it (the stair-chain portal `p1→0173` is `proj=0`).
So the cellar shell around the player isn't drawn → grey.
- **This is exactly what the 2026-06-05 Residual-A handoff already flagged as NEXT:** *"when the player
is in the cellar but the eye is up in the room, clipRoot = room → the PVS flood from the room does NOT
reach the cellar → the cellar floor drops."* And the 2026-06-02 design doc §3 calls the grey a
**SHELL-SEALING bug** ("the closed cell mesh is not covering those pixels"). **The answer was in the
docs the whole time.**
- **Two fixes shipped this session (both real, both PARTIAL, neither is the cellar/walls fix):**
- `d2212cf` — Part 1 camera boom convergence snap (retail `UpdateCamera` 0x00456fcd). Freezes the
at-rest boom drift. Test-covered. The user confirmed the at-rest room was **never** flickering, so
this fixed a real-but-invisible thing. KEEP (faithful) or reassess.
- `2b7f5a1` — branch inside/outside on `is_player_outside` (retail `RenderNormalMode` 0x453aa0) instead
of the camera cell. **Reduced** the player-OUTSIDE doorway grey ("reduced a lot" — user). Test-covered.
Does NOT touch the player-inside case. KEEP (no regression — see §4) or reassess.
---
## 1. The two problems (do NOT conflate them — I did, and it cost the session)
| | Problem A — doorway transition grey | Problem B — interior shell not sealed (THE user's pain) |
|---|---|---|
| **When** | Player **fully outside** (landcell `<0x100`), camera lags **inside** the doorway | Player **inside** cell X, camera **inside** a DIFFERENT cell Y (camera in room, player in cellar) |
| **Why** | Branch keyed on camera → wrongly ran `DrawInside` rooted at the threshold | Flood roots at the camera's cell Y; the player's cell X isn't in/sealed by the flood from Y |
| **Symptom** | Whole screen grey at the in↔out threshold | Walls/cellar-floor grey while standing inside; flicker at room/cellar transitions |
| **Status** | **Reduced** by `2b7f5a1` (retail-verified) | **UNSOLVED** — this is the real target |
| **Evidence** | `outPolys=0` while the exit portal projects full-screen (`proj=6 clip=8`) | `root=0171` vs `playerCell=0174`; `vis=4` from the room excludes the cellar |
**Problem B is the one to fix.** Problem A's fix is a genuine retail-faithful improvement that happens to
share a "grey" symptom, which is what made me conflate them.
---
## 2. Problem B — what we know, and the core open question
**The render roots the PView flood at `clipRoot = visibility.CameraCell` (the VIEWER/camera cell)** —
`GameWindow.cs:7322` (now wrapped by the `ShouldRenderIndoor` branch, but still the **viewer cell** when
indoor). Retail `RenderNormalMode` (0x453aa0:92675) literally calls `DrawInside(this->viewer_cell)`. So
rooting at the viewer cell is retail-faithful **on its face**.
**But** when the 3rd-person camera is in a different interior cell than the player (room vs cellar), the
flood from the camera's cell does **not** seal the player's cell → the geometry **around the player**
(cellar floor, near walls) isn't drawn → grey. The decisive `[flap-cam]` line (`root=0171`,
`playerCell=0174`) is the whole story.
**The core open question the next model MUST resolve against the decomp (don't guess — this is the
crux):** how does **retail** avoid this? Candidate mechanisms, each needs decomp verification:
- **(a) Retail's collided camera stays in (or floods to) the player's cell.** Residual A
(`SmartBox::update_viewer` 0x00453ce0, swept `viewer_sphere`) is shipped, but verify: does retail's
`viewer_cell` actually equal the player's cell when the player is in the cellar and the boom would put
the eye up in the room? The spring arm sweeps from the **head-pivot** — if the pivot is the player's
head in the cellar, the swept eye may be stopped by the cellar ceiling and stay in `0174`. **Check
whether acdream's `viewer_cell` SHOULD be `0174` here and isn't** (i.e. the camera-collision/cell-
resolution is putting the eye in `0171` when retail would keep it in `0174`).
- **(b) Retail's flood from the viewer cell DOES reach the player's cell**, because retail's portal clip
is robust and the stair-chain portals don't go `proj=0`. Here `p1→0173 proj=0` stops the flood. Is
`0174` reachable only through `0173` (which is culled)? If so, the flood-reach is the bug, not the root.
- **(c) The design doc §5 intent: visibility roots at the PLAYER's physics cell** (`CellGraph.CurrCell`),
**eye drives projection only.** The 2026-06-05 viewer-cell handoff said *don't* switch the root to the
player cell ("retail roots `DrawInside` at viewer_cell"). **This is a real contradiction in our own
docs** — design doc §5 says player-cell-roots-visibility; the later handoff says viewer-cell-roots.
The next model should settle it from the decomp: in `RenderNormalMode`, `viewer_cell` is the argument
to `DrawInside` — but WHAT sets `viewer_cell`, and is it ever != the player's cell in normal play? If
retail's `viewer_cell` is always the player's cell (because the camera collision keeps it there), then
(a) and (c) converge and acdream's bug is that its `viewer_cell` drifts to the camera's room cell.
**Strong hypothesis to test first (cheap):** acdream's `viewer_cell` (`visibility.CameraCell`) is wrong
here — it's the *room* because the eye is geometrically up in the room, but **retail's collided
`viewer_cell` would be the cellar** (the swept sphere from the head-pivot is stopped by the cellar
ceiling / the pivot is in the cellar). I.e. this is a **camera-cell-resolution** bug, not a flood/root
bug. Verify by reading how `visibility.CameraCell` / `viewerCellId` is computed (`CellVisibility.
FindCameraCell` + `PhysicsCameraCollisionProbe.SweepEye`) and whether the pivot/sweep should keep the
cell in `0174`.
---
## 3. The next diagnostic step (do this BEFORE any fix — evidence first)
The apparatus is committed and ready. Stand the player in the cellar with the camera up in the room
(the exact repro), and capture:
```powershell
$env:ACDREAM_DAT_DIR="$env:USERPROFILE\Documents\Asheron's Call"; $env:ACDREAM_LIVE="1"
$env:ACDREAM_TEST_HOST="127.0.0.1"; $env:ACDREAM_TEST_PORT="9000"
$env:ACDREAM_TEST_USER="testaccount"; $env:ACDREAM_TEST_PASS="testpassword"
$env:ACDREAM_PROBE_FLAP="1"; $env:ACDREAM_PROBE_SHELL="1"; $env:ACDREAM_PROBE_VIS="1"
dotnet run --project src\AcDream.App\AcDream.App.csproj --no-build -c Debug 2>&1 | Tee-Object launch.log
```
Pin these three things from the log while standing in the cellar:
1. **Is the player's cell (`0174`) in the visible set?** `[flap-cam]` shows `root=` vs `playerCell=`;
`[vis]`/`[flap]` shows `vis=` count + (turn on `ACDREAM_PROBE_VIS` for the cell-id list). If `0174`
is NOT in the set → **flood-reach / wrong-root** problem (§2 a/b/c).
2. **If `0174` IS in the set, does its shell DRAW?** `[shell]` probe (`ACDREAM_PROBE_SHELL`) reports
per-cell shell draw (geometry/texture/depth). If `0174` is visible but its shell is skipped / a
polygon is back-facing / depth-culled → **mesh-seal** problem (design doc §3: dat-dump the `0174`
EnvCell mesh, look for a missing/back-facing floor polygon).
3. **What `viewer_cell` SHOULD it be?** Compare the live `viewerCell=` to where the player is. If the
camera collision (Residual A) is failing to keep the eye's cell == the player's cell, that's §2(a).
This single capture discriminates root-vs-flood-vs-mesh. **Don't pick a fix until it does.**
---
## 4. Exactly what's committed this session (and why each is safe to keep)
| SHA | What | Keep? |
|---|---|---|
| `d2212cf` | Part 1 boom convergence snap — `RetailChaseCamera.ApplyConvergenceSnap` + wiring; retail `UpdateCamera` 0x00456fcd (`SnapEpsilon=2×0.000199999995`, `RotCloseEpsilon=0.000199999995`). 4 new App tests. | **Keep** — retail-faithful, fixes at-rest boom drift, no regression. Not the visible fix. |
| `2b7f5a1` | Branch inside/outside on `is_player_outside` (`RenderingDiagnostics.ShouldRenderIndoor` + `GameWindow.cs:7322`). 5 new Core tests. | **Keep** — retail-faithful, reduced Problem A. **Provably no Problem-B regression:** for the player-inside case it yields `clipRoot = CameraCell`, identical to the pre-fix `visibility?.CameraCell`. |
Plan doc (Part 1's TDD steps; Parts 2/3 there are SUPERSEDED by this handoff —
see §5): `docs/superpowers/plans/2026-06-05-indoor-viewer-cell-flicker-fix.md`.
**Working tree is clean** (the TEMP w-stat probe added to `PortalVisibilityBuilder.cs` was stripped;
`git status` shows nothing under src/tests). Test baseline: **App 187p/0f**, **Core 1331p / 4f / 1s**,
build green. The 4 Core fails are the documented set (2× `DoorBugTrajectoryReplay.LiveCompare_*`,
`BSPStepUpTests.D4`, `DoorCollisionApparatusTests`).
---
## 5. DO NOT RE-LITIGATE (evidence-disproven this session)
- **The flicker is NOT viewer-cell oscillation / cell-membership instability.** Captured the render gate
(`terrain=Skip/Planes`, `outVisible`) flapping with the **viewer cell STABLE at `0171`**. The planned
**Part 2 (point_inside_cell_bsp ±0.2 mm dead-zone) was NOT implemented and is NOT the fix.**
- **The doorway grey is NOT the portal PROJECTION degenerating.** At the grey frame the exit portal
`p0→0170` projects **full-screen** (`proj=6 clip=8`, ndc spans ±1) while `outPolys=0`. So
`ProjectToNdc` is fine; the **`OutsideView`/flood assembly** (and, per §2, the **root/flood-reach**)
is the issue. Do not "harden the w-clip" (5f596f2 already did the clip-space side-plane clip; the
9f95252 eye-in-portal flood band-aid is still in — reassess only after Problem B is understood).
- **The boom drift (Part 1) was real but is NOT the visible flicker.** Freezing the boom did not change
the user-visible symptom.
- The 3-part plan's framing (flicker = boom + cell dead-zone; void = clip) was the previous-session
hypothesis; this session's live evidence **reassigns** the dominant symptom to Problem B (shell
sealing / flood root). Treat the plan's Parts 2-3 as superseded.
---
## 6. Canonical prior art (already documents Problem B — read these)
- **`docs/research/2026-06-05-render-residual-a-shipped-core-inside-render-handoff.md`** — *"player in
cellar, eye in room → clipRoot=room → flood doesn't reach the cellar → cellar floor drops. NEXT = the
CORE inside render (R1 completion)."* THE pointer.
- **`docs/superpowers/specs/2026-06-02-render-pipeline-redesign-design.md`** §2 (the binary
`RenderNormalMode` model), §3 (grey = SHELL-SEALING bug; `[shell]` probe + dat dump), §5 (the
two-camera invariant — and the doc/handoff contradiction to settle, §2 above).
- **`memory/reference_render_pipeline_state.md`** — Residual A made the viewer cell *accurate*, which
**exposed** that the flood doesn't reach the player's cell. (This session is more evidence for that.)
- **`memory/feedback_render_one_gate.md`** + **`memory/feedback_verify_render_seal_before_layering.md`**
— get a SCREENSHOT + `[shell]` evidence EARLY; one gate for all geometry.
- **`memory/feedback_render_downstream_of_membership.md`** — a transition flicker can be a membership/
flood-root bug, not a render bug.
---
## 7. Kickoff prompt (copy-paste)
```
Continue acdream M1.5 indoor render in worktree thirsty-goldberg-51bb9b (branch
claude/thirsty-goldberg-51bb9b, HEAD 2b7f5a1). Do NOT branch/worktree; do NOT push without asking;
NEVER git stash/gc. PowerShell on Windows; launch logs are UTF-16. Running the client: see CLAUDE.md;
+Acdream spawns at the Holtburg cottage.
TARGET BUG (the user's real pain, with a screenshot): standing INSIDE the cottage, the floor draws but
the WALLS are grey (the time-of-day clear color) with NPCs/doors/outdoor-slices showing through; in the
CELLAR the FLOOR is missing (grey); flicker when changing rooms / entering the cellar. This is the KNOWN
"R1-completion" SHELL-SEALING / wrong-flood-root problem, NOT the camera boom and NOT the inside/outside
branch (both partially fixed this session).
READ FIRST (in order):
1. docs/research/2026-06-05-shell-sealing-cellar-floor-handoff.md (THIS handoff — the two problems §1,
the decisive root=0171/playerCell=0174 evidence §2, the next diagnostic step §3, the DO-NOT-RETRY §5).
2. docs/research/2026-06-05-render-residual-a-shipped-core-inside-render-handoff.md (the R1-completion
pointer) + docs/superpowers/specs/2026-06-02-render-pipeline-redesign-design.md (§2/§3/§5).
3. memory: reference_render_pipeline_state, feedback_render_one_gate,
feedback_verify_render_seal_before_layering, feedback_render_downstream_of_membership.
DO (evidence first — this bug has burned many speculative fixes; do NOT add a workaround):
- Reproduce + capture with ACDREAM_PROBE_FLAP=1 ACDREAM_PROBE_SHELL=1 ACDREAM_PROBE_VIS=1, player in the
CELLAR with the camera up in the room. Pin: (1) is the player's cell (0174) in the visible set? (2) if
so, does its shell DRAW ([shell] probe) or is a floor polygon missing/back-facing (dat-dump the 0174
EnvCell mesh, design doc §3)? (3) is acdream's viewer_cell wrong — should retail's collided viewer_cell
be the cellar (0174), not the room (0171)? (See §2 a/b/c — settle the design-doc-vs-handoff
viewer-cell-vs-player-cell contradiction against the decomp: RenderNormalMode 0x453aa0 / update_viewer
0x00453ce0 / what sets viewer_cell.)
- THEN fix the actual cause (camera-cell-resolution keeping the eye in the player's cell, OR the flood
reaching the player's cell, OR the mesh seal) — retail-faithful, TDD where the logic is pure,
visual-verify with the user.
DON'T (§5, evidence-disproven): the flicker is NOT viewer-cell oscillation (Part 2 dead-zone is NOT the
fix); the doorway grey is NOT the portal projection (it projects full-screen while OutsideView is empty);
the boom (Part 1) is not the visible bug. Get a SCREENSHOT/[shell] evidence EARLY; don't declare a fix
before the user's eyes confirm it.
TEST BASELINE: App 187p/0f. Core 1331p / 4f (documented) / 1s. Build green.
```
---
## 8. A note from the outgoing model (honest)
I repeatedly presented fixes with too much confidence and conflated Problem A (doorway grey, which I did
reduce) with Problem B (the cellar/walls shell-sealing, which I did not touch). The user called this out
correctly. The two committed changes are real and test-backed, but the **next model should treat the
user's screenshot symptom (walls + cellar floor grey, interior-transition flicker) as Problem B from the
start**, gather `[shell]`/`[flap]` evidence in the cellar BEFORE proposing anything, and **get the user's
eyes on a sealed result before claiming success.**