diff --git a/CLAUDE.md b/CLAUDE.md index 1c3c2e9a..a41dc517 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -249,6 +249,19 @@ pursuing live in [`docs/architecture/code-structure.md`](docs/architecture/code- ## How to operate +**Memory — read the digests before domain work.** Durable project knowledge +lives in `claude-memory/` (the auto-loaded index is `MEMORY.md`). Before starting +work in a domain that has a **digest**, read it first: `project_render_pipeline_digest.md` +(indoor render / doorway FLAP) and `project_physics_collision_digest.md` +(physics / collision / #98 / membership). Each digest is current-truth-on-top +plus a DO-NOT-RETRY table — it supersedes the dated banners that used to sprawl +across this file. The memory-handling protocol (distill-don't-journal, the digest +pattern, tags, recall + capture) is in `reference_obsidian_vault.md`. **Do NOT add +new dated banners to this file — update the relevant digest instead.** Obsidian +(auto-started in the main repo via a SessionStart hook) is the live search / graph / +tag lens over `claude-memory/` through the `mcp__obsidian__*` tools when it's +running; the files read and write the same with it closed. + **You are the lead engineer AND architect on this project at all times.** You own the architecture (`docs/architecture/acdream-architecture.md`), the execution plan (milestones doc + strategic roadmap), the development @@ -725,389 +738,22 @@ Visual side-by-side passed: Holtburg town, inn interior, dungeon all render identically to pre-O. Spec: [`docs/superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md`](docs/superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md). -**2026-05-30 — RENDER PIPELINE PIVOT (read this first).** The two-pipe -(inside / outside) render approach is **ABANDONED**. acdream inherited a -WorldBuilder-style split — a normal outdoor draw plus a separate flat -`RenderInsideOut` stencil pass toggled on `cameraInsideBuilding` — and that -split is the root cause of every indoor seam bug (the flap, missing/transparent -walls, terrain bleeding into interiors). Retail has no such split; it renders -through one portal-visibility traversal (`PView`) and is seamless by -construction. We are building **Phase U — a single unified retail-faithful -render pipeline**. This supersedes the A8/A8.F two-pipe arc (issue #103). The -camera-collision work (retail `SmartBox::update_viewer` spring arm) + a -physics viewer-cap fix **SHIPPED this session and are kept** (they're real and -retail-faithful, just not the seam fix). Full decision + scope + next-session -pickup prompt: -[`docs/research/2026-05-30-unified-render-pipeline-decision-and-handoff.md`](docs/research/2026-05-30-unified-render-pipeline-decision-and-handoff.md). -The M1.5 narrative below is history retained for context. - -**2026-05-31 — U.4c doorway FLAP FIXED** (`0ee328a`, visual-verified "flap gone"). -Root cause (converged on a live `ACDREAM_PROBE_FLAP` capture, after disproving an -H2 `PortalSide` side-test fix and an H1 PVS-grounding hypothesis): indoor visibility -was rooted at the 3rd-person camera **eye**, which drifts out of the player's cell → -`FindCameraCell` returns a STALE cell for its grace frames → the doorway portal is -culled as behind-the-eye → exit cell + terrain + shells drop. Fix: root indoor -visibility (cell resolution + portal-side test) at the **player's cell** -(retail `CellManager::ChangePosition`; matches the existing lighting decision). Eye -still drives projection. **The flap is done; the indoor pipeline is NOT yet seamless** — -the visual gate revealed three SEPARATE residuals: (1) **#78** outdoor terrain not gated -inside (now more visible since terrain draws again); (2) **camera collision** needed (the -chase eye is outside the player's cell ~79% of frames → the eye-projected clip -over-includes → transparent outer walls); (3) **U.5** outside-looking-in (deferred). -Camera collision (retail `SmartBox::update_viewer` keeping the eye in the cell) is the -highest-leverage next step. CANONICAL handoff (read first next session): -[`docs/research/2026-05-31-u4c-flap-fixed-and-residuals-handoff.md`](docs/research/2026-05-31-u4c-flap-fixed-and-residuals-handoff.md). -Apparatus `ACDREAM_PROBE_FLAP` + `tools/A8CellAudit` are committed + ready. Do NOT retry -H1 (PVS grounding) or H2 (`PortalSide` side-test) — both evidence-disproven. - **Currently working toward: M1.5 — Indoor world feels right** (resumed from 2026-05-20 baseline after Phase O ship). -**2026-06-08 (evening) — FLAP ROOT CAUSE SETTLED BY LIVE-RETAIL MEASUREMENT; full retail render -port DECIDED (Option A). READ THIS FIRST — it supersedes EVERY flap banner below, including the -bounded-propagation/churn direction (REFUTED by measurement: `maxPop=1`, 0 churn).** We attached cdb -to the **live 2013 retail client** at the Holtburg doorway + read the decomp. Findings (measured, not -inferred): **retail has ONE render path — `DrawInside(viewer_cell)` every frame, NO inside/outside -branch** (`RenderNormalMode`'s outside branch is dead code; `is_player_outside` only gates -sky/lighting). "Entering a building" is NOT a render event — only the camera sweep resolving a -different `viewer_cell` (outdoor `CLandCell` → indoor `CEnvCell`); same path before/after the -threshold → no seam → no flap. **Retail's eye JITTERS ~36 µm at rest** (so a byte-stable eye is the -WRONG target — my render-position rest-snap fix `cd974b2` failed + regressed, reverted `9b1857a`); -retail's membership is stable anyway because it does **many small per-building floods** (~7/frame, -~2 cells each, via the terrain BSP → `DrawPortal` → `ConstructView(CBldPortal)`), not one giant -unified flood. **Our 3 divergences:** (D1) we invented an inside/outside branch -(`GameWindow.cs:7498`, `clipRoot = viewerRoot ?? _outdoorNode` :7396); (D2) a synthetic `_outdoorNode`; -(D3) one unified flood. **Decision (user-approved): Option A — rip out the branch + outdoor node, root -always at the real `viewer_cell`, one `DrawInside`, per-building rendering.** DO NOT retry: byte-stable -eye, bounded-propagation/churn, physics rest-jitter, viewer-cell dead-zone, two-pipe split (all -evidence-disproven). **CANONICAL PICKUP (exhaustive — read top-to-bottom before any code):** -[`docs/research/2026-06-08-full-retail-render-port-OPTION-A-handoff.md`](docs/research/2026-06-08-full-retail-render-port-OPTION-A-handoff.md). -Close its §8 open traces (viewer_sought_position write site; ClipPortals/AddViewToPortals; how -`DrawInside` handles an outdoor `CLandCell` root) BEFORE writing the implementation plan. +**Indoor render & the doorway "FLAP" — read the digest, not banners.** +The full current state, the root cause, the DO-NOT-RETRY list, the ⚠️ +`ACDREAM_PROBE_FLAP`→white-textures landmine, and the detail-doc pointers are +distilled in **`claude-memory/project_render_pipeline_digest.md`** (auto-loaded +via MEMORY.md). As of 2026-06-09 (HEAD `a1b12df`): governing direction is +**Option A — one `DrawInside(viewer_cell)`, NO inside/outside branch**; +R-A1/R-A2/R-A2b shipped (outside-looking-in flap GONE, seams GONE, portal-flood +churn KILLED); remaining = the visible indoor flap narrowed to §4 (edge-on +doorway grey + corner camera-seal). Render roots at the **VIEWER** cell, not the +player cell. Read the digest before any render/flap work — it supersedes the +dated render banners that used to live here. -**2026-06-05 (PM) — Indoor FLICKER + bluish VOID ROOT CAUSE CONFIRMED (decomp + live cdb); 3-part retail-faithful fix PLANNED (READ THIS FIRST).** -The "core inside render / cellar floor drops" framing below is **SUPERSEDED** by this session's diagnosis. -R1's per-cell `DrawInside` is already built and the cottage/cellar **seals** (user visual-verified). The -residual indoor **flicker (grey↔texture while standing still)** + **stable bluish void** are ONE root cause — -**visibility metastability at cell boundaries:** the 3rd-person camera **boom drifts at rest** -(`desiredBack 3.11→3.07`), walking the eye across a portal plane, and acdream re-resolves the **viewer cell** -fresh each frame with **no hysteresis** → it flips `0170↔0171` → the render (rooted at the viewer cell) -redraws two solves → flicker; and the 2D project-then-clip **degenerates** at close portals (`proj=0`) → grey -void. **Live cdb on retail CONFIRMS** retail's `viewer_cell` is rock-stable here (clean monotonic transitions, -ZERO oscillation across 4,916 samples) — retail holds the boom + uses a **0.2 mm cell dead-zone** + clips -portals **in clip-space**. **FIX (3 parts, retail-faithful, planned):** (1) camera boom stability -[`RetailChaseCamera`; `UpdateCamera` 0x456660] → kills the flicker trigger; (2) viewer-cell ±0.2 mm dead-zone -[`PhysicsCameraCollisionProbe.SweepEye`; `point_inside_cell_bsp` 0x53c1f0]; (3) w-space (w=0) portal clip -[`PortalProjection`/`PortalVisibilityBuilder`; `GetClip` 0x5a4320 / `polyClipFinish` 0x6b6d00]. Two partial -fixes committed: `5f596f2` (NDC side-plane clip — KEEP), `9f95252` (eye-in-portal flood — reassess/revert). -Baseline App 183p / Core 1326p-4f-1s. **CANONICAL PICKUP:** -[`docs/research/2026-06-05-viewer-cell-flicker-rootcause-and-fix-plan-handoff.md`](docs/research/2026-06-05-viewer-cell-flicker-rootcause-and-fix-plan-handoff.md). - -**2026-06-05 — Render Residual A (camera collision) SHIPPED + user-kept; next = the CORE INSIDE RENDER (R1 completion) (SUPERSEDED 2026-06-05 PM — see the banner above; the cellar IS sealed, the real bug is the boundary flicker/void).** -Residual A = a verbatim port of retail `SmartBox::update_viewer` (pc:92761): the indoor sweep's start -cell is seated at the head-PIVOT via `AdjustPosition` (pc:280009) → `find_visible_child_cell` (pc:311397), -plus the two fallbacks + cellId==0 snap-to-player. Commits `0ffc3f5` (spec) / `5177b54` (Core primitives) -/ `9e70031` (`ResolveResult.Ok` + `SweepEye` orchestration); TDD, 11 new tests, no regression (Core -1326p/4f/1s, App 179p). **Key finding (live capture):** A's V1 sweep ALREADY contained the eye -(`eyeInRoot=Y` 99.75%, `viewerCell` never 0) — so A is a faithfulness completion, not the fix for the -dominant bluish void. **What A EXPOSED (the bridge to the next phase):** the render roots at the VIEWER -cell (`clipRoot = visibility.CameraCell`, GameWindow.cs:7322, Phase W V1 "one viewpoint"); A made that -cell ACCURATE (the eye's real cell), so when the player is in the cellar but the eye is up in the room, -`clipRoot = room` and the PVS flood from the room does NOT reach the cellar → the cellar floor drops. -The user chose **"Keep it"** (the faithful viewpoint) over revert. **NEXT = the CORE inside render (R1 -completion)** — make `DrawInside` flood + seal correctly from the viewer cell so the cottage/cellar -interior is SEALED: no bluish void, no see-through-to-other-buildings BLEED, cellar floor draws. This -(NOT the handoff-era "C / outside-looking-in", which is R2, a later phase) is what fixes the visible -problems; the locked design already exists. **CANONICAL PICKUP:** -[`docs/research/2026-06-05-render-residual-a-shipped-core-inside-render-handoff.md`](docs/research/2026-06-05-render-residual-a-shipped-core-inside-render-handoff.md) -(+ the LOCKED render design [`docs/superpowers/specs/2026-06-02-render-pipeline-redesign-design.md`](docs/superpowers/specs/2026-06-02-render-pipeline-redesign-design.md)). - -**2026-06-03 — P1 membership DONE + P2 active (history; cellar-lip now FIXED per the 2026-06-05 banner above).** The verbatim spatial-pipeline -port (master plan -[`docs/superpowers/specs/2026-06-03-verbatim-spatial-pipeline-port-master-plan.md`](docs/superpowers/specs/2026-06-03-verbatim-spatial-pipeline-port-master-plan.md)) -is the active effort. **P1 (membership) = DONE** — proven to ALREADY match retail; the believed -doorway "0/11 lag" was a cdb CAPTURE ARTIFACT (`change_cell` logs `m_position` before `set_frame` -writes it). Aligned re-capture → production gate 9/9 with NO code change; live `[cell-transit]` clean. -Merged + pushed to both remotes (HEAD `f0d37d8`). **P2 (door/building-shell collision) = IN PROGRESS, -root cause LOCALIZED** to **BSP Path 5 grounded step-up** — the Path 5 wrappers (`DoStepUp`=retail -`step_up`, `DoStepDown`=retail `step_down`) are verified faithful; the divergence is in the step-up -CLIMB (`find_walkable`/`step_sphere_down` up-adjust). The 5 failing Core tests are P2's target. The -visible doorway seam has moved from physics into the RENDER path (P3 camera-collision + P4 PView seal). -The (a)–(d) membership cleanups (remove `ResolveCellId`, unify `find_env_collisions`, intrinsic -building stabs, per-cell collision graph) are **approval-gated refactors of WORKING code**. **CANONICAL -PICKUP:** -[`docs/research/2026-06-03-p2-door-stepup-handoff.md`](docs/research/2026-06-03-p2-door-stepup-handoff.md) -(+ the P1 RESOLVED banner in -[`docs/research/2026-06-03-p1-membership-swept-advance-handoff.md`](docs/research/2026-06-03-p1-membership-swept-advance-handoff.md) -and the visual-gate/render-residuals note -[`docs/research/2026-06-03-p1-visual-gate-render-residuals.md`](docs/research/2026-06-03-p1-visual-gate-render-residuals.md)). - -**A6.P1 + A6.P2 + A6.P3 -slice 1 SHIPPED 2026-05-21.** **A6.P3 slice 2 v2 SHIPPED 2026-05-22** -(commit `f8d669b`): tried removing the L622 per-tick CP seed -(`892019b` v1) but it broke BSP step_up at the last step of stairs; -reverted + added a benign no-op-if-unchanged guard inside -`CollisionInfo.SetContactPlane`. Slice 2 outcome: **#96 partially -addressed — accepted as documented retail divergence** (the per-tick -seed is load-bearing for `AdjustOffset` slope-projection on sub-step 1 -which BSP step_up depends on; matching retail would require deeper -refactor of AdjustOffset). Slice 2 verification surfaced a NEW -M1.5-blocking bug: **user cannot walk UP out of cottage cellar — stuck -at last step due to cell-resolver ping-pong (filed as issue #98, -Finding 3 family).** **A6.P3 slice 3 SHIPPED 2026-05-22** (commits `8898166` v1 + -`3e140cf` v2): cell-resolver stickiness added in `ResolveCellId`'s -indoor branch (point-in check against `fallbackCellId`'s CellBSP -before falling through to FindCellList). Data confirms ping-pong is -FULLY CLOSED — scen4 cellar capture shows 1 cell-transit (login -teleport) vs 20+ pre-fix. **#90 workaround now redundant — deferred -to A6.P4 removal. #98 APPARATUS COMPLETE 2026-05-23 evening** -(commits `35b37df` triage → `f62a873` cell-dump probe → `3f56915` -fixtures → `856aa78` replay harness → `6f666c1` cdb script → -`28c282a` divergence comparison doc). Four sessions of speculative -fixes (10+ variants) shipped the wrong diagnosis each time; this -session shipped the APPARATUS that turns evidence-driven analysis -into a 200ms test loop. Real divergence: retail's sphere is at -world Z ≈ 94.48 (resting on cottage floor) when find_walkable -accepts; acdream's failing-frame sphere is at world Z ≈ 92.01 -(2.47m lower). Retail's ContactPlane writes during cellar-up are -ONLY flat floors (cellar floor or cottage floor), never the ramp. -Retail's find_crossed_edge fires once in 35K BPs; ours uses it -heavily. **Fix targets (priority): (1) Transition.AdjustOffset -slope projection / DoStepUp WalkInterp handling — ramp climb -doesn't gain Z; (2) cottage-cell candidacy using wrong sphere -reference; (3) find_crossed_edge over-use; (4) ramp polygon normal -divergence (low confidence).** Full divergence reading + -fix-plan pickup prompt at -[`docs/research/2026-05-23-a6-p3-issue98-replay-comparison.md`](docs/research/2026-05-23-a6-p3-issue98-replay-comparison.md). -Current A6 phase: -**A6.P3 — PAUSED 2026-05-23 (full day). Trajectory replay harness shipped -but BLOCKED on a new bug surfaced during commissioning.** Read -[`docs/research/2026-05-23-a6-p3-issue98-harness-handoff.md`](docs/research/2026-05-23-a6-p3-issue98-harness-handoff.md) -as the canonical pickup document — it has the chronological commit list, -the apparatus inventory, the exclusion list (do-not-retry), and three -concrete next-session options ranked by recommendation. - -The session shipped further apparatus + first failed fix attempt + revert: -`8a232a3` (`[step-walk-adjust]` probe inside `Transition.AdjustOffset` -revealing branch tokens and per-call zGain), `8daf7e7` (findings note -at [`docs/research/2026-05-23-a6-stepwalkadjust-findings.md`](docs/research/2026-05-23-a6-stepwalkadjust-findings.md) -+ capture snapshot), `0cb4c59` (Shape 1 fix: gate `BSPQuery.AdjustSphereToPlane`'s -two `SetContactPlane` call sites by `Normal.Z >= 0.99`), `402ec10` -(revert — Shape 1 broke OnWalkable tracking, sphere went into falling -state on every sloped surface). **Refined diagnosis:** AdjustOffset is -CORRECT (145/146 calls take `into-plane` branch, +0.045 m mean zGain -per call when offset points into ramp); the climb CAPS at world Z ≈ -92.80 because step-up's downward step-down probe finds no walkable -within 0.6 m below the proposed position (cottage floor is ABOVE). -Earlier "Fix targets 1–4" priority list is OBSOLETE — AdjustOffset -projection is not the problem. The actual bug is in the step-up -validation at the ramp top. **Honest next-session moves**: (1) build -deterministic trajectory replay harness so fix attempts iterate in -<500ms instead of 5-minute live-test cycles; (2) pivot to a less- -coupled M1.5 issue while #98 awaits the harness; (3) targeted decomp -research on `CEnvCell::find_env_collisions` → `BSPTREE::find_collisions` -indoor CP-setting chain (prior research worked on the outdoor -`CLandCell` path; indoor was never fully traced). Session-end ISSUES.md -entry has the full reading and pickup prompt. **NO further #98 fix -attempts until apparatus or research has converged — six+ failed -attempts in the saga is the signal.** - -**Late-day extension (2026-05-23 PM):** trajectory replay harness shipped -(commits `4c9290c` → `5c6bdbe`). Mechanics work — runs 200 ticks in <100 ms. -Five tests pass. NEW finding: the cellar ramp polygon is in a GfxObj -(static building piece), not the cell's PhysicsPolygons. Harness now -includes `RegisterStairRampGfxObj` for synthetic stair construction -and `AttachSyntheticBsp` to wrap hydrated cells (which have BSP=null) -with a one-leaf BSP that exposes the indoor BSP collision path. -**NEW BLOCKER:** even with full apparatus, sphere goes airborne at -tick 1 with `hit=(0,1,0)` (a +Y wall normal matching no registered -geometry). 6 hypotheses tested via the harness, none isolated root cause. -Per systematic-debugging skill's "question architecture" rule, stop and -reflect. Next session: build a side-by-side comparison harness that -captures live PlayerMovementController state and diffs against the -test harness — evidence-first instead of speculation-first. -Findings doc: -[`docs/research/2026-05-21-a6-cdb-capture-findings.md`](docs/research/2026-05-21-a6-cdb-capture-findings.md). - -**Evening extension v2 (2026-05-23 PM late) — apparatus shipped + root -cause identified.** Four commits (`fb5fba6` → `44614ab` → `0f2db62` → -`f29c9d5`). The side-by-side comparison harness was built and exercised: -- `PhysicsResolveCapture` ships a JSON Lines writer for every player-side - `ResolveWithTransition` call. Off by default; turn on via - `ACDREAM_CAPTURE_RESOLVE=`. Filtered to `IsPlayer` so NPC / remote - DR doesn't pollute. -- Two live captures from a cottage-cellar session (41K + 70K records). -- Three `LiveCompare_*` tests load 3 representative records (spawn, - on-ramp, first-cap). Spawn + on-ramp PASS bit-perfect; the first-cap - test originally FAILED with a clear divergence — and that divergence - pinpoints the root cause. -- **The cap is caused by `obj=0xA9B47900` — a landblock-baked cottage - GfxObj.** Cottage floor polygons live in this GfxObj's polygon table - (registered as a ShadowEntry), NOT in any cottage cell. The harness's - cell fixtures (0xA9B40143/146/147) don't include the cottage GfxObj, - so the harness fails to reproduce the live cn=(0,0,-1) cap. -- User's confirming observation: jumping in the cellar caps at the same - Z — purely vertical motion. This rules out every step-up / AdjustOffset - hypothesis from the prior 6-shape saga. The bug is the head sphere - hitting the cottage floor at Z=94.0 from below (math: foot Z=92.74 - + sphereHeight 1.20 = head center 93.94, head top 94.42, intersects - cottage floor Z=94.0). -- The first-cap test is now in documents-the-bug form (PASSES while - bug exists; FAILS when fix lands). Test baseline maintained at - 1178 + 8 (serial run). -- 13 new cell fixtures cover the full 0xA9B4014X neighborhood (272 KB). -Findings doc (canonical pickup): -[`docs/research/2026-05-23-a6-p3-issue98-comparison-harness-findings.md`](docs/research/2026-05-23-a6-p3-issue98-comparison-harness-findings.md). - -**Evening v2 follow-on — apparatus convergence SHIPPED 2026-05-23 PM.** -Two commits (`cc3afbc` → `97fec19`): -- `cc3afbc` adds the GfxObj dump infrastructure (`ACDREAM_DUMP_GFXOBJS`) - mirroring the existing `ACDREAM_DUMP_CELLS` pattern, with new - `GfxObjDump`/`GfxObjDumpSerializer` parallel to `CellDump`. The new - env var triggers `PhysicsDataCache.CacheGfxObj` to write the full - resolved polygon table as JSON when a listed id caches. Closes the - gap that the existing `[resolve-bldg]` probe couldn't fill (the BSP - wire site that populates `LastBspHitPoly` was never wired, so the - probe only emitted GfxObj-level metadata, not per-poly geometry). -- `97fec19` lands the cottage GfxObj fixture (`0x01000A2B`, 74 polygons, - BSP radius 13.989m matching live), the new `RegisterCottageGfxObj` - harness helper, and a minimum-stub landblock so - `TryGetLandblockContext` succeeds at the cellar XY. Harness now - reproduces the live `cn=(0,0,-1)` cap bit-perfect. The full per-field - round-trip uncovers ONE residual: live preserves +0.0266m of +X - motion through the cap (edge-slide along the cottage floor); harness - blocks all motion. Captured in - `LiveCompare_FirstCap_ResidualXMotionDivergence_DocumentsNextInvestigation` - in documents-the-bug form. -- All 21 issue-#98-relevant tests (12 harness + 4 GfxObjDumpRoundTrip + - 1 new PhysicsDiagnosticsTests + 4 CellDumpRoundTripTests) pass - deterministically in isolation. -- Pre-existing test suite flakiness observed (8–19 failures across runs - of the same code, from PhysicsResolveCapture / PhysicsDiagnostics - statics leaking between test classes). INDEPENDENT of A6.P3 — verified - by stashing the cottage helper and reproducing the same flaky range. - Out of scope for this session; tracked as follow-up. - -**Evening v3 finding (2026-05-23 PM, even later) — NEW root-cause -hypothesis identified:** the cottage-floor cap is a SYMPTOM. The actual -bug is **stale ramp contact plane causing per-tick Z drift** that makes -the cap reachable in the first place. - -Evidence: -- Body's contact plane at cap = ramp's plane (n=(0, 0.7190, 0.6950), - d=-69.5035) from the live capture's `bodyBefore` -- Cellar ramp's actual world XY: X∈[129.7, 131.3], Y∈[10.19, 13.09] - (computed from the cellar cell fixture's vertex data + WorldTransform) -- Player position at cap: world (141.5, 7.22, 92.74) — **10 m away** - from the ramp in cell-local X -- `AdjustOffset` projects requested motion along the contact-plane - perpendicular. Math: dot((0.0266, -0.4022, 0), (0, 0.719, 0.695)) - = -0.2892 → projected = (0.0266, -0.1943, +0.2010). **+0.201 m of - Z gain per tick**, applied because the engine believes the player - is on the slope. -- Head sphere top at cap = foot Z + 1.68 = 94.42. Cottage floor at - Z=94.00. **Head sphere exceeds cottage floor by 0.42 m** → cap fires -- If the contact plane refreshed to the flat cellar floor when the - player walked off the ramp, AdjustOffset would produce zero Z gain - (no Z component in requested motion + horizontal-plane perpendicular). - No drift, no cap. - -How this question surfaced: user asked "we know how retail OPENs it -from above, how hard can it be to know how to open it from below?" — -that reframing made the question "what's different about our state -when walking up vs down?" The answer: **nothing, actually — the -cottage geometry is the same. But our contact plane is wrong.** The -six prior fix attempts were all investigating the cap-event mechanics -(step-up, slope projection at the cap, edge-slide, SidesType, +X -residual). None questioned why the contact plane was the ramp at all -when the player was 10 m from the ramp. - -**Next-session move:** verify the stale-contact-plane hypothesis -chronologically against the live capture (walk the JSONL records, find -the last tick the player was on the actual ramp, quantify Z drift), -then locate the walkable-refresh code path in -`Transition.FindEnvCollisions` / `SpherePath.SetWalkable` that's -supposed to detect a new walkable polygon under the sphere and -overwrite the contact plane. Retail decomp anchor: -`CObjCell::find_env_collisions`. Full pickup prompt at the bottom of -[`docs/research/2026-05-23-a6-p3-issue98-comparison-harness-findings.md`](docs/research/2026-05-23-a6-p3-issue98-comparison-harness-findings.md). - -**A6.P4 door bug — `pos_hits_sphere` near-miss recording shipped -2026-05-25 PM** (commit `3253d84`). Single-line ordering fix in -`BSPQuery.PosHitsSphere`: `if (hit) hitPoly = poly;` now precedes the -front-face cull, matching retail's `CPolygon::pos_hits_sphere` at -`acclient_2013_pseudo_c.txt:322974-322993` where `*arg5 = this` fires -on static-overlap BEFORE `dot(N, movement) >= 0 → return 0`. With this -ordering, Path 5's existing `if (hitPoly0 is not null)` near-miss -branch (`BSPQuery.cs:1869`) finally fires — `NegPolyHitDispatch` -sets `path.NegPolyHit`, the outer `transitional_insert` loop dispatches -via `slide_sphere`, and the sphere slides along walls it's touching -instead of squeezing through. The handoff hypothesized swept-sphere + -closest-considered-polygon tracking; reading retail showed both -`pos_hits_sphere` and `polygon_hits_sphere_slow_but_sure` are STATIC -tests using motion only for the cull — the fix is just the ordering. -3 new RED→GREEN unit tests in `BSPQueryTests.FindCollisions_Path5_*` -cover: overlap + parallel motion (RED→GREEN), overlap + away motion -(RED→GREEN), overlap + into motion (regression guard, already passed). -Zero regressions in full Core suite — with-fix failure set is a strict -subset of baseline (14 vs 17, the 14 are pre-existing static-leak -flakiness + 2 stale-capture document-the-bug tests). Issue #98 -`LiveCompare_FirstCap_FixClosesCottageFloorCap` regression test -passes. **Needs visual verification at Holtburg cottage door inside- -out off-center ~50 cm scenario** before A6.P4 is marked complete — -sphere should block at the door surface with no squeeze-through. The -"runs a bit into the door" over-penetration symptom is hypothesized -to close together with the squeeze-through (continuous near-miss -recording while approaching a wall means the sphere slides along it -substep-by-substep rather than catastrophically penetrating then -recovering), but separate investigation if the symptom persists. -Original demo scenario (Holtburg Sewer end-to-end) is unreachable: sewer -doesn't exist on this server, and **issue #95** (portal-graph visibility -blowup) blocks any substitute dungeon. Revised M1.5 demo split into -building/cellar half (PARTIALLY ACHIEVABLE post-slice-1; cellar-ascent -blocked on #98) + dungeon half (blocked on #95). Issues in scope: #80, -#81, #83, #88, #90 (workaround removal after slice 3), **#95** -(visibility; not A6 scope), **#96** (L622 seed; retail divergence -accepted), **#97** (phantom collisions; may close as #98 side-effect), -**#98** (cellar-ascent stuck; A6.P3 slice 3 target), L-indoor, -L-spotlight, indoor sling-out (Finding 3 family with #98), and the -`TryFindIndoorWalkablePlane` definition deletion (A6.P4). **M2 -("Kill a drudge") is deferred until M1.5 lands.** Full M1.5 writeup at -the corresponding block in `docs/plans/2026-05-12-milestones.md`. - -**A6.P8 — Mesh-AABB-fallback phantom suppression for GfxObj-only stabs — SHIPPED 2026-05-25.** -Three commits: `f6305b1` (PhysicsDataCache.IsPhantomGfxObjSource + 3 unit tests), -`5240d65` (GameWindow.cs wire-in at line 6127), `6ca872f` (test-class doc -line-ref sync from code review). Issue #101 CLOSED — the 10 phantom stair -cyls on the Holtburg upper-floor cottage staircase are gone; collision -falls through to entity `0x40B50089` (GfxObj `0x01000C16`, `hasPhys=True` -BSP with walkable inclined polygon at `Normal.Z=0.717`, world ramp from -(111.10, 25.50, 94.00)→(107.50, 27.10, 97.50)). Visual-verified end-to-end -2026-05-25: holding W continuously climbs Z=94→97.5 over the full 45° -ramp; no phantom diagonal slides (`[cyl-test]` count on `obj=0x40B500*` -post-fix = 0 vs 7101 pre-fix). Spec: -[`docs/superpowers/plans/2026-05-25-issue-101-stairs-cyl-phantom.md`](docs/superpowers/plans/2026-05-25-issue-101-stairs-cyl-phantom.md). - -**Issue #100 — Transparent ground around buildings — SHIPPED 2026-05-25 (primary acceptance); -visibility-culling follow-up handed off.** Three commits: `f48c74a` (terrain shader Z nudge, -retail `zFightTerrainAdjust = 0.00999999978` applied per-vertex in `terrain_modern.vert`), -`a64e6f2` (removed ~50 LOC of `hiddenTerrainCells` / `BuildingTerrainCells` plumbing across -LandblockMesh / LoadedLandblock / LandblockLoader / GameWindow / GpuWorldState / -LandblockStreamer + 2 dead tests), `84e3b72` (docs SHA stabilization follow-up). -Visual-verified 2026-05-25 PM at Holtburg: 24m × 24m transparent rectangles around -every cottage are GONE; ground reads as continuous cobblestone / grass. Plan: -[`docs/superpowers/plans/2026-05-25-issue-100-terrain-cutout.md`](docs/superpowers/plans/2026-05-25-issue-100-terrain-cutout.md); -predecessor research [`docs/research/2026-05-25-issue-100-terrain-cutout-handoff.md`](docs/research/2026-05-25-issue-100-terrain-cutout-handoff.md). -**Secondary finding from visual verification:** outdoor terrain mesh visible inside -cottage cellars at certain camera angles (clears when camera moves closer; gameplay -unaffected). High-confidence root cause: **indoor-cell visibility culling not gating -outdoor terrain** — same family as filed issue #78 (outdoor stabs visible through inn -floor) and #95 (dungeon portal-graph blowup). Per user direction, NOT filed as a new -issue; treated as additional evidence for #78. Next session investigates + ports -retail's `CEnvCell::find_visible_child_cell` (decomp anchor -`acclient_2013_pseudo_c.txt:311397`) and/or WB's `RenderInsideOut` stencil pipeline. -Full handoff with pickup prompt: -[`docs/research/2026-05-25-issue-100-shipped-and-culling-handoff.md`](docs/research/2026-05-25-issue-100-shipped-and-culling-handoff.md). +**Physics / collision / cell-membership — read the digest, not banners.** The #98 cellar-ascent saga, the A6.P* phase ledger, the door/step-up (P2) work, the phantom-collision fixes (#100/#101), P1 membership, the apparatus inventory, and the full 18-entry DO-NOT-RETRY list are distilled in **`claude-memory/project_physics_collision_digest.md`** (auto-loaded via MEMORY.md). Current state (2026-06-09): #98 CLOSED via the `b3ce505` stopgap (a WORKAROUND — it introduced #99 door run-through, OPEN HIGH); P2 cellar-lip FIXED (`cc4590f`, visual-gated); P1 membership matches retail (no port needed); #100/#101 CLOSED. The open debt is the per-cell shadow architecture (A6.P4) that closes #99. Read the digest before any collision/physics/membership work — it supersedes the dated A6 banners that used to live here. **Today's pre-M1.5 baseline (2026-05-20).** Five surgical fixes shipped to close the user-reported "logged in inside the inn, ran