diff --git a/docs/research/2026-06-05-shell-sealing-cellar-floor-handoff.md b/docs/research/2026-06-05-shell-sealing-cellar-floor-handoff.md new file mode 100644 index 00000000..fd598b56 --- /dev/null +++ b/docs/research/2026-06-05-shell-sealing-cellar-floor-handoff.md @@ -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.**