acdream/docs/research/2026-06-05-shell-sealing-cellar-floor-handoff.md
Erik 8116d101bc 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>
2026-06-05 17:04:52 +02:00

239 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.**