acdream/docs/research/2026-06-02-phase-w-membership-fixed-render-handoff.md
Erik 55e1b30553 docs(render): Phase W session-2 handoff — membership FIXED + render rewrite (Stage 3 done)
Canonical pickup for the next session. Membership root cause (static :1947 re-derive)
FIXED the retail way (find_cell_list interior-wins pick + swept determination, 59f3a13)
and offline-verified (doorway strobe -> one clean transition). T0 made the suite
deterministic (12 known failures, none Phase-W regressions). Stage 3 (render-root
unification) DONE (6a1fbbd->573c555). Remaining: Stage 4 (the seal: sky/landscape inside
the portal-clip bracket + conditional doorway Z-clear = no blue-hole), Stage 5 (entity/
particle clip), green-tests triage, then the single final visual verification. Render is
wire-and-fill-gaps (PView infra exists). Flags a stash discrepancy (1 of 2 stashes missing
from the shared refs/stash) for the user to check against other worktrees.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 15:39:41 +02:00

13 KiB
Raw Blame History

Phase W (session 2) — Membership FIXED + render-rewrite handoff (2026-06-02)

Canonical pickup for the next session. This session found the root cause of the indoor cell-strobe and fixed it the retail way (offline-verified), then planned + began the render rewrite. Read this first, then the design doc + the render plan. The membership half is done; the render half is planned + partly executing.

0. Git / safety state — READ FIRST

  • Branch claude/thirsty-goldberg-51bb9b — UNPUSHED. Do not push without the user.
  • ⚠ STASH DISCREPANCY — investigate. Session START had 2 stashes; now there is 1 (stash@{0}: …issue98-pre-triage-backup-20260523-145942). The #98/#101/A8-culling WIP stash (originally stash@{0}) is no longer listed. refs/stash is shared repo-wide across the user's many worktrees, and that stash's note said "pop to restore" — so it may have been legitimately popped by another worktree, OR dropped by a base-comparison git stash test in a subagent this session. A git fsck --unreachable search did NOT surface it in this repo's dangling objects. Action for the user: check your other worktrees / git reflog for it; recovery window is ~2 weeks (gc). Lesson: never let subagents run git stash while pre-existing stashes exist — instruct them to use a fresh worktree or commit-to-temp-branch for base comparisons.
  • HEAD at handoff time = 573c555 (Stage 3 complete). Stage 3 (render-root unification) DONE6a1fbbd573c555: deleted the FindCameraCell AABB grace-frame fallback, added the seen_outside sky/terrain gate (retail CellManager::ChangePosition), added CellGraph.FindVisibleChildCell, 6 CellGraphRootTests green. Build 0/0, App 160/160, Core 12-failure baseline unchanged (no new failures). Next: Stage 4. (git log --oneline -20.)
  • Throwaway untracked (delete freely): baseline-w2a-*.png, shot-*.png, launch*.log. doorway-capture.jsonl (719 MB) was deleted this session — the committed fixture tests/AcDream.Core.Tests/Fixtures/issue98/doorway-threshold-capture.jsonl replaces it.

1. Commit ledger (session 2, oldest→newest)

SHA What
840c1b6 4-model research (3 in-tree + Codex) + Phase W design (transition-membership + PView render)
50b168b chunk-1 plan (membership flicker fix)
851cecc Stage 0 — [cell-swept] diagnostic (zero behavior change)
3e1d502 Stage 1 — return swept sp.CurCellId from ResolveWithTransition (kept)
2acd8f9d23d1f4 W2b stab-list hysteresis (SHIPPED then reverted — wrong mechanism, wrong place)
ed00719 design §1a — Stage-1 gate finding: deeper root is FindEnvCollisions:1947
59f3a13 MEMBERSHIP FIX — faithful find_cell_list (interior-wins pick + swept determination, drop static :1947)
a06226f render-rewrite plan (Stages 3-5), grounded
21ee5e1,fcea816 T0 — static-leak test isolation + portable doorway fixture
6a1fbbd,3520860,38a52a7,573c555 Stage 3 DONE — render-root unification (delete FindCameraCell fallback; seen_outside gate; FindVisibleChildCell; 6 tests; no regressions)

2. What's SOLVED — cell membership (the root cause)

The indoor 0170↔0031 doorway strobe (and 0170↔0171, cellar) is fixed, offline-verified.

  • Root cause (airtight, code analysis): acdream re-derived the cell from the static sphere position twice — once in ResolveWithTransition's return (Stage 1 fixed that) and, the deeper one, in Transition.FindEnvCollisions at TransitionTypes.cs:1947, which called engine.ResolveCellId(staticOrigin,…) and clobbered sp.CheckCellId every sweep pass. Retail never re-derives statically — it carries sphere_path.curr_cell through the swept transition (validate_transition advances on an accepted move, reverts on a block) and commits it in SetPositionInternal.
  • The fix (59f3a13), the retail way:
    1. CellTransit.BuildCellSetAndPickContaining — ported retail find_cell_list's containing-cell pick (pseudo_c:308788-308819): interior-wins (first EnvCell whose BSP contains the center, stop), else the outdoor landcell via the XY-column test (which acdream had been skipping — the missing half that forced the :1947 bolt-on).
    2. FindEnvCollisions:1947 — replaced the static ResolveCellId with the swept CellTransit.FindCellSet(...) (production / DataCache != null; the old ResolveCellId stays only as a test-engine fallback). Gated downstream by the existing accept-on-move in ValidateTransition (TransitionTypes.cs:3404).
  • Offline verification: replaying the real captured doorway trajectory (committed fixture) through the fixed engine gives one clean 0170→0031 transition, no ping-pong (was a per-tick strobe). LiveCompare_FirstCap (#98 cottage-floor-cap) still passes. Not yet live-verified — folds into the single final visual gate.
  • A-vs-B is settled: "B" (transition-owned membership) was correct AND turned out small (the sweep already tracks the cell; the bug was the static re-derive). The "A" (post-sweep portal-crossing detector) was a non-retail invention — off the table.

3. Canonical artifacts

  • Design: docs/superpowers/specs/2026-06-02-phase-w-transition-membership-and-pview-render-design.md (§1 root cause, §1a the :1947 finding, §2 render target, §5 risks, §6 acceptance).
  • Render plan (per-step, grounded): docs/superpowers/plans/2026-06-02-phase-w-render-rewrite.md — T0 (done), Stage 3-5 + the green-tests triage. This is what the new session executes.
  • Membership plan: docs/superpowers/plans/2026-06-02-phase-w-membership-flicker-fix.md (done).
  • Research: docs/research/2026-06-02-retail-cell-render-study-{opus48-a,opus48-b,sonnet46,codex}.md
    • the shared prompt …-research-prompt.md + the evidence …-render-cell-membership-evidence.md.

4. Render rewrite — state + what's next

Key reframe (good news): the render half is wire-and-fill-gaps, NOT a from-scratch port. The PView infrastructure already exists — PortalVisibilityBuilder (visible-set BFS + OutsideView, enqueue-once gate, so #102's MaxReprocessPerCell is already gone), ClipFrame/ ClipFrameAssembler (the doorway clip), EnvCellRenderer GL_BLEND fix (already shipped U.4), WbDrawDispatcher cell gate — and the old RenderInsideOut/ACDREAM_A8_INDOOR_BRANCH stencil split is already gone (comments only). The gaps:

  • Stage 3 (DONE 573c555) — root render at CellGraph.CurrCell + seen_outside; deleted the AABB FindCameraCell grace-frame fallback; seen_outside sky/terrain gate (retail CellManager::ChangePosition); CellGraph.FindVisibleChildCell added. Interim state (expected, no user sees it before the final gate): sky may draw full-screen indoors until Stage 4 clips it.
  • Stage 4 (the seal) — draw sky/landscape inside the portal-clip bracket + the conditional doorway Z-clear (depth only, not color → no blue-hole); verify ceilings + opaque walls. Key gap found: sky currently draws BEFORE the clip bracket (GameWindow.cs:~7268); move it in.
  • Stage 5 — clip entities/particles to the PView visible set (kills NPC/door/smoke bleed).
  • Green-tests triage — see §5.

The plan has exact file:line change-points (verify — lines shift): physicsRoot/visibility ~GameWindow.cs:7162-7166; sky gate ~:7267; terrain gate ~:7406; FindCameraCell CellVisibility.cs:389; grace :214; ComputeVisibilityFromRoot :356; EnvCellRenderer GL_BLEND :1004-1023; PortalVisibilityBuilder.Build BFS+OutsideView :1-239.

5. The directives (CRITICAL — from the user, this session)

  • NO intermediate user visual gates. Drive Stages 3→4→5 + triage to done. The per-stage "visual gates" in the plan are internal build+test-green checkpoints only.
  • Single final visual verification — cottage AND a dungeon (sealed, sky/rain through the door, no blue-hole, no transparent walls, no bleed). That is the ONLY time to bring the user in.
  • GREEN tests at verification — no broken/red tests when handed over. T0 made the suite deterministic: exactly 12 failures. None are Phase-W regressions (the membership fix only changed the returned cell id, not ResolveResult.Position). Of the 12:
    • 3 document-the-bug (DoorBugTrajectoryReplayTests ×2, DoorCollisionApparatusTests) — reconcile per their stage.
    • 9 pre-existing tech debt (red before Phase W, unrelated): MotionInterpreterTests.GetMaxSpeed ×3 (method doesn't branch on command), BSPStepUpTests B1/C3/D4 (step-up gaps), PlayerMovementControllerTests.Update_ForwardInput…, PositionManagerTests.ComputeOffset_BothActive…, DispatcherToMovementIntegrationTests.Dispatcher_W_held….
    • Triage-to-green pass before the final gate: fix the stale ones (e.g. GetMaxSpeed test if the no-branch behavior is intended), and for any genuine pre-existing gap that's out of Phase-W scope (step-up), transparently flag it to the user — do NOT silently [Skip] real-bug tests.
  • Process: superpowers:writing-plans (done) + superpowers:subagent-driven-development (each stage = a bounded subagent task + review). Sonnet implementers; Opus for load-bearing review.

6. Apparatus / how to reproduce

  • Launch (per CLAUDE.md "Running the client") with ACDREAM_PROBE_CELL=1 (decisive [cell-transit], low volume) and/or ACDREAM_PROBE_SWEPT=1 ([cell-swept] — swept cell per resolve, this session's diagnostic). Pipe via Tee-Object; the log is UTF-16 — read with PowerShell Get-Content or the ripgrep Grep tool, NOT GNU grep. Screenshots: PowerShell CopyFromScreen on AcDream.App.
  • Cottage cells: 0171 room (Z94), 0175 stairs (Z93), 0174 cellar (Z90), 0170 vestibule, 0031/0032 outdoor. ACE respawns at last logout (sometimes outside 0031, sometimes in 0171).
  • Offline membership verification (no walk): replay …/Fixtures/issue98/doorway-threshold-capture.jsonl (57 records, the 0170↔0031 seam) through ResolveWithTransition, chaining the cell — see DoorwayMembershipReplayTests. ACDREAM_CAPTURE_RESOLVE=<path> captures a fresh trajectory if needed.

7. Do-NOT-repeat / settled facts

  • Membership is fixed — the find_cell_list port (59f3a13). Don't re-investigate the strobe root; it's :1947 (the static re-derive), now replaced by the swept pick. Don't reintroduce a static ResolveCellId in the sweep.
  • A-vs-B settled: B, and it was small. No portal-crossing-detector invention ("A").
  • Render infra already exists (PView builder, clip frame, GL_BLEND fix, cell gate); the A8 stencil split is gone. Render = wire + 3 gaps (root, sky-inside-clip + Z-clear, entity clip). Don't rebuild the PView builder or rip out a stencil pipeline that isn't there.
  • The 12 test failures are NOT Phase-W regressions (position unchanged). Don't chase them as regressions; triage per §5.
  • Earlier-disproven (do not revisit): camera/eye as root, stencil-mask, flag-based per-entity gate routing, W2b's stab-list-prune-in-ResolveCellId.

8. Pickup prompt (copy-paste)

PHASE W (Unified Cell Graph) — continue on branch claude/thirsty-goldberg-51bb9b (do NOT
branch/worktree; do NOT push without asking). FIRST: git log --oneline -20 + git status to see
where Stage 3 landed; AND check the stash discrepancy in the handoff §0 (one of 2 stashes is
missing — check other worktrees before doing anything that gc's objects).

READ FIRST: docs/research/2026-06-02-phase-w-membership-fixed-render-handoff.md, then the design
docs/superpowers/specs/2026-06-02-phase-w-transition-membership-and-pview-render-design.md and the
render plan docs/superpowers/plans/2026-06-02-phase-w-render-rewrite.md.

STATE: M1.5 "indoor world feels right." Cell-membership root cause FIXED the retail way
(find_cell_list interior-wins pick + swept determination, static :1947 removed; commit 59f3a13;
offline-verified — doorway strobe → one clean transition). T0 made the suite deterministic (12
known failures, none Phase-W regressions). Stage 3 (render-root unification) was dispatched.

DO NEXT, subagent-driven (superpowers:subagent-driven-development), NO intermediate user gates:
1. Stage 3 (render-root unification) is DONE (`6a1fbbd`→`573c555`, build green, 6 tests). START at Stage 4.
2. Stage 4 — the seal: sky/landscape inside the portal-clip bracket + conditional doorway Z-clear
   (no blue-hole); verify ceilings/opaque walls. The big one.
3. Stage 5 — entity/particle cell-clip.
4. Green-tests triage (handoff §5): fix stale tests, reconcile document-the-bug, flag genuine
   pre-existing gaps; full suite GREEN.
5. THEN — and only then — the SINGLE final visual verification (user): cottage + a dungeon, sealed
   and seamless. Update roadmap + flip Phase W shipped.

EVIDENCE-FIRST for render/visual: launch with ACDREAM_PROBE_CELL/SWEPT, read the UTF-16 log with
PowerShell/ripgrep (not GNU grep). Membership is verifiable offline via the committed
doorway-threshold fixture (no walk). Render is wire+fill-gaps (infra exists); do not rebuild it.