R-A2b (485e44d) killed the 0171<->0173 churn (maxPop 16->1, measured). Visible flap residual is sec 4 (edge-on openings render-side + corner camera-seal). Camera-damping tried+failed+reverted. The white-walls scare was a RED HERRING: heavy per-frame probes (ACDREAM_PROBE_FLAP) starve the thread-unsafe dat-reader so texture-decode loses the race -> white; a clean launch (no probes) fixes it. The dat-reader thread-safety bug is the real underlying issue (filed). Repo clean at HEAD.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
15 KiB
HANDOFF — R-A2b shipped (churn killed) · flap residual is §4 · the texture RED-HERRING
Date: 2026-06-09. Branch: claude/thirsty-goldberg-51bb9b. HEAD: 485e44d.
Milestone: M1.5 (indoor world feels right). Phase: full retail render port (Option A) → R-A2b done; §4 open.
Read this top-to-bottom before any code. This session shipped a real, MEASURED fix (R-A2b) but the VISIBLE flap is only partly resolved, and a separate runtime bug (the dat-reader) + a self-inflicted diagnostic mistake (heavy probes) caused a long, confusing detour ("missing textures"). The §5 lesson at the bottom is the most important durable takeaway.
0. TL;DR
- R-A2b SHIPPED + MEASURED (commit
485e44d): removed the&& !eyeInsideOpeningbypass from the portal-flood side-cull so back portals cull like retail'sPView::InitCellside test. This kills the0171↔0173re-enqueue churn — measuredmaxPop16 → 1 (44 % of frames churning → 0 %, across 1.3 M frames). The flood is now deterministic. 218 App tests green, including the void-fix and #95 over-inclusion guards. - But R-A2b did NOT fix the VISIBLE flap. The remaining grey flicker at doorways/windows is §4, a different mechanism: the openings project edge-on from the 3rd-person eye and the clip collapses (geometric — retail's clip collapses too; retail avoids it by keeping the eye head-on/collided). R-A2b killed the churn layer; the edge-on layer remains.
- The camera-damping attempt FAILED and was reverted (laggy, no flap improvement). Do not re-try it.
- The "missing textures" scare was a RED HERRING, not a code bug. The cottage walls rendered WHITE
because the heavy debug probes (
ACDREAM_PROBE_FLAP=1+ per-frameTeeto multi-hundred-MB logs) starve the thread-unsafe dat-reader, which then fails to decode the wall textures. A CLEAN launch (no probes) renders correctly — user-confirmed. The underlying dat-reader thread-safety bug (theAccessViolationcrashes) is real and FILED. - Repo is clean at HEAD
485e44d(R-A2b in). Only uncommitted tracked change: the plan-doc PINNED note (committed alongside this handoff). Throwawayanalyze_*.py+*.logare untracked.
1. What shipped — R-A2b (the churn fix), commit 485e44d
The change (one functional edit + a probe): in PortalVisibilityBuilder.cs, the side-cull was
if (i < ClipPlanes.Count && !CameraOnInteriorSide(...) && !eyeInsideOpening) continue;. R-A2b drops the
&& !eyeInsideOpening, in BOTH Build and BuildFromExterior. Retail's PView::InitCell side test
(decomp :432962) culls a back-facing portal by the side test ALONE — there is no eye-in-opening bypass.
The forward-portal clip-empty void rescue (Build ~clippedRegion.Count == 0 branch, the 2026-06-05
fix) is a SEPARATE code path and is untouched — Build_EyeStandingInInteriorPortal_FloodsNeighbour
stays green.
The pin (B1, not B2): live capture flap-sidechk.log showed every back portal (0173→0171,
0172→0173) with camInterior=False (our side test already agrees with retail — it WANTS to cull) and
traversed only when eyeIn=True (eye within 1.75 m of the shared doorway). So the cycle was the
eyeInsideOpening bypass, not a CameraOnInteriorSide convention bug. Forward portals showed
camInterior=True (unaffected; void rescue preserved).
Measured result:
launch-churn-confirm.log(pre-fix walk):maxPopworst 16, 44 % of frames≥2.flap-fix-verify.log/flap-structured.log(post-fix):maxPopworst 1, 0 %≥2across ~1.3 M frames. The back portal is nowskip=side(culled) even ateyeIn=True. Churn eliminated.- New RED→GREEN test
Build_BackFacingPortal_EyeStandingInOpening_StillCulled; full App suite 218 green.
Docs: spec docs/superpowers/specs/2026-06-09-portal-flood-bounded-propagation-r-a2b-design.md
(REVISION → Option B), plan docs/superpowers/plans/2026-06-09-portal-flood-r-a2b-side-cull.md (with the
PINNED B1 note). Commits: 3fd71a1 (spec) → 7b8a490 (plan) → 89a2032 (sidechk probe) → 485e44d (fix).
R-A2b status / disposition: committed at HEAD. It is a real, retail-faithful, measured improvement
(no more churn → deterministic flood, better perf). It is innocent of the texture issue (§5). It does
NOT, by itself, make the visible flap go away (that's §4). Recommendation: keep it. Next session
should do ONE clean (no-probe) launch to re-confirm it renders + the churn stays gone, then move to §4.
(Plan Phase 3 — removing the now-dead MaxReprocessPerCell cap — was NOT done; it's optional cleanup,
risky for the synthetic cyclic tests that have no ClipPlanes. Leave the cap as a harmless backstop, or do
it carefully with fixture ClipPlanes.)
2. The VISIBLE flap residual = §4 (NOT fixed by R-A2b)
Two DISTINCT sub-issues, both ending in "background where geometry should be." Do not conflate them.
2a. Doorway / window grey flap (room↔room) — RENDER-side edge-on
- Measured (
flap-structured.log, the user's room→room pass): root0171visoscillates 2↔3↔4 (1,725 transitions);OutsideViewoutPolysoscillates 0↔1 (1,544 transitions) → the outdoor-through-window region flips empty(grey)↔drawn; 8,574 frames have an edge-onclip=0. - The eye is collision-correct here:
viewerCell=0171(the adjacent INTERIOR cell), ~1.7 m back, no wall hit. So it is NOT penetration. The openings simply project edge-on from that legitimate position → the clip collapses to <3 verts → grey. - §5.3 (decomp) established the clip collapse is GEOMETRIC — retail's
polyClipFinishcollapses at edge-on too. There is no clip robustness to port. Retail avoids the flicker because its eye is collided/head-on 93 % at the doorway (flap-cam-measure.log), so it rarely views openings edge-on. Ours floats (pulledIn~0/collNormValid=False97 %). - So 2a is bounded by the camera, not the clip. But the camera-damping attempt to address it FAILED (§3).
2b. Corner see-through — CAMERA-SEAL failure (eye penetrates wall)
- Live-captured (
flap-corner.logtail): pressing the camera into a corner → eye escapes to outdoor cell0xA9B40031atX=165.22(well outside the cottage),pulledIn=0,collNormValid=False(NO collision stopped the 2.61 m boom) → render roots outside → the ENTIRE interior drops → bluish background. Retail "under no circumstances sees through the wall." - The viewer sweep does query the cottage exterior-shell GfxObj (
FindObjCollisionspassesisViewer,TransitionTypes.cs:2376), so it's not a "shell not queried" gap. The boom slips through somewhere (corner seam? the interior doorway is open so the boom legitimately enters the next room, but in a corner it reaches the exterior). Not fully root-caused. This is the most CONCRETE, tractable §4 bug — fixing the camera seal (keep the eye inside, like retail) would also reduce 2a (head-on eye).
3. DO-NOT-RETRY (failed/refuted this session)
- Camera-damping spring-arm on the published eye (a separate
_publishedEyedamped toward the collision result,RetailChaseCamera.cs). TRIED, FAILED: laggy camera feel, zero flap improvement (the forward-crossing flap has no collision to damp; the eye is already smooth). Fully reverted (git checkout, verified). Do not re-try damping the published eye. - "It's the corner" / "runtime-state, relaunch fixes it" / "disk full" — all WRONG explanations I gave for the white walls. The truth is §5 (probes starve the dat-reader). Don't repeat these.
- Carried-over DO-NOTs (still valid): byte-stable eye; bounded-propagation/churn as the WHOLE story (it was the churn LAYER, R-A2b fixed it); two-pipe split; PVS membership grounding retail lacks; the §3 "port retail's edge-on clip robustness" (retail has none).
4. WHAT TO WORK ON NEXT (recommendation)
This flap has consumed many sessions for partial results. Honest options, in rough preference order:
- Re-validate + keep R-A2b, then attack §2b (the corner camera-seal). It's the most concrete,
measured-wrong behavior (eye demonstrably escapes to an outdoor cell). Root-cause WHY the viewer-sphere
sweep finds no collision when the boom reaches the exterior in a corner (the sweep queries the shell but
collNormValid=False). Fixing the seal (eye stays inside) is retail-faithful AND would dampen 2a (a head-on eye doesn't view openings edge-on). Decomp oracle:SmartBox::update_viewer(:92761), theviewer_spheresweep + cell enclosure. - Accept/defer the flap and move to milestone work. The §2a edge-on flicker is genuinely hard (retail relies on the camera; our 3rd-person floating eye is the divergence) and the visible payoff per session has been low. Per the milestone discipline, M2 ("kill a drudge": F.2/F.3/F.5a/L.1c/L.1b) or other M1.5 issues may be a better use of effort than continued flap grinding. This is a legitimate call — flag it to the user.
- Fix the dat-reader thread-safety bug (the real underlying cause of both the crashes and the white-wall load failures). See §5. It's a correctness/stability win independent of the flap. Filed as a background task this session.
Do NOT silently keep grinding §2a with more camera tweaks — the camera-damping already failed. If pursuing the flap, §2b (seal) is the tractable lever; if not, say so and move on.
5. THE TEXTURE RED-HERRING — the durable lesson
Symptom: mid-session, the cottage walls rendered WHITE (geometry present — windows/painting/floor/NPCs drew — but the wall surfaces were the clear/background color). I called it "missing textures."
My failure: I thrashed — blamed the corner, then "runtime state," then disk, then implied my code, across ~5 messages, while the user (rightly) got furious. I should have checked baseline reproduction immediately.
Actual root cause (proven):
- The wall data wasn't loading.
[flap]probe showed cell0171with identity transform + zero portals = an unhydrated/empty cell (a dat-LOAD failure, not a flood/visibility bug — R-A2b only reads cell data, it cannot make a cell load empty). - It reproduced on the EXACT baseline (
git checkout 8f879bd,diffempty, rebuilt) — so NOT my code. - Disk fine (1074 GB free); dats intact (normal sizes); no corrupt cache.
- The trigger: the heavy per-frame probes (
ACDREAM_PROBE_FLAP=1writing[flap]/[pv-trace]/sidechkevery frame +Tee-Objectto hundreds-of-MB logs) load the render thread enough to skew the timing of the background mesh/texture-decode thread, which races on the thread-unsafe dat-reader (MemoryMappedBlockAllocator.ReadBlock— the same code that throwsAccessViolationand crashed several launches). Under that timing the texture/cell load loses the race → empty/white. - FIX: launch CLEAN (no probes).
launch-final.log: no load errors, render path + all atlases loaded, user confirmed the walls render. This is also how the user normally runs the client.
DURABLE RULES (add to memory):
- Never run normal play or a visual gate with
ACDREAM_PROBE_FLAP(or other per-frame probes) on. They starve the thread-unsafe dat-reader → intermittent white-wall / empty-cell load failures (and raise theAccessViolationcrash rate). Use probes ONLY for short, targeted captures, then relaunch clean. - For any render/load symptom, check baseline reproduction FIRST (
git stash/git checkout <baseline>- relaunch) before theorizing or blaming a change. One reproduction test beats five wrong explanations.
The real bug (separate task, FILED): DatReaderWriter ... MemoryMappedBlockAllocator.ReadBlock
AccessViolation — the dat-reader is not thread-safe under the concurrent mesh-decode/streaming access.
Fix = serialize dat block reads behind a per-DatDatabase lock, or thread-local buffers, or a private
MemoryMappedViewAccessor per reader. Files: src/AcDream.App/Rendering/Wb/ObjectMeshManager.cs,
DatDatabaseWrapper.cs, the DatReaderWriter block allocator. This is the highest-value stability fix
and is independent of the flap.
6. Apparatus (reuse; STRIP after §4 ships)
- Probes (gated in
AcDream.Core.Rendering.RenderingDiagnostics):ACDREAM_PROBE_FLAP=1→[flap](root cell per-portal D/TRV/CULL/proj/clip + outPolys + vis),[flap-sweep](camera sweep:pulledIn/collNormValid/viewerCell/in=/out=),[pv-trace](per-cell flood decisions, signature-dedup'd ≤160), and thesidechkline added this session (per-portalcamInterior/eyeIn/D). HEAVY — see §5; do not leave on for play.ACDREAM_PROBE_PORTAL_CHURN=1→[portal-churn](per-BuildmaxPop+ reciprocal pre→post).
- Throwaway analyzers (untracked, in the worktree root):
analyze_flap_vis.py(same-root vs root-swap split),analyze_churn_confirm.py(maxPop distribution + flap reproduction; takes<log> [startByte]),analyze_segment.py(windowed segment: churn/cull/flap/outPolys/camera pull-in from a byte offset). All auto-detect UTF-16 (PowerShellTee-Objectwrites UTF-16LE — Python must BOM-detect or it reads 0 lines). - cdb on retail:
tools/cdb/flap-cam-measure.cdb(retail eye + CameraManager; PDBrefs/acclient.pdb= MATCH). Exit code 5 = clean detach. - The
sidechkprobe (inPortalVisibilityBuilder.Build, aftereyeInsideOpening) is throwaway — strip it with the rest when §4 ships.
7. Repo state (exact)
- HEAD =
485e44d(R-A2b fix). Working treesrc/matches HEAD (verifiedgit diff HEAD -- src/empty). RetailChaseCamera.cs is clean (camera-damping fully reverted —grep _publishedEye= 0). - Uncommitted tracked:
docs/superpowers/plans/2026-06-09-portal-flood-r-a2b-side-cull.md(the PINNED B1 note) — committed with this handoff. - Untracked (throwaway, gitignore/delete):
analyze_*.py,flap-*.log,launch-*.log, the older*.jsonl/*.pngfrom prior sessions. - The user's last running client was the clean (no-probe) launch and renders correctly. The source now has R-A2b restored, so the next build will include R-A2b (rebuild + clean-launch to align).
8. First moves next session
- Read this doc + memory
project_indoor_flap_rootcause. git log --oneline -6(HEAD485e44d); confirmgit diff HEAD -- src/empty.- Clean launch — NO PROBES (omit
ACDREAM_PROBE_FLAP). Confirm the cottage renders (walls textured) and, if you want, that R-A2b's churn stays gone (one shortACDREAM_PROBE_PORTAL_CHURN=1capture, then relaunch clean). This re-validates R-A2b after the chaos. - Decide §4 direction with the user (per §4): §2b corner-seal (tractable) vs defer-the-flap vs the dat-reader thread-safety fix. Do NOT re-try camera-damping.