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

174 lines
13 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 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)
DONE** — `6a1fbbd``573c555`: 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) |
| `2acd8f9``d23d1f4` | 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.
```