# A6.P3 issue #98 handoff — 2026-05-23 (early morning)

**Worktree:** `C:\Users\erikn\source\repos\acdream\.claude\worktrees\strange-albattani-3fc83c`
**Branch:** `claude/strange-albattani-3fc83c`
**HEAD at handoff:** `467a81f` (this doc) on top of `cf3deff` (slice 5 probe + diagnosis)

**Status:** Cellar-up still broken. Tonight's slice 6 attempt at placement-insert bypass (multiple variations) did not converge. Worktree code is at the slice 5 baseline (commit `cf3deff`); none of tonight's bypass variations landed. **Investigation direction needs to pivot** — the placement-insert path is not the right place to fix this.

**Pasteable session-start prompt at the bottom of this doc.**

---

## TL;DR

Three sessions on this bug. Each previous session was confident about the diagnosis; each one was wrong:

| Session | Diagnosis | Outcome |
|---|---|---|
| 2026-05-22 morning | `BSPQuery.FindCollisions` Path 5 vs Path 6 path-selection | **Wrong** — slice 5 probe + retail BP4 data proved every retail `find_collisions` hit has `collide=0`, so retail enters the same Contact branch we do. |
| 2026-05-22 evening (slice 5) | Cellar ceiling polygon 0x0020 blocks placement_insert; cell-promotion would unstick the player | **Sharpened but incomplete** — the probe identified the polygon correctly, but cell-promotion alone doesn't fix it. |
| 2026-05-22 late evening (slice 6 attempts, this handoff) | Bypass placement_insert when blocker is a downward-facing cell-boundary polygon | **6+ variations tried, none unstuck the player.** Each variation produced "bypass fires, player still stuck." |

**The CLEAN finding from tonight:** the placement-insert path is NOT the root cause. Bypassing it (in 6 different ways) doesn't unstick the player. The actual blocker is somewhere else in the resolve chain, OR in the geometry pipeline (terrain mesh hole missing).

**User's most actionable clue (not yet investigated):** "Looking down to the cellar I can see that the entry is covered with outside ground. Like the ground continues and covers only the open path down into the cellar." → suggests a missing hole in the outdoor terrain mesh over the cellar entry. That's a terrain-generation bug, not a physics bug.

## What's committed

- `cf3deff` (slice 5) — `[place-fail]` + `[place-fail-obj]` probe + side-channel polygon attribution + the corrected diagnosis in ISSUES.md #98. **This is the durable value from this work.** It rules out the morning's Path 5/6 hypothesis with hard data and gives any future investigator the diagnostic infrastructure to identify which polygon blocks any placement check.

- The two captures at `docs/research/2026-05-21-a6-captures/scen4_cottage_cellar_place_fail/`:
  - `acdream.log` (probe pass 1)
  - `acdream_v2_with_obj_probe.log` (probe pass 2 with object-id emit)

## What did NOT work tonight (reverted)

All six variations of placement-insert bypass in `Transition.FindEnvCollisions` + `Transition.DoStepUp`:

| Variant | What it tried | Failure mode |
|---|---|---|
| **Sibling fallback** | If primary cell's Path 1 placement returns Collided, try other cells via portal-graph BFS. Accept if ANY sibling cell's BSP accepts the sphere. | All siblings (0xA9B40143, 0xA9B40146) also returned Collided. No cell accepts the sphere position. |
| **Cell-boundary bypass** (no lift) | When primary's blocker is a downward-facing polygon (N.Z < -0.5), return OK from FindEnvCollisions without modifying CheckPos. | Sphere stayed where the step-down probe left it (cellar walkable at world Z=93.22). Next tick re-runs same logic. Player oscillates at one Z. |
| **Bypass + ceiling-clearance lift** (`+0.05`) | Same as above, but also lift CheckPos to ceiling_world_z + 0.05 (sphere foot just above ceiling). | Sphere foot stuck at 93.87 across 72 events. Cell-resolver did not promote to cottage cell (cottage CellBSP volume might not extend down to sphere center at world Z=94.35, or rotation makes the check fail). |
| **Bypass + aggressive lift** (`+ diameter + 0.05`) | Lift CheckPos by ceiling + 0.96m so sphere clearly clears the cottage floor thickness layer. | 0 bypass events captured. Possibly client-side issue or geometry placement diverged enough to skip the bypass branch entirely. |
| **Override DoStepDown false result via flag** | When DoStepDown returns false AND CellBoundaryBypassActive=true, override stepDown=true. | The flag is reset at start of each TI iteration, and DoStepDown normally returns true via bypass — so the override branch never fires. Same player position. |
| **Per-bypass +0.1m lift in FindEnvCollisions** | Lift CheckPos by 0.1m each bypass fire. Multiple bypass fires per tick = cumulative climb. | Not properly tested — user signaled fatigue with the repeat-test cycle before this could be evaluated. |

**Common pattern across all variants:** bypass mechanically fires (verified via `[place-bypass]` log entries up to 72 per session). But the player's visual position does not progress in world Z. Sphere world-Z stays in the 93.0-93.9 band across hundreds of bypass events.

## What we KNOW (hard data, slice 5 captures)

1. **Blocking polygon identified:** polyId `0x0020` in cellar cell `0xA9B40147`'s BSP. Plane in cell-local: `n=(0,0,-1) d=-0.2`. World Z=93.82. This IS a real polygon in the dat — it's the underside of the cottage main floor's thickness layer (cellar ceiling).

2. **The polygon's "twin":** polyId `0x0004` quad form (n=(0,-0.707,0.707) d=-0.247) is a 45° walkable INSIDE the cellar. The step-down probe's `find_walkable` converges on this polygon and lifts the sphere to world Z ≈ 93.60 (sphere center).

3. **Cell origin (corrected):** cellar cell `0xA9B40147` has `WorldTransform.Translation = (130.5, 11.5, 94.02)`. My earlier inference of cell origin from `wpos - lpos` ignored cell rotation and was wrong. The probe's direct `worldOrigin` capture is authoritative.

4. **Sibling cells via portal graph:** the cellar connects to `0xA9B40146` and `0xA9B40143`. Neither cell's BSP accepts the sphere placement at world Z=93.60 — both have their own geometry that rejects (cottage floor underside, walls).

5. **Retail's player ascent reaches `ContactPlane = cottage main floor`:** retail's BP7 (`set_contact_plane`) fires 18 times during the ascent, all setting ContactPlane to `(0,0,1) d=-93.9998` (world Z=94, the cottage main floor surface). That polygon lives in some BSP — possibly the cottage main floor cell's BSP — and retail's find_walkable reaches it. Our find_walkable doesn't.

6. **Player input is real:** the user's input log shows MovementForward Press events. The user IS walking. The sphere world-Y advances ~0.3m over 22 ticks of bypass events — confirming forward motion IS being applied, just not climbing.

## What we DON'T KNOW (the open questions)

A. **Why our sphere world-Z doesn't progress despite the step-down probe lifting it onto the 45° walkable.** Each TI iteration's `StepSphereDown` adjusts the sphere upward, but successive iterations don't accumulate. After 5 iterations, sphere stops at the 45° walkable's surface. Maybe walk_interp depletion after iter 1 prevents further lift in iter 2-5; if so, retail must do the same and shouldn't progress either — but retail does. **Hypothesis: retail's `find_walkable` reaches a HIGHER walkable polygon than ours, possibly the cottage main floor itself, possibly via multi-cell iteration.**

B. **Why our ResolveCellId doesn't promote to cottage cell after the lift.** Even with sphere center at world Z=94.35 (above ceiling, above cottage main floor at Z=94), `engine.ResolveCellId` returned the cellar cell. Either the cottage cell's CellBSP volume doesn't extend down to Z=94.35 (geometry quirk), our PointInsideCellBsp test is too strict, or the portal-graph BFS doesn't include the cottage cell as a candidate at this position.

C. **The user's terrain-mesh clue: "outside ground covers the cellar entry."** Not investigated. If the outdoor landblock terrain mesh is missing a hole over the cellar entry, the visible terrain would block the player at the cellar's upward exit. This is a TERRAIN GENERATION bug, completely separate from `BSPQuery.FindCollisions` / `Transition.DoStepUp`. Code to inspect: `LandblockMesh.Build`, scenery generation, building stabs, the dat's `LandBlockInfo.CellsHas` flag handling.

D. **Why descending into the cellar WORKS but ascending doesn't.** The descent is the same physics + same dat geometry. Comparing descent vs ascent might reveal what's symmetric and what's not. We haven't captured `[place-fail]` during descent.

## What did the slice-5 captures actually prove?

Re-read carefully: the data identifies the BLOCKER (polygon 0x0020). But it does NOT prove that bypassing the placement_insert is the right fix. The captures show:

- Retail's BP5 (`adjust_sphere`) fires on the ramp polygon during the ascent (17 hits on `n=(0,-0.719,0.695)`). Sphere climbs from `cz=-1.07` to `+1.05` in object-local. **This is the player CLIMBING THE RAMP.**
- Retail's BP7 sets ContactPlane to the cottage main floor (world Z=94) 18 times. **This is the player REACHING the cottage main floor.**

Both happen in retail. In our client, neither happens — the sphere stays in the cellar's middle, oscillating near the 45° walkable. **The bug is in how our physics PROGRESSES the sphere UP THE RAMP**, not in how it handles the placement_insert at the top.

Maybe the placement_insert problem we obsessed over tonight is a SYMPTOM, not a cause. The sphere is stuck near the cellar ramp top → step-up fires → placement check fails. But the FIRST-ORDER question is: why is the sphere stuck in the middle of the cellar instead of climbing the ramp?

## Most promising directions for the next session

**Order matters — investigate in this sequence:**

1. **Investigate the terrain-mesh clue (highest signal, lowest effort).** Open the client at the cottage entrance, look DOWN into the cellar. If there's terrain covering the cellar's upward opening, that's a major suspect for the physical block. Code to inspect: terrain mesh generation, `LandblockMesh.Build`, hole-cutting where indoor cells exist above terrain. ~30 min investigation.

2. **Capture acdream's [place-fail] log during the cellar DESCENT (currently works) and compare to the ASCENT (doesn't work).** Same dat, same physics. The difference will be obvious.

3. **Add a `[step-walk]` probe** that logs sphere position + ContactPlane + WalkInterp at the start and end of each `ResolveWithTransition` call. Use it to see whether the sphere's Z progresses tick-by-tick during forward walking on the cellar ramp. If Z doesn't progress per tick, the bug is in `AdjustOffset` slope-projection, not in step-up.

4. **Capture retail at the cellar DESCENT** via cdb. Compare to ascent. If retail's `[BP1]` `transitional_insert` reaches different polygons during descent vs ascent, that tells us what's asymmetric.

5. **DO NOT** re-attempt any placement-insert bypass variant. Tonight's 6 variants are conclusive evidence that this code path is not the fix.

## Specific files to inspect for direction #1 (terrain mesh)

- `src/AcDream.App/Rendering/Wb/LandblockMesh.cs` — terrain mesh generation, scenery placement
- `src/AcDream.Core/Rendering/Wb/TerrainUtils.cs` — terrain triangle generation, split formula
- Anywhere that handles `LandBlockInfo.CellsHas` (the "this cell has indoor cells above it" flag)
- WorldBuilder's terrain generation as a reference (in `references/WorldBuilder/`)

## Pickup prompt for fresh session

Open a new Claude Code session at this worktree:

- **Path:** `C:\Users\erikn\source\repos\acdream\.claude\worktrees\strange-albattani-3fc83c`
- **Branch:** `claude/strange-albattani-3fc83c`
- **HEAD:** `467a81f` (this handoff doc) on top of `cf3deff` (slice 5 probe)

Then paste:

---

```
Pick up A6.P3 issue #98 — cellar ascent stuck — with a NEW investigation direction.

Read FIRST:
  docs/research/2026-05-23-a6-p3-issue98-handoff.md
  docs/research/2026-05-22-a6-p3-slice5-handoff.md
  docs/ISSUES.md issue #98 entry

Then state both altitudes:
  Currently working toward: M1.5 — Indoor world feels right
  Current phase: A6.P3 — fix #98 cellar-up
  Next concrete step: investigate the terrain-mesh hole over the cellar entry
  (user's clue: "outside ground covers only the open path down into the
  cellar"). This is direction #1 from the slice 6 handoff.

IMPORTANT: do NOT re-attempt any placement-insert bypass in
BSPQuery.FindCollisions, Transition.FindEnvCollisions, or Transition.DoStepDown.
The 2026-05-22 evening / 2026-05-23 early-morning sessions tried 6 variations
of this approach and none unstuck the player. The slice 5 probe data
identified polygon 0x0020 as the blocker but bypassing it doesn't fix the
underlying issue.

The actual fix is likely in one of these orders of likelihood:
  1. Terrain mesh generation missing a "hole" over the cellar entry (#1)
  2. Step-down probe's find_walkable doesn't reach the cottage main floor
     polygon (which retail's BP7 data confirms IS the eventual ContactPlane)
  3. AdjustOffset slope-projection isn't accumulating Z progress on the
     cellar ramp (per-tick climb is too slow or zero)

Test baseline: 1148 pass + 8 fail. Maintain through any fix.
CLAUDE.md rules apply. No workarounds without explicit approval.

If the user instructs "continue fixing" after 3+ failed attempts, push back
firmly — the systematic-debugging skill is unambiguous about this, and the
2026-05-22 sessions have proven that swinging through fatigue produces 6+
wasted variations.
```

---

## References

- A6.P3 slice 5 (committed): commit `cf3deff` adds `[place-fail]` probe + diagnosis correction
- Slice 5 handoff: [`docs/research/2026-05-22-a6-p3-slice5-handoff.md`](2026-05-22-a6-p3-slice5-handoff.md)
- Original A6.P3 handoff (morning, since superseded): [`docs/research/2026-05-22-a6-p3-handoff.md`](2026-05-22-a6-p3-handoff.md)
- ISSUES.md #98 entry — has the corrected diagnosis already
- Captures: `docs/research/2026-05-21-a6-captures/scen4_cottage_cellar_place_fail/`
- Retail cellar-up gold-standard data: `docs/research/2026-05-21-a6-captures/scen4_cottage_cellar_retail_for_issue98/`
