acdream/docs/superpowers/plans/2026-05-26-phase-a8-restructure.md
Erik 29e306b0f6 docs: Phase A8 — mark prior restructure design+plan as SUPERSEDED
Both documents retained for historical reference. The new full-WB-port
design + plan (2026-05-26-phase-a8-wb-full-port-design.md + plan, ea60d1f +
651e7e2) replace them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 10:08:48 +02:00

1148 lines
53 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.

# Phase A8 — Render-Frame Restructure to WB-Faithful Order Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Replace the R3.5 v2 "frankenstein" render frame (initial terrain + depth-clear-if-inside + stencil pipeline, three workarounds stacked) with WorldBuilder's `RenderInsideOut` order verbatim. Close R4 Issue B (sky doesn't render through windows). Match the proven reference. Pre-restructure falsification spike (RR0) determines whether R4 Issues A + C are pre-existing on `main` (out of A8 scope) or A8-caused (re-brainstorm before continuing).
**Architecture:** Skip initial sky + terrain at the start of the render frame when the camera is inside a cell; delete the depth-clear-if-inside block entirely; inside the indoor branch insert a stencil-gated `_skyRenderer.RenderSky` step (DepthMask off) between `EnableOutdoorPass` and the terrain re-draw so windows show real sky; unify the two-flag asymmetry (`cameraInsideCell` lenient + `cameraReallyInside` strict) into a single strict `cameraInside` flag computed via `PointInCell`. Grace mechanism in `CellVisibility` stays alive for non-render consumers.
**Tech Stack:** C# .NET 10, Silk.NET (OpenGL 4.3 + GL_ARB_bindless_texture + GL_ARB_shader_draw_parameters), xUnit.
**Predecessor context (REQUIRED reading before starting):**
- [docs/superpowers/specs/2026-05-26-phase-a8-restructure-design.md](../specs/2026-05-26-phase-a8-restructure-design.md) — the approved design this plan implements
- [docs/research/2026-05-26-a8-r3.5-restructure-handoff.md](../../research/2026-05-26-a8-r3.5-restructure-handoff.md) — full handoff covering the R3.5 v1+v2 saga and architectural mismatch
- [references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/VisibilityManager.cs:73-239](../../../references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/VisibilityManager.cs) — the proven WB reference; the restructure must match this verbatim through Step 4. Step 5 (3-stencil-bit cross-building) stays deferred.
**Infrastructure preserved (consume as-is — these are already shipped + tested):**
- `src/AcDream.App/Rendering/IndoorCellStencilPipeline.cs``UploadPortalMesh` / `MarkAndPunch` / `EnableOutdoorPass` / `DisableStencil`
- `src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs``EntitySet.{IndoorPass, OutdoorScenery, LiveDynamic, All}` partition (R2)
- `src/AcDream.Core/World/WorldEntity.cs``IsBuildingShell` flag (R1)
- `src/AcDream.App/Rendering/CellVisibility.cs``PointInCell` static, `ComputeVisibility`, `FindCameraCell` with grace mechanism
**HEAD at start of session:** `2bfeafd` (R3.5 v2)
**Clean revert points:** `60f07bc` (R3 baseline) or further back to `55f26f2` (R2)
---
## File Structure
| File | What changes | Why |
|---|---|---|
| `docs/research/2026-05-26-a8-rr0-falsification-findings.md` | NEW doc capturing RR0 outcome on three branches | Decision evidence for whether A+C are A8-caused or pre-existing |
| `src/AcDream.App/Rendering/GameWindow.cs` | Render-frame block, lines ~70117260: gate-flag rename + skip-sky+terrain when inside + delete-depth-clear + add stencil-gated sky inside indoor branch | The restructure itself |
| `src/AcDream.App/Rendering/SkyRenderer.cs` | READ ONLY in RR3; possibly a small wrapper invocation in `GameWindow.cs` if dirty | Pre-flight verify stencil state contract |
| `docs/ISSUES.md` | Move #78 to "Recently closed"; file new follow-ups (cross-cell-portal, cellar Z-fight from outside, and A/C if RR0 confirms pre-existing on main) | Ship docs |
| `CLAUDE.md` | Update A8 paragraph: `PAUSED``SHIPPED 2026-05-2X` with the restructure commit list | Roadmap discipline |
**Decomposition rationale:** the restructure is localized to one render-frame block in one file. The other files are docs. Six tasks because of the mandatory pre-spike (RR0) and the SkyRenderer verification (RR3); the core restructure is one commit (RR2).
---
## Task RR0: Falsification spike — are Issues A + C pre-existing or A8-caused?
**Goal:** Determine whether R4 Issues A ("objects through ground + walls missing on exit") and C ("transparent floor showing cellar on entry") reproduce on `main` (no A8 work) and on `60f07bc` (R3 baseline, before R3.5 patches). This is the decision gate for whether the restructure as designed is sufficient or whether scope must expand.
**Files:**
- Create: `docs/research/2026-05-26-a8-rr0-falsification-findings.md`
**Method:** three side-by-side launches. Human visual observation per scenario. Findings doc records what was seen on each branch and the decision outcome.
- [ ] **RR0-S1: Launch HEAD (2bfeafd) and observe Issues A + C**
Build + launch in the foreground (the human watches the client window):
```powershell
$proc = Get-Process -Name AcDream.App -ErrorAction SilentlyContinue
if ($proc) {
$proc.CloseMainWindow() | Out-Null
if (-not $proc.WaitForExit(5000)) { $proc | Stop-Process -Force }
}
Start-Sleep -Seconds 3
$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"
dotnet build src\AcDream.App\AcDream.App.csproj -c Debug --nologo
dotnet run --project src\AcDream.App\AcDream.App.csproj --no-build -c Debug 2>&1 |
Tee-Object -FilePath "launch-a8-rr0-head.log"
```
Human walks `+Acdream` through these scenarios:
1. Stand outside Holtburg cottage. Walk into cottage. Watch the entry transition for ~3 seconds. Observe: does the cottage floor go transparent showing cellar below, with "wrong texture" flicker?
2. Stand inside cottage. Walk back outside. Watch the exit transition for ~3 seconds. Observe: do objects appear visible through the ground? Do walls of other buildings (adjacent cottages) appear missing?
Record per-scenario:
- Issue C (entry transparent floor): YES / NO / INTERMITTENT
- Issue A (exit through-ground): YES / NO / INTERMITTENT
Graceful close (use `$proc.CloseMainWindow()` pattern).
- [ ] **RR0-S2: Test against R3 baseline by checking out 60f07bc's GameWindow.cs only**
`60f07bc` is the R3 baseline (before R3.5 v1+v2 patches). It includes R1+R2 infrastructure (which we want), but the render-frame block is pre-patch. Single-file checkout keeps all other R1+R2 code intact:
```bash
git checkout 60f07bc -- src/AcDream.App/Rendering/GameWindow.cs
dotnet build src\AcDream.App\AcDream.App.csproj -c Debug --nologo
```
Expected: build green (R1+R2 deps unchanged between 60f07bc and HEAD).
Launch + repro the same two scenarios from S1, logging to `launch-a8-rr0-r3.log`:
```powershell
dotnet run --project src\AcDream.App\AcDream.App.csproj --no-build -c Debug 2>&1 |
Tee-Object -FilePath "launch-a8-rr0-r3.log"
```
Record per-scenario: Issue C, Issue A outcomes.
Graceful close. Restore HEAD's `GameWindow.cs`:
```bash
git checkout HEAD -- src/AcDream.App/Rendering/GameWindow.cs
dotnet build src\AcDream.App\AcDream.App.csproj -c Debug --nologo
```
Expected: build green; working tree clean.
- [ ] **RR0-S3: Test against main using a side worktree**
Main has no A8 work at all. Need a separate worktree to test it without disturbing the current worktree:
```bash
git fetch origin main
git worktree add ../tmp-main-baseline main
```
Expected: new worktree created at `../tmp-main-baseline` checked out to `main`.
Build + launch from the side worktree:
```powershell
Push-Location ..\tmp-main-baseline
$proc = Get-Process -Name AcDream.App -ErrorAction SilentlyContinue
if ($proc) {
$proc.CloseMainWindow() | Out-Null
if (-not $proc.WaitForExit(5000)) { $proc | Stop-Process -Force }
}
Start-Sleep -Seconds 3
dotnet build src\AcDream.App\AcDream.App.csproj -c Debug --nologo
dotnet run --project src\AcDream.App\AcDream.App.csproj --no-build -c Debug 2>&1 |
Tee-Object -FilePath "launch-a8-rr0-main.log"
Pop-Location
```
Record per-scenario: Issue C, Issue A outcomes.
Graceful close. Remove the side worktree:
```bash
git worktree remove ../tmp-main-baseline
```
- [ ] **RR0-S4: Write findings doc and commit**
Create `docs/research/2026-05-26-a8-rr0-falsification-findings.md` with this template (fill in the actual observations from S1S3):
```markdown
# A8 RR0 falsification — are Issues A and C pre-existing or A8-caused?
**Date:** 2026-05-26
**Method:** three-branch launch + visual repro at Holtburg cottage entry / exit transitions.
## Observations
| Branch | Commit | Issue A (exit through-ground) | Issue C (entry transparent floor) |
|---|---|---|---|
| HEAD | `2bfeafd` (R3.5 v2) | [YES / NO / INTERMITTENT] | [YES / NO / INTERMITTENT] |
| R3 baseline | `60f07bc` | [YES / NO / INTERMITTENT] | [YES / NO / INTERMITTENT] |
| main | [current main SHA] | [YES / NO / INTERMITTENT] | [YES / NO / INTERMITTENT] |
## Decision
Based on the table:
- [ ] **Outcome 1:** All three reproduce → A + C are pre-existing on main. Restructure proceeds RR1→RR5 unchanged. RR5 files A + C as new ISSUES.md entries.
- [ ] **Outcome 2:** Only R3 + HEAD reproduce → A and/or C caused by R3 work specifically. **PAUSE** the plan. Re-brainstorm via `superpowers:brainstorming` to address them; update the design doc; resume.
- [ ] **Outcome 3:** Only HEAD reproduces → R3.5 v1/v2 patches caused them. Revert in RR1 clears them. Restructure proceeds; RR4 confirms cleanup.
Selected: [outcome #]
## Logs
- `launch-a8-rr0-head.log`
- `launch-a8-rr0-r3.log`
- `launch-a8-rr0-main.log`
```
Commit the findings:
```bash
git add docs/research/2026-05-26-a8-rr0-falsification-findings.md
git commit -m "$(cat <<'EOF'
docs(research): Phase A8 RR0 — falsification findings (A + C pre-existing?)
Side-by-side launch on HEAD (2bfeafd) / R3 baseline (60f07bc) / main.
Visual repro at Holtburg cottage entry + exit transitions. Records
whether Issues A and C are A8-caused or pre-existing.
Decision gate for whether the restructure as designed is sufficient
or whether RR2 scope must expand.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```
- [ ] **RR0-S5: Decision gate — proceed or pause**
If selected outcome is 1 or 3 → proceed to RR1.
If selected outcome is 2 → **STOP**. Re-invoke `superpowers:brainstorming` to expand the design. Do NOT proceed to RR1.
---
## Task RR1: Revert R3.5 v1 + v2 as two new commits
**Goal:** Clean diff against R3 baseline for the restructure that follows. The v1+v2 patches are subsumed by RR2 (the stencil branch will use `cameraInside`; depth-clear gets deleted entirely).
**Files:**
- Modify (via `git revert`): `src/AcDream.App/Rendering/GameWindow.cs`
- [ ] **RR1-S1: Revert R3.5 v2 (depth-clear gate)**
```bash
git revert 2bfeafd --no-edit
```
Expected: one new commit lands. `git log -1` shows `Revert "fix(render): Phase A8 R3.5 v2 — gate depth-clear on cameraReallyInside too"`.
- [ ] **RR1-S2: Revert R3.5 v1 (stencil branch gate)**
```bash
git revert 38d5374 --no-edit
```
Expected: one new commit lands. `git log -1` shows `Revert "fix(render): Phase A8 R3.5 — gate stencil branch on PointInCell containment"`.
- [ ] **RR1-S3: Verify the working tree matches the R3 baseline render frame**
```bash
git diff 60f07bc HEAD -- src/AcDream.App/Rendering/GameWindow.cs
```
Expected: empty diff. The render-frame block at HEAD now matches `60f07bc`'s GameWindow.cs verbatim.
If the diff is NOT empty, investigate — likely a non-A8 commit landed between 60f07bc and 2bfeafd that touched GameWindow.cs unrelated to R3.5 (uncommon but possible).
- [ ] **RR1-S4: Build + test green**
```bash
dotnet build -c Debug --nologo
dotnet test --nologo
```
Expected: build green; test failures within the documented 1423 flaky window (PhysicsResolveCapture/Diagnostics static-leak issues, pre-existing).
---
## Task RR2: Restructure render frame to WB-faithful order
**Goal:** Re-wire `GameWindow.cs`'s render-frame block to match `VisibilityManager.RenderInsideOut` Steps 14 verbatim, unify the two-flag asymmetry, and add the stencil-gated sky step.
**Files:**
- Modify: `src/AcDream.App/Rendering/GameWindow.cs` — render-frame block, lines ~70117260
The exact line numbers below are approximations from the worktree state at the time this plan was written (HEAD = `2bfeafd`). After RR1 reverts the line numbers shift; locate each edit by the exact `old_string` content, not by line number.
- [ ] **RR2-S1: Rename `cameraInsideCell` → `cameraInside` with strict semantics**
In `src/AcDream.App/Rendering/GameWindow.cs`, find this line (currently ~7012):
```csharp
bool cameraInsideCell = visibility?.CameraCell is not null;
```
Replace with:
```csharp
// Phase A8 restructure (2026-05-26): SINGLE source of truth for
// "rendering as if camera is inside a cell." Strict PointInCell
// check, no grace. Drives sky, terrain, stencil branch, weather,
// sky-PES debug. `playerInsideCell` (line below) STAYS unchanged
// — it tracks player position, not camera, for lighting.
//
// The grace mechanism in CellVisibility.FindCameraCell stays
// alive; non-render consumers (IsInsideAnyCell, etc.) may still
// benefit. Only the render-frame consumers use this strict flag.
//
// See: docs/superpowers/specs/2026-05-26-phase-a8-restructure-design.md
bool cameraInside = visibility?.CameraCell is not null
&& CellVisibility.PointInCell(camPos, visibility.CameraCell);
```
Note: the `—` em-dash in the comment above is just for the doc renderer; in the actual file write em-dashes directly (or hyphens; not load-bearing).
- [ ] **RR2-S2: Update sky-PES debug call to use cameraInside**
Find this line (currently ~7032):
```csharp
if (_options.EnableSkyPesDebug)
UpdateSkyPes((float)WorldTime.DayFraction, _activeDayGroup, camPos, cameraInsideCell);
```
Replace with:
```csharp
if (_options.EnableSkyPesDebug)
UpdateSkyPes((float)WorldTime.DayFraction, _activeDayGroup, camPos, cameraInside);
```
- [ ] **RR2-S3: Update sky pre-scene gate to use cameraInside**
Find this line (currently ~7090):
```csharp
if (!cameraInsideCell)
{
_skyRenderer?.RenderSky(camera, camPos, (float)WorldTime.DayFraction,
_activeDayGroup, kf, environOverrideActive);
if (_particleSystem is not null && _particleRenderer is not null)
_particleRenderer.Draw(_particleSystem, camera, camPos,
AcDream.Core.Vfx.ParticleRenderPass.SkyPreScene);
}
```
Replace with:
```csharp
if (!cameraInside)
{
_skyRenderer?.RenderSky(camera, camPos, (float)WorldTime.DayFraction,
_activeDayGroup, kf, environOverrideActive);
if (_particleSystem is not null && _particleRenderer is not null)
_particleRenderer.Draw(_particleSystem, camera, camPos,
AcDream.Core.Vfx.ParticleRenderPass.SkyPreScene);
}
```
- [ ] **RR2-S4: Gate initial terrain draw on `!cameraInside`**
Find this block (currently ~71117123):
```csharp
// Phase N.5b: wrap Draw in CPU stopwatch for [TERRAIN-DIAG] rollup
// (gated on ACDREAM_WB_DIAG=1, same env var as [WB-DIAG]). Stopwatch
// is cheap; only the periodic Console.WriteLine is gated.
_terrainCpuStopwatch.Restart();
_terrain?.Draw(camera, frustum, neverCullLandblockId: playerLb);
_terrainCpuStopwatch.Stop();
```
Replace with:
```csharp
// Phase N.5b: wrap Draw in CPU stopwatch for [TERRAIN-DIAG] rollup
// (gated on ACDREAM_WB_DIAG=1, same env var as [WB-DIAG]). Stopwatch
// is cheap; only the periodic Console.WriteLine is gated.
//
// Phase A8 restructure: skip initial terrain when cameraInside.
// WB renders terrain ONLY at the stencil-gated step inside the
// indoor branch (see line ~7215). Drawing terrain unconditionally
// here would require a depth-clear-if-inside workaround (deleted
// by this restructure).
_terrainCpuStopwatch.Restart();
if (!cameraInside)
_terrain?.Draw(camera, frustum, neverCullLandblockId: playerLb);
_terrainCpuStopwatch.Stop();
```
The diag stopwatch + sample buffer + MaybeFlushTerrainDiag below stay unchanged — they record sub-microsecond when skipped, which is correct.
- [ ] **RR2-S5: Delete the R3.5 long comment block + cameraReallyInside declaration + depth-clear-if-inside**
Find this entire block (currently ~71257155):
```csharp
// Phase A8 R3.5 — transition-flicker fix. `cameraInsideCell`
// stays true for ~3 grace frames after the camera physically
// exits a cell (see CellVisibility._cellSwitchGraceFrames).
// The grace mechanism prevents sky/lighting flicker at the
// doorway threshold, but the render-frame mechanisms that
// touch depth (depth-clear AND the stencil pipeline) MUST be
// gated on the stricter PointInCell containment so they don't
// fire during grace frames when the camera is actually outside.
//
// cameraInsideCell — lenient, grace-aware → sky, lighting
// cameraReallyInside — PointInCell, no grace → depth-clear,
// stencil pipeline branch
//
// R3.5 v1 only gated the stencil branch on `cameraReallyInside`;
// depth-clear stayed on `cameraInsideCell`. Result: during grace
// frames the depth-clear ran but the outdoor branch ran (because
// !cameraReallyInside), so terrain depth was destroyed AND
// everything below terrain (cellars, basement geometry) won the
// depth test in the outdoor pass → "objects visible through
// ground." R3.5 v2 unifies the depth-related gates on
// `cameraReallyInside` so terrain depth is preserved during
// grace, eliminating the through-ground artifact.
bool cameraReallyInside = visibility?.CameraCell is not null
&& CellVisibility.PointInCell(camPos, visibility.CameraCell);
// Conditional depth clear: when camera is ACTUALLY inside a cell
// volume (not just in the grace window), clear depth (not color)
// so interior geometry writes fresh Z values on top of the
// terrain color buffer. Matches ACME GameScene.cs pattern.
if (cameraReallyInside)
_gl!.Clear(ClearBufferMask.DepthBufferBit);
```
Delete the entire block. No replacement — the depth-clear was a workaround for the now-skipped initial terrain. RR2-S4 (terrain gated on `!cameraInside`) replaces both functions.
Note: after RR1 reverts have run, the `cameraReallyInside` declaration is GONE (it was added by R3.5 v1). The remaining lines you need to delete in this step are smaller — just the depth-clear-if-inside block. The full block above is what HEAD looks like BEFORE RR1 reverts; after RR1 the matching `old_string` shrinks to roughly:
```csharp
// Conditional depth clear: when camera is ACTUALLY inside a cell
// volume (not just in the grace window), clear depth (not color)
// so interior geometry writes fresh Z values on top of the
// terrain color buffer. Matches ACME GameScene.cs pattern.
if (cameraInsideCell)
_gl!.Clear(ClearBufferMask.DepthBufferBit);
```
(Note: post-RR1, the variable name is `cameraInsideCell` again — but RR2-S1 already renamed it to `cameraInside`. So at this step's execution time, the gate is `cameraInside`. Locate by the `_gl!.Clear(ClearBufferMask.DepthBufferBit)` line and the comment above it; delete the whole 4-line block.)
- [ ] **RR2-S6: Modify the indoor branch — switch condition to cameraInside, prepare for stencil-gated sky insertion**
Find the indoor branch condition (currently ~7174):
```csharp
// The `visibility?.CameraCell is not null` repeat is for the
// compiler's null-flow analysis: `cameraReallyInside` already
// implies it, but flow doesn't propagate through a separate
// local. Restating it inside the if condition lets the branch
// body use the un-`?`d form without null-forgiving.
if (cameraReallyInside && _indoorStencilPipeline is not null
&& visibility?.CameraCell is not null)
{
```
Replace with:
```csharp
// The `visibility?.CameraCell is not null` repeat is for the
// compiler's null-flow analysis: `cameraInside` already implies
// it, but flow doesn't propagate through a separate local.
// Restating it inside the if condition lets the branch body use
// the un-`?`d form without null-forgiving.
if (cameraInside && _indoorStencilPipeline is not null
&& visibility?.CameraCell is not null)
{
```
(Post-RR1, the variable is `cameraInsideCell` and is then renamed by RR2-S1 to `cameraInside`. Locate this step's edit by the `_indoorStencilPipeline is not null` text — that's stable across the renames.)
- [ ] **RR2-S7: Insert stencil-gated sky step between EnableOutdoorPass and the terrain re-draw**
Find this block (currently ~72097215):
```csharp
// 5. Stencil-gated outdoor pass.
_indoorStencilPipeline.EnableOutdoorPass();
// 5a. Re-draw terrain — at portal-silhouette pixels only,
// terrain Z (with the f48c74a -0.01 nudge) wins over the
// punched 1.0 depth. Color writes through window.
_terrain?.Draw(camera, frustum, neverCullLandblockId: playerLb);
```
Replace with:
```csharp
// 5. Stencil-gated outdoor pass.
_indoorStencilPipeline.EnableOutdoorPass();
// 5a. Stencil-gated SKY. DepthMask OFF so sky color writes
// through the punched depth=1.0 but depth STAYS at the
// punch value, letting the next step's terrain re-draw
// win the depth test wherever closer terrain exists.
// EnableOutdoorPass left stencil at Equal(1, 0x01) so
// sky color only writes where bit 1 is set (portal
// silhouettes). This is acdream's enhancement over WB
// (which shows fog clear color through windows); see
// design doc §Q2 + brainstorm outcomes.
//
// PRE-FLIGHT (RR3): SkyRenderer.RenderSky must NOT
// toggle stencil state internally. Verified in commit
// <RR3 SHA — fill in after RR3 ships>.
_gl!.DepthMask(false);
_skyRenderer?.RenderSky(camera, camPos, (float)WorldTime.DayFraction,
_activeDayGroup, kf, environOverrideActive);
_gl!.DepthMask(true);
// 5b. Re-draw terrain — at portal-silhouette pixels only,
// terrain Z (with the f48c74a -0.01 nudge) wins over the
// punched 1.0 depth. Color writes through window,
// overwriting the sky's color writes where terrain is
// closer (near-field landscape through the window).
_terrain?.Draw(camera, frustum, neverCullLandblockId: playerLb);
```
The step numbering in the comments shifts: what was "5a" terrain re-draw is now "5b" with sky as "5a." Outdoor scenery below stays as "5c" (was "5b"). Update consistently below:
Find the OutdoorScenery comment immediately following:
```csharp
// 5b. Outdoor scenery — same stencil gating.
_wbDrawDispatcher!.Draw(camera, _worldState.LandblockEntries, frustum,
neverCullLandblockId: playerLb,
visibleCellIds: visibility.VisibleCellIds,
animatedEntityIds: animatedIds,
set: AcDream.App.Rendering.Wb.WbDrawDispatcher.EntitySet.OutdoorScenery);
```
Replace the comment marker:
```csharp
// 5c. Outdoor scenery — same stencil gating.
_wbDrawDispatcher!.Draw(camera, _worldState.LandblockEntries, frustum,
neverCullLandblockId: playerLb,
visibleCellIds: visibility.VisibleCellIds,
animatedEntityIds: animatedIds,
set: AcDream.App.Rendering.Wb.WbDrawDispatcher.EntitySet.OutdoorScenery);
```
- [ ] **RR2-S8: Update animated-id-set comment to reference cameraInside**
Find this comment block (currently ~71577160):
```csharp
// L-fix1 (2026-04-28): animated-entity id set. Required by both
// the cameraReallyInside branch (to route them to LiveDynamic
// pass) and the outdoor path (where it preserves visibility
// across landblock frustum culling).
```
Replace with:
```csharp
// L-fix1 (2026-04-28): animated-entity id set. Required by both
// the cameraInside branch (to route them to LiveDynamic pass)
// and the outdoor path (where it preserves visibility across
// landblock frustum culling).
```
(Post-RR1 the comment may already say `cameraInsideCell` instead of `cameraReallyInside`. Adjust the `old_string` to match whatever's actually there.)
- [ ] **RR2-S9: Update weather post-scene gate**
Find this block (currently ~72527267):
```csharp
// Bug A fix (post-#26 worktree, 2026-04-26): weather sky
// meshes (Properties & 0x04, e.g. the 815m-tall rain
// cylinder 0x01004C42/0x01004C44) render AFTER the scene so
// the additive rain streaks overlay terrain and entities
// instead of being painted over by them. This is the second
// half of retail's LScape::draw split — GameSky::Draw(1)
// fires after the DrawBlock loop. Same indoor gate as the
// sky pass: weather is suppressed inside cells.
if (!cameraInsideCell)
{
_skyRenderer?.RenderWeather(camera, camPos, (float)WorldTime.DayFraction,
_activeDayGroup, kf, environOverrideActive);
if (_particleSystem is not null && _particleRenderer is not null)
_particleRenderer.Draw(_particleSystem, camera, camPos,
AcDream.Core.Vfx.ParticleRenderPass.SkyPostScene);
}
```
Replace the gate line only:
```csharp
if (!cameraInside)
{
_skyRenderer?.RenderWeather(camera, camPos, (float)WorldTime.DayFraction,
_activeDayGroup, kf, environOverrideActive);
if (_particleSystem is not null && _particleRenderer is not null)
_particleRenderer.Draw(_particleSystem, camera, camPos,
AcDream.Core.Vfx.ParticleRenderPass.SkyPostScene);
}
```
(Comment block stays. Only the `if (!cameraInsideCell)``if (!cameraInside)` line.)
- [ ] **RR2-S10: Build to verify all edits compile**
```bash
dotnet build -c Debug --nologo
```
Expected: green, 0 errors, 0 warnings. If the compiler reports an undefined `cameraInsideCell` anywhere, find that remaining reference and rename it to `cameraInside`.
- [ ] **RR2-S11: Run full test suite**
```bash
dotnet test --nologo
```
Expected: failures within the documented 1423 flaky window only (PhysicsResolveCapture/Diagnostics static-leak from unrelated tests, pre-existing).
If a test fails with `cameraInsideCell` in its name or assertion message, that's a stale reference — investigate and update.
- [ ] **RR2-S12: Commit**
```bash
git add src/AcDream.App/Rendering/GameWindow.cs
git commit -m "$(cat <<'EOF'
feat(render): Phase A8 RR2 — restructure render frame to WB-faithful order
Replaces the R3.5 v1+v2 frankenstein (terrain twice + depth-clear
workaround + two-flag asymmetry) with WB's RenderInsideOut order
verbatim:
Outdoor frame (cameraInside == false): unchanged from pre-A8.
sky -> terrain -> Draw(All) -> weather
Indoor frame (cameraInside == true):
1. skip initial sky
2. skip initial terrain
3. NO depth-clear (deleted)
4. MarkAndPunch (camera-cell exit portals)
5. IndoorPass (cell mesh + statics + building shells)
6. EnableOutdoorPass
7. stencil-gated sky (DepthMask off; closes R4 Issue B)
8. stencil-gated terrain re-draw
9. stencil-gated OutdoorScenery
10. DisableStencil
11. LiveDynamic
12. skip weather
Unifies the two-flag asymmetry into a single strict `cameraInside`
flag computed via PointInCell. Grace mechanism in CellVisibility
stays alive for non-render consumers.
Closes the architectural mismatch flagged in the 2026-05-26 R3.5
restructure handoff. R1 (#78 fix) + R2 (taxonomy partition) + R3
(stencil pipeline) shipped earlier; this commit replaces R3.5's
patches.
Design: docs/superpowers/specs/2026-05-26-phase-a8-restructure-design.md
Plan: docs/superpowers/plans/2026-05-26-phase-a8-restructure.md
WB reference: references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/VisibilityManager.cs:73-160
RR3 verifies the SkyRenderer-stencil-contract precondition for the
stencil-gated sky step. RR4 visual-verifies. RR5 ships docs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```
---
## Task RR3: Verify SkyRenderer doesn't toggle stencil state
**Goal:** Confirm the stencil-gated sky step's precondition: `_skyRenderer.RenderSky` does NOT touch `EnableCap.StencilTest`, `StencilFunc`, `StencilOp`, or `StencilMask` between EnableOutdoorPass's setup and the sky fragment writes. If it does, our gate is lost.
**Files:**
- Read: `src/AcDream.App/Rendering/SkyRenderer.cs`
- Possibly modify: `src/AcDream.App/Rendering/GameWindow.cs` (only if SkyRenderer is dirty)
- [ ] **RR3-S1: Grep SkyRenderer for stencil-state calls**
```bash
grep -n "Stencil\|stencil" src/AcDream.App/Rendering/SkyRenderer.cs
```
Possible outcomes:
- **No matches at all** → SkyRenderer is clean. Proceed to RR3-S2 (note-only commit).
- **Matches found** → inspect each. If they are inside conditional code paths that aren't reachable from our `RenderSky` call site (e.g., dead code, debug-only), still clean. If they touch live state during a normal `RenderSky` call, dirty — proceed to RR3-S3 (wrapper required).
Most likely outcome: no matches. SkyRenderer was written for outdoor rendering with no stencil concerns.
Also grep for any other GL state SkyRenderer may touch that could affect our step (DepthFunc, ColorMask):
```bash
grep -n "DepthFunc\|ColorMask\|DepthMask" src/AcDream.App/Rendering/SkyRenderer.cs
```
If SkyRenderer changes `DepthMask` internally and restores, our outer `DepthMask(false)` + `DepthMask(true)` brackets may be redundant but safe. If SkyRenderer permanently changes `DepthMask` without restoring, we have a different problem (also worth knowing).
- [ ] **RR3-S2: If SkyRenderer is clean, commit a verification note**
If RR3-S1 found no stencil-state writes, update the comment in `GameWindow.cs` at the stencil-gated sky step to record the verification. Find this comment (added in RR2-S7):
```csharp
// PRE-FLIGHT (RR3): SkyRenderer.RenderSky must NOT
// toggle stencil state internally. Verified in commit
// <RR3 SHA — fill in after RR3 ships>.
```
Replace `<RR3 SHA — fill in after RR3 ships>` with this commit's SHA (you'll know it once you run `git commit`; first commit with the placeholder, then `git commit --amend` after seeing the SHA, OR write the comment with the line-range reference to SkyRenderer.cs instead of a SHA reference). Preferred form (line-range, more durable than a SHA):
```csharp
// PRE-FLIGHT (RR3): SkyRenderer.RenderSky must NOT
// toggle stencil state internally. Verified by reading
// src/AcDream.App/Rendering/SkyRenderer.cs at commit
// time — no Stencil*/EnableCap.StencilTest calls in
// the file.
```
Commit:
```bash
git add src/AcDream.App/Rendering/GameWindow.cs
git commit -m "$(cat <<'EOF'
chore(render): Phase A8 RR3 — verify SkyRenderer doesn't touch stencil
Pre-flight precondition for the RR2 stencil-gated sky step: confirms
SkyRenderer.RenderSky doesn't enable/disable stencil test, change
stencil function/op/mask. Verified by grep over
src/AcDream.App/Rendering/SkyRenderer.cs — no matches.
Comment at the stencil-gated sky call site updated to record the
verification + reference the verified source.
No behavior change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```
- [ ] **RR3-S3: If SkyRenderer is dirty, add a state save/restore wrapper**
This step only runs if RR3-S1 found stencil writes inside `SkyRenderer.RenderSky` that would interfere.
In `GameWindow.cs`, replace the stencil-gated sky block (RR2-S7's insertion) with a state-saving variant:
```csharp
// 5a. Stencil-gated SKY with state save/restore wrapper.
// SkyRenderer.RenderSky touches stencil state (see
// RR3-S1 finding); save before, restore after.
_gl!.GetInteger(GLEnum.StencilFunc, out int savedStencilFunc);
_gl!.GetInteger(GLEnum.StencilRef, out int savedStencilRef);
_gl!.GetInteger(GLEnum.StencilValueMask, out int savedStencilValueMask);
_gl!.GetInteger(GLEnum.StencilWritemask, out int savedStencilWritemask);
bool savedStencilTest = _gl!.IsEnabled(EnableCap.StencilTest);
_gl!.DepthMask(false);
_skyRenderer?.RenderSky(camera, camPos, (float)WorldTime.DayFraction,
_activeDayGroup, kf, environOverrideActive);
_gl!.DepthMask(true);
// Restore stencil state to what EnableOutdoorPass left us with.
if (savedStencilTest) _gl!.Enable(EnableCap.StencilTest);
else _gl!.Disable(EnableCap.StencilTest);
_gl!.StencilFunc((StencilFunction)savedStencilFunc, savedStencilRef, (uint)savedStencilValueMask);
_gl!.StencilMask((uint)savedStencilWritemask);
```
Commit:
```bash
git add src/AcDream.App/Rendering/GameWindow.cs
git commit -m "$(cat <<'EOF'
fix(render): Phase A8 RR3 — wrap stencil-gated sky with state save/restore
SkyRenderer.RenderSky internally touches stencil state (verified in
RR3-S1). The stencil-gated sky step inside the A8 indoor branch needs
the EnableOutdoorPass stencil setup intact for the subsequent terrain
re-draw and OutdoorScenery passes. Wrap RenderSky with glGetInteger
save + restore so the outer stencil context survives.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```
---
## Task RR4: Visual verification matrix
**Goal:** Confirm the restructure delivers the design's promises across the four building types + both transitions + sky-through-windows. Human runs the scenarios; Claude collects logs; per-scenario PASS/FAIL recorded against the acceptance criteria.
**Files:**
- Create: `docs/research/2026-05-26-a8-rr4-visual-verification.md` — outcome doc
- [ ] **RR4-S1: Build for verification**
```bash
dotnet build src\AcDream.App\AcDream.App.csproj -c Debug --nologo
```
Expected: green.
- [ ] **RR4-S2: Pre-launch — close any running client; launch fresh**
```powershell
$proc = Get-Process -Name AcDream.App -ErrorAction SilentlyContinue
if ($proc) {
$proc.CloseMainWindow() | Out-Null
if (-not $proc.WaitForExit(5000)) { $proc | Stop-Process -Force }
}
Start-Sleep -Seconds 3
$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"
dotnet run --project src\AcDream.App\AcDream.App.csproj --no-build -c Debug 2>&1 |
Tee-Object -FilePath "launch-a8-rr4-verify.log"
```
Background run (so the tool returns; human keeps the client window foreground).
- [ ] **RR4-S3: Scenario A — Holtburg cottage interior**
Human walks `+Acdream` into a Holtburg cottage near the network portal. Stand in the middle of the room. Look at each wall in turn. Look out a window.
**Acceptance:**
- All walls SOLID — no see-through to outdoor terrain
- Outdoor terrain visible ONLY through windows / open doors
- **SKY visible through windows** (the R4 Issue B closure)
- Player character body visible (no head-backwards, no missing limbs)
- No "transparent rectangles around buildings" regression (#100 stays closed)
Record per criterion: PASS / FAIL with notes.
- [ ] **RR4-S4: Scenario B — Holtburg cottage cellar**
Walk to a cottage cellar (descend stairs).
**Acceptance:**
- Cellar walls + floor + ceiling SOLID
- Cellar stairs SOLID from inside view — no grass/terrain overlay through the stair geometry
- (Out-to-in stair-grass artifact is NOT A8 scope; will be filed as #103 by RR5 if observed)
Record per criterion: PASS / FAIL.
- [ ] **RR4-S5: Scenario C — Holtburg Inn (multi-room cell mesh)**
Walk into the Holtburg inn. Move through its rooms.
**Acceptance:**
- All inn walls SOLID
- Adjacent rooms not visible through walls (no "I can see the door of the next room")
- Furniture (cell statics) visible and properly positioned
- Sky visible through inn windows
Record per criterion: PASS / FAIL.
- [ ] **RR4-S6: Scenario D — A dungeon (portal-entry indoor world)**
Pick any reachable dungeon (network portal to a known dungeon).
**Acceptance:**
- Corridor walls SOLID
- Adjacent corridors not visible through walls
- Lighting reads as indoor (sun zeroed, indoor ambient applied)
- No outdoor stab/terrain leak
- (Dungeons typically have no exit portals; no sky-through-window expected. If a dungeon HAS exit portals, sky should show through them.)
Record per criterion: PASS / FAIL.
- [ ] **RR4-S7: Transition scenarios — exit and entry**
Walk through an entry/exit transition at the cottage door several times.
**Exit transition (indoor → outdoor):**
- Clean transition; no through-ground objects; no missing walls
- (If A reproduces AND RR0 confirmed pre-existing on main, this is documented limitation; PASS with note)
**Entry transition (outdoor → indoor):**
- Clean transition; no floor transparent showing cellar
- (If C reproduces AND RR0 confirmed pre-existing on main, documented limitation; PASS with note)
Record per scenario: PASS / FAIL with notes.
- [ ] **RR4-S8: Graceful close + write findings doc**
```powershell
$proc = Get-Process -Name AcDream.App -ErrorAction SilentlyContinue
if ($proc) {
$proc.CloseMainWindow() | Out-Null
if (-not $proc.WaitForExit(5000)) { $proc | Stop-Process -Force }
}
```
Create `docs/research/2026-05-26-a8-rr4-visual-verification.md` capturing each scenario's PASS/FAIL outcome with observations.
Template:
```markdown
# A8 RR4 visual verification — render-frame restructure outcome
**Date:** 2026-05-26
**HEAD:** [restructure-commit SHA from RR2]
**Build:** green
## Scenario outcomes
| Scenario | Acceptance | Outcome | Notes |
|---|---|---|---|
| A: cottage interior | walls solid + sky through windows | PASS / FAIL | |
| B: cottage cellar | walls solid + stairs solid from inside | PASS / FAIL | |
| C: inn (multi-room) | walls solid + sky through windows | PASS / FAIL | |
| D: dungeon | walls solid + indoor lighting | PASS / FAIL | |
| Exit transition | clean | PASS / FAIL / DOCUMENTED PRE-EXISTING | |
| Entry transition | clean | PASS / FAIL / DOCUMENTED PRE-EXISTING | |
## Gate decision
- [ ] All four building-type scenarios PASS → proceed to RR5
- [ ] Any building-type scenario FAILED → STOP, invoke /investigate
- [ ] Transition scenarios FAILED but RR0 confirmed pre-existing → file as new ISSUES.md entries in RR5
- [ ] Transition scenarios FAILED and RR0 did NOT confirm pre-existing → STOP, re-brainstorm
```
Commit the findings:
```bash
git add docs/research/2026-05-26-a8-rr4-visual-verification.md
git commit -m "$(cat <<'EOF'
docs(research): Phase A8 RR4 — visual verification outcome
Cottage / cellar / inn / dungeon + exit/entry transition + sky-through-
windows scenarios per the design's acceptance matrix. Gate decision
recorded for RR5 ship-or-stop.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```
- [ ] **RR4-S9: Gate decision**
Per the findings:
- All building-type scenarios PASS AND transition scenarios are clean OR documented-pre-existing → proceed to RR5.
- Any building-type scenario FAILED → STOP. Open `/investigate` to triage. Do NOT ship RR5.
- Transition scenarios failed and NOT pre-existing → STOP. Re-brainstorm scope expansion.
---
## Task RR5: Ship docs (close #78, file follow-ups, update CLAUDE.md)
**Goal:** Move #78 to closed, file new ISSUES for known limitations + any RR0-confirmed pre-existing transition issues, update CLAUDE.md's A8 paragraph from "PAUSED" → "SHIPPED."
**Files:**
- Modify: `docs/ISSUES.md`
- Modify: `CLAUDE.md`
- [ ] **RR5-S1: Read ISSUES.md to confirm next available ID**
```bash
grep -n "^## #1[0-9][0-9]" docs/ISSUES.md
```
Expected: shows the highest #1xx ID currently filed. Tentative next available was #102 at plan-write time (highest filed = #101). Verify and adjust.
- [ ] **RR5-S2: Move #78 to Recently closed**
Find #78's current entry in `docs/ISSUES.md` (OPEN). Move it to the "Recently closed" section with format:
```markdown
**#78 — Outdoor stabs/buildings visible through the rendered floor** — CLOSED 2026-05-26 by Phase A8 (R1+R2+R3+RR1+RR2 across commits ed72704 → [RR2 SHA]). Restructure ports WB's RenderInsideOut stencil pipeline with a corrected entity taxonomy (WorldEntity.IsBuildingShell). Visual-verified at cottage interior, cellar, inn, and dungeon. Sky visible through windows (Issue B closure).
```
- [ ] **RR5-S3: File #102 — Cross-cell-portal far-side visibility (Step 5 deferral)**
Add this OPEN issue (use the actual next available ID confirmed in RR5-S1):
```markdown
## #102 — Far-side portal visibility through walls (WB Step 5 deferral)
**Status:** OPEN (low priority; first ship of A8 deferred this).
**Description:** When standing inside a multi-room building, looking at a wall between rooms, portals on the FAR side of the room (e.g. a doorway opening to outdoors on the other side of the wall) may have their silhouette stencil-marked by Phase A8. This lets outdoor terrain leak through the wall at that silhouette. The first-ship approximation in A8 RR2 stencil-marks ONLY the camera's own cell's exit portals (not BFS-extended VisibleCellIds), which AVOIDS the leak in most cases but loses cross-cell-portal visibility.
**Acceptance:** Inside Holtburg Inn looking at the wall between two rooms, no visible terrain or scenery shows through. WB Step 5's 3-stencil-bit cross-building pipeline is the reference fix (see references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/VisibilityManager.cs:156-232).
**Files:**
- src/AcDream.App/Rendering/IndoorCellStencilPipeline.cs — currently single-bit stencil; would extend to bits 1+2.
- src/AcDream.App/Rendering/GameWindow.cs — render frame would gain a per-far-portal pass.
```
- [ ] **RR5-S4: File #103 — Outdoor-to-indoor cellar terrain Z-fight (out-to-in artifact)**
Add this OPEN issue:
```markdown
## #103 — Outdoor-to-indoor cellar terrain Z-fight (out-to-in artifact)
**Status:** OPEN (low priority; pre-existing).
**Description:** Looking from OUTSIDE a cottage cellar at the stair geometry from above, grass/terrain may overlap the stair triangles. Pre-existing; not addressed by A8 (no stencil work runs when the camera is outside). #100's 1cm terrain Z nudge is insufficient because cellar geometry sits multiple meters below terrain Z — depth-precision artifacts persist at oblique angles.
**Acceptance:** From outside a cottage, looking at the cellar entrance, stair geometry reads as solid stone (no grass overlay) regardless of camera angle.
**Files:** likely a deeper terrain-occlusion mechanism (per-cell terrain mask, or proper outdoor portal culling) — beyond the scope of A8.
```
- [ ] **RR5-S5: File R4 Issue A as #104 IF RR0 confirmed pre-existing on main**
Read `docs/research/2026-05-26-a8-rr0-falsification-findings.md` written in RR0-S4. If outcome was "all three reproduce" (Issue A pre-existing on main), file:
```markdown
## #104 — Exit transition: objects visible through ground (pre-existing)
**Status:** OPEN (medium priority; pre-existing on main, confirmed by A8 RR0 falsification).
**Description:** Briefly after the camera exits a cell (transitioning indoor → outdoor), objects below terrain Z (cellar geometry, basement entities) may render visible through the ground for 13 frames. Falsified against `main` in [a8-rr0-falsification-findings.md](../research/2026-05-26-a8-rr0-falsification-findings.md) — reproduces on `main` without any A8 work. Likely root cause: terrain Z + grace-frame interaction with entity depth tests at certain camera angles.
**Acceptance:** Exit a cottage; no objects visible through the ground at any camera angle during the transition. Steady-state outdoor view: terrain occludes below-ground entities cleanly.
**Files:**
- Likely terrain shader (terrain_modern.vert) or the cell-grace mechanism (`CellVisibility.FindCameraCell`).
- May need a "true outside" gate that ignores grace for depth-test correctness.
```
(If RR0 did NOT confirm pre-existing, skip this step. The expected handling was already decided in RR0-S5.)
- [ ] **RR5-S6: File R4 Issue C as #105 IF RR0 confirmed pre-existing on main**
Same condition as RR5-S5. If RR0 confirmed Issue C pre-existing:
```markdown
## #105 — Entry transition: floor transparent showing cellar (pre-existing)
**Status:** OPEN (medium priority; pre-existing on main, confirmed by A8 RR0 falsification).
**Description:** Briefly during the camera entry into a cottage (outdoor → indoor transition), the cottage floor may go transparent showing the cellar geometry below, with the cellar ceiling's texture flickering at the floor pixels. Falsified against `main` in [a8-rr0-falsification-findings.md](../research/2026-05-26-a8-rr0-falsification-findings.md) — reproduces on `main` without any A8 work. Likely root cause: Z-fight between cottage floor mesh and cellar ceiling mesh at adjacent world Z values (both with +0.02 EnvCell render lift).
**Acceptance:** Enter a cottage; floor reads as solid floor texture throughout the transition. No glimpse of cellar geometry through the floor.
**Files:**
- The +0.02m EnvCell render lift might need per-cell adjustment.
- Alternative: depth-fight-resolving via polygon offset on indoor cell meshes.
```
- [ ] **RR5-S7: Update CLAUDE.md A8 paragraph**
Find the current A8 paragraph in `CLAUDE.md` (begins with "**A8.P3 —" or "**Phase A8 —"; locate by grep). Find the "PAUSED" / "R3.5" / "restructure" mentions. Replace with a SHIPPED summary:
```markdown
**Phase A8 — Indoor-cell visibility culling — SHIPPED 2026-05-26.** Closes
issue #78. Six tasks across the restructure (after R1+R2+R3 shipped earlier
in the same week and R3.5 v1+v2 paused for restructure):
- RR0: falsification spike — confirmed R4 Issues A + C [pre-existing on main / A8-specific] (per `docs/research/2026-05-26-a8-rr0-falsification-findings.md`).
- RR1: reverted R3.5 v1 + v2 patches (`38d5374`, `2bfeafd`) as two new revert commits.
- RR2: restructured render frame to WB-faithful RenderInsideOut order. Skipped initial sky+terrain when inside; deleted depth-clear-if-inside; added stencil-gated sky inside the indoor branch (closes R4 Issue B). Unified the two-flag asymmetry into a single strict `cameraInside` flag via PointInCell.
- RR3: verified `SkyRenderer.RenderSky` doesn't toggle stencil state internally — stencil-gated sky step's precondition holds.
- RR4: visual-verified at Holtburg cottage interior + cellar + Inn + dungeon, plus exit/entry transitions and sky-through-windows.
- RR5 (this ship-docs commit).
Five deferred follow-ups remain open:
- #102 — Cross-cell-portal far-side visibility (WB Step 5).
- #103 — Cellar terrain Z-fight from outside (pre-existing).
- #104 — Exit transition through-ground (filed only if RR0 confirmed pre-existing).
- #105 — Entry transition transparent floor (filed only if RR0 confirmed pre-existing).
- Grace-mechanism cleanup (Q4 third option from brainstorm).
Full design: [docs/superpowers/specs/2026-05-26-phase-a8-restructure-design.md](docs/superpowers/specs/2026-05-26-phase-a8-restructure-design.md).
Plan: [docs/superpowers/plans/2026-05-26-phase-a8-restructure.md](docs/superpowers/plans/2026-05-26-phase-a8-restructure.md).
Restructure handoff (historical): [docs/research/2026-05-26-a8-r3.5-restructure-handoff.md](docs/research/2026-05-26-a8-r3.5-restructure-handoff.md).
```
(Customize the "Five deferred follow-ups" list based on which of #104 + #105 were actually filed — if RR0 confirmed neither pre-existing, both come off the list; if just A, drop #105; etc.)
Also find the "Currently working toward" line + the A8 references in the Milestone Discipline section. Update them to reflect ship status (e.g. "M1.5 — Indoor world feels right — partially advanced; A8 shipped 2026-05-26; remaining indoor work continues at [next phase]").
- [ ] **RR5-S8: Commit ship docs**
```bash
git add docs/ISSUES.md CLAUDE.md
git commit -m "$(cat <<'EOF'
ship(render): Phase A8 — indoor-cell visibility culling SHIPPED
Closes #78 (outdoor stabs/terrain visible through indoor walls). Files
#102 (cross-cell-portal Step 5 deferral), #103 (cellar Z-fight from
outside, pre-existing), and [#104/#105 if RR0 confirmed Issues A/C
pre-existing on main].
Visual-verified at Holtburg cottage interior + cellar, Holtburg Inn,
and dungeon. Sky visible through windows. Exit/entry transitions
[clean / documented pre-existing per RR0 findings].
Architecture: WB RenderInsideOut order verbatim. Entity taxonomy
partition (WorldEntity.IsBuildingShell from R1) drives
WbDrawDispatcher.EntitySet (R2) into IndoorPass / OutdoorScenery /
LiveDynamic. Stencil-gated sky inside the indoor branch is acdream's
enhancement over WB's reference.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```
---
## Self-review
**Spec coverage check:**
- [x] Q1 (initial-terrain conditional) → RR2-S4 gates terrain on `!cameraInside`.
- [x] Q2 (sky through windows) → RR2-S7 inserts stencil-gated sky step.
- [x] Q3 (Issue C investigation) → RR5-S6 files as #105 if RR0 confirmed pre-existing.
- [x] Q4 (gate unification) → RR2-S1 introduces single `cameraInside`; RR2-S2/S3/S6/S8/S9 propagate.
- [x] Q5 (R3.5 revert) → RR1-S1/S2 revert.
- [x] RR0 spike (pre-restructure falsification) → entire Task RR0 + decision gate RR0-S5.
- [x] SkyRenderer precondition check → Task RR3.
- [x] Visual verification matrix (cottage/cellar/inn/dungeon + transitions + sky-through-windows) → RR4 Scenarios AD + S7 transitions.
- [x] Close #78 + file new issues → RR5-S2 through RR5-S6.
**Placeholder scan:**
- No "TBD" or "implement later" — every step has actual code or actual commands.
- Two intentional placeholder substitutions: `[RR2 SHA]` in the #78 closure entry (filled in at RR5 execution time) and `[a SHA / pre-existing / not pre-existing]` strings in the CLAUDE.md update template (filled in based on RR0 outcome). These are NOT spec failures — they're explicit "fill in at execution time based on observed state."
- "Customize" appears once in RR5-S7 for the deferred-follow-ups list — accompanied by a clear conditional that the executor fills in.
**Type consistency:**
- `cameraInside` (single source of truth) used consistently across RR2-S1/S2/S3/S6/S8/S9; never spelled `cameraInsideCell` or `cameraReallyInside` in any post-RR1 step.
- `_indoorStencilPipeline` is the field name referenced everywhere (matches R3's already-shipped code).
- `EntitySet.IndoorPass` / `OutdoorScenery` / `LiveDynamic` enum values match R2's already-shipped code.
- `IndoorCellStencilPipeline` method names — `UploadPortalMesh`, `MarkAndPunch`, `EnableOutdoorPass`, `DisableStencil` — match the dormant infrastructure shipped in Tasks 16.
**Task granularity:**
- RR0: 5 steps, each ~515 minutes (most are launch-and-observe). Visual repro takes the longest.
- RR1: 4 steps, each <2 minutes.
- RR2: 12 steps, each 25 minutes (mechanical edits with exact code shown).
- RR3: 3 steps (S2 OR S3 depending on S1 outcome), each 510 minutes.
- RR4: 9 steps, each 310 minutes (mostly observation).
- RR5: 8 steps, each 510 minutes (mostly edits to two markdown files).
Total estimated time: ~2.53 hours assuming RR0 doesn't trigger scope expansion.
**Outcome contingencies:**
- RR0 outcome 2 (A8-caused) STOP at RR0-S5. Re-brainstorm.
- RR3-S1 dirty divert to RR3-S3 wrapper.
- RR4 building-type FAILED STOP at RR4-S9. Open `/investigate`.
- RR4 transition FAILED + not pre-existing STOP, re-brainstorm.
All gates explicit; no dead-ends.
---
**SUPERSEDED 2026-05-26 (PM) by [2026-05-26-phase-a8-wb-full-port.md](2026-05-26-phase-a8-wb-full-port.md).**
This plan implemented the "WB-faithful restructure" design which RR0 evidence invalidated. The new plan implements the full WorldBuilder RenderInsideOut + RenderOutsideIn port. RR0 was completed and its findings doc is retained.
This document is retained for historical reference.