R1 + R2 + R3 + R3.5 v1 + R3.5 v2 all shipped this session (ed72704→2bfeafd). Primary #78 fix works (cottage walls solid from inside). Three transition / sky issues remain that resist symptom-level patching: A — Exit indoor→outdoor: "objects through ground + building parts missing" B — Inside through window: "sky doesn't render" C — Entry outdoor→indoor: "floor transparent showing cellar + wrong texture" Root cause: architectural mismatch with WB's RenderInsideOut reference. We draw initial terrain unconditionally + depth-clear-if-inside as a workaround; WB skips initial terrain when inside and renders terrain ONLY at the stencil-gated step. The R3.5 v1+v2 patches were symptom fixes that kept producing new edge cases — the exact "patching symptoms" anti-pattern the predecessor revert handoff called out. Handoff doc captures: what shipped, what works, what doesn't (with verbatim user reports), the architectural diagnosis (WB vs our pipeline), the recommended next-session approach (brainstorm → write-plan → execute with the full superpowers workflow), and a self-contained pickup prompt. No code changes in this commit — handoff is doc-only. The 5 implementation commits (ed72704→2bfeafd) remain at HEAD; next session decides whether to revert R3.5 v1+v2 for a cleaner diff vs the R3 baseline, or layer the restructure on top. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
19 KiB
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
Original re-plan: docs/superpowers/plans/2026-05-26-phase-a8-replan.md
Entity-taxonomy fix-shape (approved): docs/research/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.IsBuildingShellcorrectly tags cottage walls at the dat-source boundary (LandblockLoader.Buildingsloop).WbDrawDispatcher.EntitySet.IndoorPasscorrectly 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:
- (No initial terrain. Depth buffer is empty from frame-start
glClear.) - MarkAndPunch — stencil bit 1 + depth = 1.0 at exit-portal silhouettes only.
- Render interior EnvCells with stencil OFF, normal
DepthFunc.Less. Cell mesh wins fresh depth at most pixels. - Enable stencil restriction (
StencilFunc Equal 1, 0x01). - 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.
- (Step 5: WB's 3-stencil-bit pipeline for cross-building visibility — deferred.)
Our R3.5 v2 order:
- Terrain drawn unconditionally (line 7115; color + depth at ~99.99).
- depth-clear-if-cameraReallyInside (depth → 1.0; redundant with MarkAndPunch).
- MarkAndPunch (no-op against the depth-cleared 1.0).
- IndoorPass — cell mesh + statics + building shells.
- EnableOutdoorPass + terrain RE-draw + OutdoorScenery (stencil-gated).
- 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:
-
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.
- WB faithfully: yes, skip when
-
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.RenderSkybetweenEnableOutdoorPassand 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.
-
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).
-
Should we eliminate
cameraInsideCellvscameraReallyInsideasymmetry?- 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
cameraReallyInsideeverywhere? 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
cameraReallyInsideand remove the grace mechanism entirely. Simpler is better.
- Today:
-
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 38d5374or new commits) at the start of the implementation session, work from the R3 baseline. Cleaner diff, easier review.
- v1 (stencil branch gate): subsumed by the restructure since the stencil branch will use
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 of60f07bc(R3 baseline). - Task RR2: Restructure render frame to WB-faithful order. Sub-steps:
- Move
cameraReallyInsidecomputation up next tocameraInsideCell(~line 7011-7014). - Gate the initial terrain draw (line 7115) on
!cameraReallyInside. - Delete the depth-clear-if-inside block entirely.
- Decide on
cameraInsideCellvscameraReallyInsideunification (per Phase 1 brainstorm Q4). - Inside branch: keep existing structure (MarkAndPunch → IndoorPass → EnableOutdoorPass → terrain → OutdoorScenery → DisableStencil → LiveDynamic).
- Move
- 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 2bfeafdfor a cleaner diff against R3 baseline.
Either path is valid — pick whichever the brainstorm settles on.