docs: A6.P3 #98 resolution + A6.P4 design + #99/#100 filed

Knowledge-preservation pass after the issue #98 cellar-up fix shipped
(`b3ce505`). Closes the saga's documentation loop and plans the next
phase.

Changes:
  - docs/research/2026-05-23-a6-p3-issue98-comparison-harness-findings.md
    Appended "Resolution 2026-05-24" section: v3 hypothesis falsified,
    actual mechanism (head-bump cottage GfxObj floor poly from below)
    confirmed, b3ce505 fix shipped, known door regression flagged.
    Memory artifacts cross-referenced.
  - docs/ISSUES.md
    #98 moved to DONE with full resolution writeup + decomp anchors.
    #99 filed: door regression at building thresholds (caused by
    b3ce505's indoor-primary gate). Closes via A6.P4.
    #100 filed: transparent rectangular patches around houses
    (terrain rendering). Bisect found commit 35b37df introduced the
    hiddenTerrainCells mechanism that collapses 24m outdoor cells
    when buildings sit in them; cottage building only fills part of
    its cell so the rest of the 24m cell shows the sky-bleeding gap.
    Three fix-path options documented.
  - docs/superpowers/specs/2026-05-24-phase-a6-p4-retail-shadow-architecture.md
    Full A6.P4 design doc. Three-slice plan: (1) query-side portal
    expansion to close #99 while preserving #98 fix, (2) port retail's
    BuildShadowCellSet at registration time so per-cell semantics match
    `CObjCell::find_cell_list`, (3) remove b3ce505 stopgap entirely.
    Decomp anchors, file-by-file plan, risk inventory, open questions.

Memory entries written separately (out-of-tree at
~/.claude/projects/.../memory/):
  - feedback_retail_per_cell_shadow_list.md
    The architectural lesson: retail uses per-cell shadow_object_list
    with portal-aware registration; our landblock-wide spatial
    registry diverges at indoor/outdoor seams.
  - feedback_apparatus_for_physics_bugs.md
    The apparatus-first pattern that cracked the saga: live capture +
    fixture dump + replay harness. Template for future physics bugs.
    Quote rule: "when a physics bug is resisting and you catch
    yourself about to ship 'fix attempt N+1 with no new evidence,'
    STOP. Build the apparatus first."
  - MEMORY.md index updated with both new entries.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-24 07:23:49 +02:00
parent b3ce505ca8
commit b55ae831bd
3 changed files with 487 additions and 1 deletions

View file

@ -664,7 +664,159 @@ Falsifiable: if #96 fix closes #97 as a side-effect, the hypothesis is confirmed
---
## #98 — Cellar ascent stuck at top (BSP step physics; NOT cell-resolver)
## #98 — [DONE 2026-05-24 · `b3ce505`] Cellar ascent stuck at top (NOT BSP step; per-cell-list architectural divergence)
**Closed:** 2026-05-24
**Commit:** `b3ce505 fix(phys): A6.P3 #98 — gate outdoor shadow radial sweep on indoor primary cell`
**Resolution:** The proximate fix is the indoor-primary radial-sweep
gate in `ShadowObjectRegistry.GetNearbyObjects`. Architectural root
cause: our landblock-wide spatial shadow registry diverges from
retail's per-cell `shadow_object_list` with portal-aware registration —
the cottage GfxObj (registered landblock-wide via cellScope=0) was
returned to sphere queries inside the cellar EnvCell, and its
downward-facing floor poly at world Z=94 head-bumped the climbing
sphere from below.
After ~10 failed speculative fix attempts across four sessions, the
fix landed cleanly once the apparatus converged. The "v3 stale ramp
contact plane" hypothesis was falsified by chronological replay against
`a6-issue98-resolve-capture-2.jsonl` — the player IS on the ramp at the
cap event; the contact plane is correctly the ramp's plane; the head
sphere bumps the cottage GfxObj's floor poly from below (the
evening-v2 finding was correct all along).
Decomp anchors (`docs/research/named-retail/acclient_2013_pseudo_c.txt`):
- 308742+ : `CObjCell::find_cell_list` — indoor/outdoor branch
- 308751-308769 : the branch — indoor adds 1 cell; outdoor calls `add_all_outside_cells`
- 308773-308825 : portal-visible neighbor recursion
- 308916 : `CObjCell::find_obj_collisions(this, ...)` — strict per-cell iteration
**Visual verification 2026-05-24:** user confirmed "Finally I can go up!"
**Knowledge artifacts:**
- Findings doc resolution section: [`docs/research/2026-05-23-a6-p3-issue98-comparison-harness-findings.md`](research/2026-05-23-a6-p3-issue98-comparison-harness-findings.md) (bottom)
- Memory: `feedback_retail_per_cell_shadow_list.md`, `feedback_apparatus_for_physics_bugs.md`
- A6.P4 phase planned to do the full retail-faithful per-cell port and obviate the b3ce505 stopgap
**Known regression introduced:** doors at doorway thresholds — see #99 below.
---
## #99 — Run-through doors at building thresholds (regression from b3ce505)
**Status:** OPEN
**Severity:** HIGH (M1 demo regression — opening doors was previously a working demo target)
**Filed:** 2026-05-24
**Component:** physics, shadow-object collision query
**Description:** With the issue #98 fix (commit `b3ce505`), the
indoor-primary radial-sweep gate causes our engine to miss outdoor-
registered door entities when a sphere has crossed the threshold and
the primary cell resolves to the indoor side. Players can walk through
doors that previously blocked them.
User report 2026-05-24: "I can also run through doors."
**Root cause / status:** This is the doorway edge case explicitly
flagged in the b3ce505 commit message. Doors are server-spawned
entities with their own cylinder collision, registered via
`UpdatePosition` to whichever cell their position resolves to. Doors
at building thresholds typically resolve to **outdoor** cells. The
b3ce505 gate skips the outdoor radial sweep when the sphere's primary
cell is indoor → outdoor-registered doors are not returned → no
collision → walk-through.
Retail handles this case via the portal-visible recursion in
`find_cell_list` (lines 308773-308825 of the named-retail decomp): at
registration time, an object is added to its position's cell PLUS all
portal-visible neighbor cells. So a door at a doorway portal ends up in
both the outdoor cell's shadow list AND the indoor cell's list — a
sphere on either side sees it.
**Fix path:** Closes naturally as part of A6.P4 (per-cell shadow
architecture refactor — see design spec at
`docs/superpowers/specs/2026-05-24-phase-a6-p4-retail-shadow-architecture.md`).
A6.P4 ports retail's `find_cell_list` indoor branch + portal recursion
into `ShadowObjectRegistry.Register`, eliminates the cellScope=0
landblock-wide approximation, and removes the b3ce505 stopgap.
If A6.P4 takes longer than expected, an intermediate "portal-aware
indoor query" patch (~20 lines: walk indoor cells' `VisibleCellIds`,
collect portal-reachable outdoor cells, include in `GetNearbyObjects`
indoor branch) would close #99 without touching registration. Tagged
as fallback option B in the A6.P4 spec.
**Files:**
- `src/AcDream.Core/Physics/ShadowObjectRegistry.cs``GetNearbyObjects` indoor branch
- `src/AcDream.Core/Physics/TransitionTypes.cs:2180+``FindObjCollisions` caller
**Acceptance:** Doors at Holtburg cottage/inn doorways block the player
from both sides (outside walking in, inside walking out). Issue #98's
cellar-up fix remains intact.
**Related:** #98 (sibling — same architectural cause), #97 (phantom
collisions on 2nd floor — also likely closed by A6.P4), Finding 3
family (sling-out — also likely).
---
## #100 — Transparent rectangular patches around every house (terrain rendering)
**Status:** OPEN
**Severity:** MEDIUM (visual regression; affects every Holtburg house)
**Filed:** 2026-05-24
**Component:** rendering, terrain
**Description:** Standing outside any Holtburg house, the ground in a
rectangular footprint around the building appears as a flat dark patch
instead of cobblestone / grass terrain. Visible as a sharp-edged
rectangle the size of the house's outdoor footprint. Same shape on
every house observed.
User report 2026-05-24 (with screenshot): "around every house now I
missing the ground texture, it is transparent. I can see through the
ground."
**Root cause / status:** **Bisect 2026-05-24 — commit `35b37df`** is the introducer (the only commit on this worktree branch that touches `src/AcDream.Core/Terrain/`). It added a `hiddenTerrainCells` parameter to `LandblockMesh.Build` that collapses terrain triangles owned by buildings to zero-area degenerates, intended so the building's own ground-level mesh visually fills the gap (avoids Z-fighting between terrain and building floor).
The hide mechanism works at **outdoor-cell granularity** — 24 m × 24 m cells indexed by `cy * 8 + cx` from `LandBlockInfo.Buildings`. A cottage building only fills ~half of one outdoor cell (cottage footprint ~12 m × 12 m vs cell 24 m × 24 m), so the entire 24 × 24 cell terrain gets hidden but the cottage geometry only covers a smaller area inside it. The visible result: a dark rectangle (sky / framebuffer clear bleeding through) around every house where terrain was hidden but no building mesh fills the gap.
Confirmed in [`src/AcDream.Core/Terrain/LandblockMesh.cs:178`](src/AcDream.Core/Terrain/LandblockMesh.cs:178):
```csharp
if (hiddenTerrainCells is not null && hiddenTerrainCells.Contains(cellIdx))
{
indices[i] = (uint)(cellIdx * VerticesPerCell); // collapse to vertex 0 → degenerate
continue;
}
```
The cells flagged hidden come from `LandblockLoader.BuildBuildingTerrainCells` (also kept by 35b37df), which reads `LandBlockInfo.Buildings` and emits one cellIdx per listed building's `cy*8+cx`.
The b3ce505 issue-#98 fix did NOT cause or interact with this — it only touched physics collision code.
**Fix paths** (need design decision):
1. **Polygon-level terrain occlusion** instead of cell-level. Build per-poly cutouts from each building's ground-footprint convex hull / bounding box, modify the terrain mesh to actually have a hole the building exactly fits. Retail-faithful for this case but a real engineering change to `LandblockMesh.Build`.
2. **Drop the hiddenTerrainCells mechanism entirely** and accept Z-fighting on the building floor vs terrain seam (or solve Z-fighting via a tiny render-only Z lift on the building floor mesh, the same trick we already use for env cell floors at line 5363 `+ new Vector3(0f, 0f, 0.02f)`).
3. **Render the building's "yard" mesh** if buildings have such a thing in retail. (Need to check — Holtburg cottages may have stone foundation polys around them that retail renders.)
Option 2 is the smallest change; option 1 is the most faithful. Option 3 needs retail visual research.
**Files:**
- `src/AcDream.Core/Terrain/LandblockMesh.cs:178` — the collapse code
- `src/AcDream.Core/World/LandblockLoader.cs``BuildBuildingTerrainCells`
- `src/AcDream.App/Rendering/GameWindow.cs:1808, 5366, 8761` — sites calling `LandblockMesh.Build` with `hiddenTerrainCells`
**Acceptance:** Standing outside a Holtburg house, the ground around it
renders with the same cobblestone / grass texture as the surrounding
terrain — no dark rectangular patches.
---
## #98-old-context-preserved-for-reference
(retained from the OPEN form for historical context — superseded by the
DONE resolution above. Skip to next active issue if you've read enough.)
**Status:** OPEN — **NEW diagnosis after A6.P3 slice 3 (2026-05-22)**
**Severity:** HIGH (blocks M1.5 demo cellar half — user can descend but cannot return)