# Phase A8 — R3.5 transition-flicker iteration PAUSED. Handoff for restructure session. **Date:** 2026-05-26 (PM) **Status:** R1 + R2 + R3 + R3.5 v1 + R3.5 v2 all shipped. Primary #78 indoor fix WORKS. Three distinct transition/sky issues surfaced during R4 visual verification that resist symptom-level patching. **Paused for proper brainstorm → write-plan → execute-plan workflow in a fresh session.** **Branch:** `claude/strange-albattani-3fc83c` (worktree) **HEAD:** `2bfeafd` **Predecessor handoff:** [docs/research/2026-05-26-a8-revert-handoff.md](2026-05-26-a8-revert-handoff.md) **Original re-plan:** [docs/superpowers/plans/2026-05-26-phase-a8-replan.md](../superpowers/plans/2026-05-26-phase-a8-replan.md) **Entity-taxonomy fix-shape (approved):** [docs/research/2026-05-26-a8-entity-taxonomy.md](2026-05-26-a8-entity-taxonomy.md) --- ## TL;DR R1 (IsBuildingShell flag), R2 (EntitySet partition reshape), R3 (render-frame integration of WB-order stencil pipeline) all shipped clean. The primary #78 fix WORKS: standing inside a Holtburg cottage, the walls now block outdoor visibility — no see-through buildings, no see-through scenery. M1.5's "indoor world feels right" is partially achieved. Visual verification (R4) surfaced **three remaining issues** that are NOT individual bugs — they're symptoms of an **architectural mismatch** between our render frame and WB's `RenderInsideOut` reference. Specifically: we draw terrain unconditionally before the stencil work and use depth-clear-if-inside as a workaround, while WB skips initial terrain entirely when inside and renders terrain ONLY at the stencil-gated step. Two patch attempts (R3.5 v1 and R3.5 v2) papered over parts of the symptom but kept producing new edge cases — the exact "patching symptoms" anti-pattern CLAUDE.md and the predecessor revert handoff explicitly call out. **Next session must brainstorm the right architecture, write a plan, and execute.** Do NOT continue inline patches. --- ## What shipped this session (5 commits) | Commit | Task | What it does | |---|---|---| | `ed72704` | R1 | Adds `WorldEntity.IsBuildingShell: bool init` set in `LandblockLoader.Buildings` loop; propagated through `GameWindow.cs:5129-5136` hydration. 2 LandblockLoader tests lock the data-layer guarantee. | | `55f26f2` | R2 (amended) | Reshapes `WbDrawDispatcher.EntitySet` from `IndoorOnly`/`OutdoorOnly` to taxonomy-aware `IndoorPass` / `OutdoorScenery` / `LiveDynamic`. Adds `private static bool EntityMatchesSet(WorldEntity, EntitySet)` truth-table predicate. 7 tests cover the partition. | | `60f07bc` | R3 | Wires the stencil pipeline into `GameWindow` render frame with WB-order: `MarkAndPunch → IndoorPass → EnableOutdoorPass → terrain re-draw → OutdoorScenery → DisableStencil → LiveDynamic`. Stencil-marks **camera's own cell's exit portals only** (WB Step 5 deferred). | | `38d5374` | R3.5 v1 | Adds `cameraReallyInside = PointInCell(camPos, visibility.CameraCell)` gate for the stencil branch (kept `cameraInsideCell` for sky / lighting / depth-clear). Attempt to close the exit-transition flicker. | | `2bfeafd` | R3.5 v2 | Also gates the depth-clear-if-inside on `cameraReallyInside`. Attempt to close the "objects through ground" symptom the v1 fix exposed. | All 5 commits are kept; none are reverted. Build green at HEAD. Test failures within the documented 14-23 pre-existing flaky window. --- ## What's WORKING (the primary fix) Standing inside any Holtburg cottage (ground floor or cellar), looking around: - **Walls are solid.** No outdoor scenery visible through walls. No buildings visible through walls. - **The original #78 symptom is gone.** This is the primary acceptance criterion for the A8 phase. - User confirmed: *"Ok better. ... When I look out now from inside it is not showing buildings below or any windows inside the house."* The architectural win is real: - `WorldEntity.IsBuildingShell` correctly tags cottage walls at the dat-source boundary (`LandblockLoader.Buildings` loop). - `WbDrawDispatcher.EntitySet.IndoorPass` correctly routes cell mesh + cell statics + building shells together — fixing the previous Round-3 regression where cottage walls disappeared. - Camera's-own-cell-portals-only approximation (Step 5 deferred) avoids the "see through wall to another room's outdoor" regression from previous Round 2. --- ## What's NOT WORKING (3 transition/sky issues) Verbatim user reports from R4 visual verification (post R3.5 v2): ### Issue A — Exit indoor→outdoor: "objects through ground + building parts missing" > "If I stand outside or just pass outside I get the flicker where objects are visible through ground and walls of other buildings are missing" **My diagnosis:** during the 3-frame grace window after camera physically exits a cell (`CellVisibility._cellSwitchGraceFrames`), `cameraInsideCell` stays true but `cameraReallyInside` becomes false (PointInCell on the previous cell returns false). With v2: - Sky still skipped (cameraInsideCell) - Initial terrain still drawn (unconditional, line 7115) - depth-clear NOT fired (cameraReallyInside) - Stencil branch NOT taken (cameraReallyInside) - Outdoor branch (`Draw(set: All)`) runs This *should* be correct — terrain depth preserved, all entities depth-tested. But the user still sees the symptom. **Working hypothesis:** with the depth buffer holding terrain Z (~99.99 post the -0.01 nudge from f48c74a), entities at world Z below terrain may still win depth tests in certain camera angles. Or the issue is something else entirely that the v2 didn't address. ### Issue B — Inside looking through window: "Sky don't render" > "Sky dont render when I look from inside to outside" **My diagnosis:** when inside, sky pass is skipped (`if (!cameraInsideCell) { _skyRenderer?.RenderSky(...); ... }` at line 7079). The stencil-gated outdoor pass re-draws terrain + outdoor scenery in portal silhouettes, but **NOT sky**. Through a window, the user sees terrain (where it projects in the portal silhouette) and beyond the terrain horizon — fog color (the framebuffer clear color is set to fog haze at line 6894, not sky color). This is a **known WB-pipeline limitation** — WB itself doesn't draw sky inside-out. To fix in acdream we'd add a stencil-gated `_skyRenderer.RenderSky` call inside the indoor branch between `EnableOutdoorPass` and the terrain re-draw. Not done in any R3.5 patch. ### Issue C — Entry outdoor→indoor: "floor transparent showing cellar + wrong texture" > "When going from outside to inside flickering so that parts of the floor is transparent so I see the cellar from above and wrong texture on the floor" **My diagnosis (LOWER CONFIDENCE):** the cottage floor and cellar ceiling are at adjacent world Z values. Both meshes are loaded (cottage cell + cellar cell both in `VisibleCellIds` when standing in the cottage). During the entry transition frame, depth-fight may occur between cottage floor (Z=100.02 with the +0.02 cell origin bump) and cellar ceiling (whatever Z that mesh sits at). "Wrong texture" suggests the cellar ceiling is winning depth at floor pixels and its texture is showing through. This is **likely a pre-existing data-model / multi-cell-Z artifact, not strictly an A8 bug**, but it became visible because the new pipeline doesn't have the depth-clear-if-inside masking it on every frame anymore. --- ## Architectural diagnosis — the root cause Reading `references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/VisibilityManager.cs:73-239` carefully: **WB's RenderInsideOut order:** 1. (No initial terrain. Depth buffer is empty from frame-start `glClear`.) 2. MarkAndPunch — stencil bit 1 + depth = 1.0 at exit-portal silhouettes only. 3. Render interior EnvCells with stencil OFF, normal `DepthFunc.Less`. Cell mesh wins fresh depth at most pixels. 4. Enable stencil restriction (`StencilFunc Equal 1, 0x01`). 5. **Render terrain + scenery + static objects** — at portal silhouettes ONLY (stencil-restricted). Terrain depth (close, ~99.99) wins against the 1.0 punch in portal areas → outdoor visible through windows. 6. (Step 5: WB's 3-stencil-bit pipeline for cross-building visibility — deferred.) **Our R3.5 v2 order:** 1. **Terrain drawn unconditionally** (line 7115; color + depth at ~99.99). 2. depth-clear-if-cameraReallyInside (depth → 1.0; redundant with MarkAndPunch). 3. MarkAndPunch (no-op against the depth-cleared 1.0). 4. IndoorPass — cell mesh + statics + building shells. 5. EnableOutdoorPass + terrain RE-draw + OutdoorScenery (stencil-gated). 6. DisableStencil + LiveDynamic. **The mismatch:** we draw terrain TWICE (initial + re-draw) and have a depth-clear that's a workaround for the initial terrain draw. WB avoids both by skipping the initial terrain entirely when inside. Our pipeline is a "FRANKENSTEIN" — it works in the steady-state indoor case (the primary #78 fix) but breaks at transitions and during grace frames because the interactions between (initial terrain + depth-clear + grace + cameraInsideCell vs cameraReallyInside flag asymmetry) keep producing new edge cases. **The R3.5 v1 and v2 patches were symptom-fixes**, not root-cause fixes. CLAUDE.md is explicit about this: *"When you spot a bug or encounter a behavioral mismatch, fix the underlying cause — do not ship a band-aid, suppression flag, grace period, retry loop, or any other 'make the symptom go away' shortcut, unless the user has explicitly approved that shape."* The user has now correctly pulled the emergency brake. --- ## Recommended next-session approach Use the **superpowers full workflow**: ### Phase 1: BRAINSTORM (use `superpowers:brainstorming`) Settle the design BEFORE writing a plan. Key brainstorm questions: 1. **Should the initial terrain draw be conditional?** - WB faithfully: yes, skip when `cameraReallyInside`. Terrain draws only at stencil-gated step. - Hybrid: keep initial terrain unconditional but remove the depth-clear so terrain depth wins against indoor cells at non-portal pixels. *(Would break the #78 fix — cottage floor at +0.02 would lose to terrain at -0.01.)* - **Probably WB-faithful is the right call.** 2. **Should sky be re-drawn stencil-gated when inside?** - WB: no. Sky color shows as fog-clear-color through windows. - acdream enhancement: yes, render `_skyRenderer.RenderSky` between `EnableOutdoorPass` and the terrain re-draw inside the indoor branch. - **Tradeoff:** WB-faithfulness vs. user's expectation that windows show sky. Retail probably shows sky through windows; investigate retail's polygon-clip scissor approach. 3. **What's the deal with the entry-flicker "floor transparent showing cellar"?** - Is it depth-fight between cottage floor mesh (Z=100.02) and cellar ceiling mesh (Z=?)? Need a brief investigation to confirm. - Is it a one-frame visibility-update lag where cottage cell isn't yet in VisibleCellIds during the entry transition frame? - Is it pre-existing in main (test by reverting all of A8 and entering a cottage on main)? - **Don't try to fix this in A8.** Identify, file as separate follow-up (likely candidate for #103 family or new #106). 4. **Should we eliminate `cameraInsideCell` vs `cameraReallyInside` asymmetry?** - Today: `cameraInsideCell` (grace-aware) gates sky/lighting; `cameraReallyInside` (PointInCell, no grace) gates depth-clear + stencil branch. - The split is a workaround for the grace-mechanism conflict with the render path. With WB-faithful order (no initial terrain, no depth-clear), can we use `cameraReallyInside` everywhere? Or does that introduce sky flicker at the threshold? - The grace mechanism was added to prevent cell-id flicker at doorways. Does PointInCell with its existing epsilon already provide enough hysteresis? - **Likely path: unify on `cameraReallyInside` and remove the grace mechanism entirely.** Simpler is better. 5. **Are R3.5 v1 + v2 patches worth keeping or should we revert them before the restructure?** - v1 (stencil branch gate): subsumed by the restructure since the stencil branch will use `cameraReallyInside`. - v2 (depth-clear gate): subsumed since depth-clear gets DELETED entirely. - **Recommendation:** revert v1 and v2 (`git revert 2bfeafd 38d5374` or new commits) at the start of the implementation session, work from the R3 baseline. Cleaner diff, easier review. ### Phase 2: WRITE-PLAN (use `superpowers:writing-plans`) Expected plan shape (TDD where possible): - **Task RR1**: Revert R3.5 v1 + v2 (`git revert 38d5374 2bfeafd`). Result: HEAD at logical state of `60f07bc` (R3 baseline). - **Task RR2**: Restructure render frame to WB-faithful order. Sub-steps: - Move `cameraReallyInside` computation up next to `cameraInsideCell` (~line 7011-7014). - Gate the initial terrain draw (line 7115) on `!cameraReallyInside`. - Delete the depth-clear-if-inside block entirely. - Decide on `cameraInsideCell` vs `cameraReallyInside` unification (per Phase 1 brainstorm Q4). - Inside branch: keep existing structure (MarkAndPunch → IndoorPass → EnableOutdoorPass → terrain → OutdoorScenery → DisableStencil → LiveDynamic). - **Task RR3 (optional)**: Add stencil-gated sky pass for sky-through-windows (per Phase 1 brainstorm Q2). Or defer as #105. - **Task RR4**: Visual verification matrix (same as R4: cottage interior, cellar, inn, dungeon; PLUS exit transition, entry transition, sky-through-windows). - **Task RR5**: Ship docs (R5 from original plan; file the genuine follow-ups; close #78). GL integration tasks are visual-verification-only by nature (the partition logic + EntitySet are already unit-tested). Don't burn cycles writing unit tests for GL state — the existing infrastructure tests (26 dispatcher + 5 stencil + 2 PortalPolygons + 1 ProbeVisibility = 34) already lock the non-GL bits. ### Phase 3: EXECUTE-PLAN (use `superpowers:subagent-driven-development`) Same pattern as this session: fresh Sonnet subagent per task, two-stage review (spec compliance + code quality). The CRITICAL extra review check beyond default — **add to the spec reviewer prompt**: *"Does the implementation match WB's RenderInsideOut order at `references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/VisibilityManager.cs:73-239`? Specifically: NO initial terrain draw when inside, NO depth-clear, terrain rendered ONLY stencil-gated?"* --- ## Pickup prompt for next session ``` Phase A8 — render frame restructure to match WB's RenderInsideOut order faithfully. R1+R2+R3+R3.5 v1+v2 shipped this session (commits ed72704 → 2bfeafd). Primary #78 fix works (cottage interior solid walls). Three transition/sky issues remain that resist symptom patching. Read first (in this order — REQUIRED): 1. docs/research/2026-05-26-a8-r3.5-restructure-handoff.md (this doc — full story of why we paused; the architectural mismatch; recommended path) 2. docs/research/2026-05-26-a8-entity-taxonomy.md (approved fix-shape) 3. docs/research/2026-05-26-a8-revert-handoff.md (predecessor; the original A8 attempt's revert lessons — still applies) 4. docs/superpowers/plans/2026-05-26-phase-a8-replan.md (this session's plan — R1+R2+R3 still apply; R3.5 patches and the WB-faithful restructure are NEW work) 5. references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/VisibilityManager.cs:73-239 (the proven reference — read it verbatim BEFORE designing the restructure) 6. CLAUDE.md — find "currently working toward" to refresh state State both altitudes: Currently working toward: M1.5 — Indoor world feels right Current phase: A8 — render frame restructure to WB-faithful order HEAD: 2bfeafd (R3.5 v2) Clean revert points: 60f07bc (R3 baseline) or 55f26f2 (R2) Test baseline: build green; 1238 pass / 14 fail (documented flaky window) Session flow — MUST use full superpowers workflow: ### Phase 1 — BRAINSTORM (use superpowers:brainstorming) Settle the design. Do NOT skip this. The previous session jumped to patching after R3 and that produced this handoff. Five questions in the recommended-next-session-approach section of this handoff doc must be answered before any code is written. Brainstorm output: a short design note in chat + an updated entry in the entity-taxonomy doc OR a fresh design doc. Get user approval before Phase 2. ### Phase 2 — WRITE-PLAN (use superpowers:writing-plans) Expected: tasks RR1 (revert R3.5), RR2 (restructure render frame), RR3 (optional sky-through-windows), RR4 (visual verification), RR5 (ship docs). Plan path: docs/superpowers/plans/2026-05-2X-phase-a8-restructure.md (date when written). ### Phase 3 — EXECUTE (use superpowers:subagent-driven-development) Fresh Sonnet subagent per task with two-stage review. Add the WB-order check to the spec reviewer prompt (see handoff doc). ## Constraints - Per CLAUDE.md "no workarounds without approval" — fix the root cause. The R3.5 v1+v2 patches were symptom fixes. Do not repeat that pattern. - Visual verification is the acceptance test. Test scenarios in the handoff's "What's NOT working" section MUST all be re-tested. - Existing infrastructure (Tasks 1-6 + R1 + R2 + R3) is correct and shipped. The restructure is a render-frame surgery, not a partition reshape or data-layer change. ## What success looks like After this restructure ships: - Standing INSIDE cottage / cellar / inn / dungeon: solid walls (unchanged from this session's R3 win). - EXITING indoor → outdoor: clean transition. No "objects through ground." No "buildings missing." Brief lighting transition is OK if sky-on-cameraInsideCell is kept, otherwise no lighting transition. - ENTERING outdoor → indoor: clean transition. No floor-transparent showing cellar. If the floor-cellar-z-fight is pre-existing on main, file as a separate issue and accept it as not-A8-scope. - LOOKING THROUGH WINDOWS from inside: terrain visible at the portal silhouette. Sky visible (if RR3 included) OR fog color (if RR3 deferred and noted in #105). - dotnet build green; test failures within the documented 14-23 flaky window. ``` --- ## Files state at session end ``` Branch: claude/strange-albattani-3fc83c HEAD: 2bfeafd fix(render): Phase A8 R3.5 v2 — gate depth-clear on cameraReallyInside too Parent: 38d5374 fix(render): Phase A8 R3.5 — gate stencil branch on PointInCell containment GP: 60f07bc feat(render): Phase A8 R3 — wire stencil pipeline into render frame (WB order) GGP: 55f26f2 feat(render): Phase A8 R2 — WbDrawDispatcher.EntitySet taxonomy partition GGGP: ed72704 feat(world): Phase A8 R1 — tag WorldEntity.IsBuildingShell at LandblockLoader Working tree: clean Build: green (0 warnings, 0 errors) Tests: 1238 pass / 14 fail (all within documented 14-23 flaky window; zero new failures attributable to A8 R1/R2/R3/R3.5) Untracked log files: launch-a8-verify*.log (deletable) ``` The five commits are all NEW additions to main; no destructive history rewrites. Next session can: - Continue from HEAD with the restructure layered on top (R3.5 patches subsumed by it). - OR `git revert 38d5374 2bfeafd` for a cleaner diff against R3 baseline. Either path is valid — pick whichever the brainstorm settles on.