acdream/docs/research/2026-05-21-walk-miss-capture-findings.md
Erik bb1e919ef2 docs(physics): spec + plan + findings for ISSUES #83 walk-miss probe spike
Three docs from the indoor walk-miss probe spike landed in commits
27c7284..a2e7a87:

- Spec: design of the [walk-miss] + [floor-polys] diagnostic emissions
  with the H1/H2/H3 disambiguation matrix.
- Plan: 3-task TDD implementation plan (flag, aggregator, emissions).
- Findings: live-capture analysis showing H3 (walkable_hits_sphere /
  adjust_sphere_to_plane synthesis rejection) is the dominant defect.
  817 of 876 ground-contact misses (93%) cluster at dz~0.48 m, while
  the 7 HITs all sit at dz~0.46 m — a 2 cm boundary between working
  and broken that points at the sphere-overlap math, not the probe
  distance. H1 (multi-cell iteration missing) is real but only 3%
  of misses, secondary. H2 (probe distance) ruled out.

Next step: line-by-line decomp comparison of FindWalkableInternal /
walkable_hits_sphere / adjust_sphere_to_plane against retail at
acclient_2013_pseudo_c.txt:322032 / :323006 / :326793, then design
the fix in a follow-up session.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 11:00:11 +02:00

252 lines
11 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.

# Indoor walk-miss probe — capture findings (ISSUES #83)
**Date:** 2026-05-21
**Session:** lucid-goldberg-1ba520
**Spec:** [`docs/superpowers/specs/2026-05-21-indoor-walk-miss-probe-design.md`](../superpowers/specs/2026-05-21-indoor-walk-miss-probe-design.md)
**Plan:** [`docs/superpowers/plans/2026-05-21-indoor-walk-miss-probe.md`](../superpowers/plans/2026-05-21-indoor-walk-miss-probe.md)
**Capture log:** `launch-walk-miss.utf8.log` (9,401 lines, this branch — uncommitted)
## TL;DR
**H3 is the dominant defect.** The indoor walkable-plane synthesis
(`BSPQuery.FindWalkableSphere``FindWalkableInternal`
`walkable_hits_sphere` + `adjust_sphere_to_plane`) **rejects floor
polygons it should accept** ~98 % of the time the player is standing on
a horizontal indoor floor. The HIT zone is razor-thin: misses cluster
at `dz=0.48 m` (cell-local foot-above-floor) while the only 7 HITs in
the entire capture all sat at `dz=0.46 m` — a **2 cm boundary** between
working and broken.
H1 (multi-cell iteration missing) is real but secondary: 59 events
(3 %) at doorway-threshold cells where the player stepped past a small
indoor floor poly and the LandCell terrain would have grounded them.
H2 (probe distance 0.5 m too short) is **not** the issue. The bulk of
H3 misses sit well within the probe envelope.
## Numbers
| Metric | Count |
|---|---:|
| Total `[walk-miss]` events | 1,814 |
| `[indoor-walkable] result=HIT` (synthesis succeeded) | 7 |
| `[indoor-walkable] result=MISS` (synthesis failed) | 1,814 |
| Synthesis HIT rate | **0.38 %** |
| `[floor-polys]` cell dumps (one per cached indoor cell) | 527 |
### Hypothesis classification (per spec disambiguation matrix)
| Class | Filter | Count | % of total |
|---|---|---:|---:|
| **H3 candidate** | `containsFootXY=True AND \|dz\| ≤ 0.5 m` | **817** | **45.0 %** |
| Airborne / jump | `containsFootXY=True AND \|dz\| > 0.5 m` | 938 | 51.7 % |
| **H1 candidate** | `containsFootXY=False AND landcell.hasTerrain=true` | **59** | 3.3 % |
| H1+H3 combo | `containsFootXY=False AND landcell.hasTerrain=false` | 0 | 0.0 % |
The 938 "airborne" events are not a defect — they correspond to the
test session's jump arc (the user jumped through the doorway during
capture). The probe correctly reports `containsFootXY=True` with a
large `dz` because the foot is XY-over a floor poly but vertically too
far above it. Setting these aside: of **876 ground-contact misses**,
**93 %** are H3.
### `nearest.dz` distribution (containsFootXY=True only)
| dz bucket | Count |
|---|---:|
| 0.00.2 m | 18 |
| 0.20.4 m | 7 |
| **0.40.5 m** | **792** |
| 0.51.0 m | 141 |
| 1.02.0 m | 427 |
| > 2.0 m | 370 |
| negative | 0 |
The massive 792-event spike at 0.40.5 m is the standing-on-the-floor
position. The 1.02.0 m and >2.0 m buckets are the jump arc.
## The 2 cm hit/miss boundary
The only 7 synthesis HITs in the capture share a precise property:
| HIT example | foot.W.Z | world floor Z | dz |
|---|---:|---:|---:|
| `cell=0xA9B40125 wpos=(104.263, 140.893, 66.480)` | 66.480 | 66.020 | **+0.46** |
| `cell=0xA9B40125 wpos=(104.272, 141.275, 66.480)` | 66.480 | 66.020 | +0.46 |
| `cell=0xA9B40123 wpos=(108.430, 134.116, 69.485)` | 69.485 | 69.020 | +0.47 |
| `cell=0xA9B40123 wpos=(108.443, 134.162, 69.485)` | 69.485 | 69.020 | +0.47 |
| `cell=0xA9B40123 wpos=(109.702, 133.700, 69.485)` | 69.485 | 69.020 | +0.47 |
The MISS lines from the same cottage, same physics tick rate:
| MISS example | foot.W.Z | world floor Z | dz |
|---|---:|---:|---:|
| `cell=0xA9B40125 foot.W=(104.263, 140.893, 66.500)` | 66.500 | 66.020 | **+0.48** |
| `cell=0xA9B40121 foot.W=(104.254, 140.441, 66.500)` | 66.500 | 66.020 | +0.48 |
The **20 mm difference in foot.W.Z** (66.480 → 66.500) flips the
synthesis from HIT to MISS. This matches the `+0.02 m` Z-bump
mentioned in
[TransitionTypes.cs:1511](src/AcDream.Core/Physics/TransitionTypes.cs:1511)
("the +0.02f Z-bump applied for render z-fight prevention"). When the
foot's world Z is at exactly the rendered floor + foot-height
(`world_floor + 0.46`), synthesis HITs. When it's 2 cm higher,
synthesis MISSES.
That's not a probe-distance issue. The probe distance is 0.5 m and
`dz=0.48 < 0.5`. The geometry is well within reach.
**The defect is in the sphere-overlap test or sphere-plane-adjustment
math inside `FindWalkableInternal`.** Retail anchors to compare against:
- `CPolygon::walkable_hits_sphere`
[`acclient_2013_pseudo_c.txt:323006-323028`](docs/research/named-retail/acclient_2013_pseudo_c.txt).
Slope test + `polygon_hits_sphere_slow_but_sure` overlap test.
- `CPolygon::adjust_sphere_to_plane`
[`acclient_2013_pseudo_c.txt:322032`](docs/research/named-retail/acclient_2013_pseudo_c.txt).
Sphere-to-plane projection with sweep-distance budget.
- `BSPLEAF::find_walkable`
[`acclient_2013_pseudo_c.txt:326793`](docs/research/named-retail/acclient_2013_pseudo_c.txt).
Iterates polys; requires BOTH `walkable_hits_sphere` AND
`adjust_sphere_to_plane` non-zero.
Our port lives in
[`BSPQuery.FindWalkableInternal`](src/AcDream.Core/Physics/BSPQuery.cs)
(called by `FindWalkableSphere`). Direct line-by-line comparison
against the retail oracle is the next step.
## H1 evidence (secondary, doorway-edge cases)
59 `[walk-miss]` events where the foot XY left the indoor floor poly
but the LandCell underneath would have been walkable. All concentrated
in cell `0xA9B40125`, whose floor poly is a tiny 1.5 m × 0.5 m strip
(`bbox=(-0.40,-5.65)..(1.10,-5.15)`) — this is a **doorway-threshold
cell**. The player crosses it; the foot XY exits the strip before they
reach the next cell.
Sample (last 3 walk-miss lines):
```
[walk-miss] cell=0xA9B40125 foot.W=(104.400,147.409,66.480)
foot.L=(0.100,-11.909,0.460) ...
containsFootXY=False
landcell.hasTerrain=true landcell.terrainZ=66.000 landcell.dz=+0.480
```
`foot.L.Y = -11.909`, well outside the strip's Y range
`[-5.65, -5.15]`. Outdoor LandCell terrain at world Z = 66.000 would
have grounded the foot at `dz = 0.480`. This is the case the prior
handoff (`docs/research/2026-05-20-indoor-walking-bug-a-handoff.md`)
diagnosed as "doorway threshold has no floor poly." It's real — but
**3 % of the total miss volume, not the primary defect**.
## H2 ruled out
Of 817 in-bbox candidate misses, **792 sit at `dz` between 0.4 m and
0.5 m**, well within the 0.5 m probe distance. Only 25 events fall in
the 0.00.4 m range (a few cm above plane — already touching).
Bumping `INDOOR_WALKABLE_PROBE_DISTANCE` will not help — the geometry
is reachable; the rejection is in the sphere-overlap math.
## Cells of interest
| Cell ID | Walk-misses | Floor polys (local-XY bboxes) | Role |
|---|---:|---|---|
| `0xA9B40121` | 1,453 | 1 @ Z=0, bbox `(-5.7,-5.15)..(5.7,4.55)` | Cottage main room (1st floor) |
| `0xA9B40123` | 283 | 5 @ Z=3.0 (multiple connected panels) | Cottage **2nd floor** |
| `0xA9B40125` | 67 | 1 @ Z=0, bbox `(-0.4,-5.65)..(1.1,-5.15)` | **Doorway threshold strip** |
| `0xA9B40126` | 11 | (no [floor-polys] dump captured at start) | Adjacent |
Cell `0xA9B40123`'s floor polys all sit at `planeZ@center=3.000`
that's 3 m above the cell origin, i.e. a 2nd-story floor. The HITs in
this cell at world Z 69.485 match: cell origin Z 66.020 + local floor
Z 3.0 = world floor 69.020, foot 0.46 above → world Z 69.485. ✓
This confirms our 2nd-floor handling is being **exercised** by the
synthesis; it's just rejecting at the same 2 cm boundary as the 1st
floor.
## Disambiguation matrix verdict (per spec)
| Matrix entry | Spec condition | This capture |
|---|---|---|
| **H1 confirmed** | `landcell.hasTerrain==true AND \|landcell.dz\| < 0.2 m` | 59 events at doorway threshold |
| **H2 confirmed** | `containsFootXY==true AND 0.5 m < nearest.dz < 5 m` | 0 events qualify (all "candidates" turned out to be jump-arc) |
| **H3 candidate** | `containsFootXY==true AND nearest.dz ≤ 0.5 m AND normalZ ≥ FloorZ` | **817 events** — the bulk |
| H1+H3 combo | `containsFootXY==false AND landcell.hasTerrain==false` | 0 events |
Spec matrix entry H3 is flagged as "next step: cdb attach to retail."
Given the 2 cm hit-vs-miss boundary and the matched normalZ + FloorZ +
in-bbox + in-probe signatures, we can attempt the retail decomp
side-by-side comparison **first** without cdb — the discrepancy is
narrow enough that the decomp + a focused unit test should expose it.
cdb is a fallback if that fails.
## Recommended next step
**Phase: design + ship the H3 fix.**
1. **Decomp comparison** (~1 hour, no code change):
- Read `acclient_2013_pseudo_c.txt:322032-322110` (`adjust_sphere_to_plane`)
and our equivalent inside `BSPQuery.FindWalkableInternal`
([BSPQuery.cs](src/AcDream.Core/Physics/BSPQuery.cs)) line-by-line.
- Read `acclient_2013_pseudo_c.txt:323006-323028` (`walkable_hits_sphere`)
and our equivalent.
- Read `acclient_2013_pseudo_c.txt:326793-326816` (`BSPLEAF::find_walkable`)
and our `FindWalkableInternal` traversal.
- Document any divergences in a follow-up findings note.
2. **Unit test for the 2 cm boundary** (~30 min):
- Synthetic `CellPhysics` with a horizontal floor at local Z=0.
- Foot sphere centered at `Z=0.46`, then again at `Z=0.48`. Assert
both HIT.
- Mirrors the IndoorWalkablePlaneTests fixture pattern.
- Expected: both fail at HEAD; pass after the fix.
3. **Fix the divergence found in step 1** (size unknown — could be a
one-line epsilon adjustment or a structural mismatch).
4. **Re-run this capture with the fix in place.** Expected outcome:
`[indoor-walkable] HIT` rate goes from 0.38 % to >95 % during
ground-contact frames; `[walk-miss]` H3 bucket collapses; H1 (the
59 doorway-edge events) remains.
5. **Then design H1 fix** as a separate, smaller phase — porting
retail's `CTransition::check_other_cells`
(`acclient_2013_pseudo_c.txt:272717`) for multi-cell BSP iteration.
Lower priority since 3 % of total misses and only manifests at
threshold strips.
6. **Delete the spike** when both H3 and H1 fixes ship: revert
`27c7284..a2e7a87` plus this findings doc.
## Anti-patterns to avoid (from prior handoffs)
- **Don't increase `INDOOR_WALKABLE_PROBE_DISTANCE`.** The data shows
probe distance is not the blocker.
- **Don't delete `TryFindIndoorWalkablePlane`** ("Bug A" from 2026-05-20)
— once H3 is fixed, the synthesis path will work correctly and is
the right call (not removable until retail's multi-cell iteration is
also ported).
- **Don't bypass `walkable_hits_sphere` overlap rejection with a
looser epsilon** without first verifying retail's exact behavior at
this boundary. The 2 cm difference is suspiciously close to the
rendered Z-bump (`+0.02 f`) used to prevent z-fighting on indoor
floors. There may be a coordinate-space mismatch where the player's
foot world Z is computed in the rendered (bumped) frame but the
synthesis expects the dat-stated (unbumped) frame, or vice versa.
Investigate before "fixing."
## Acceptance review
The probe spike's acceptance criteria from
[`2026-05-21-indoor-walk-miss-probe-design.md`](../superpowers/specs/2026-05-21-indoor-walk-miss-probe-design.md):
- [x] Build green, tests green
- [x] Live capture produced `[walk-miss]` lines at the cottage doorway
- [x] Live capture produced `[walk-miss]` lines on the cottage 2nd floor
- [x] Aggregated counts classify each MISS per the disambiguation matrix
- [x] Zero `[walk-miss]` / `[floor-polys]` lines when env var unset
(verified by code inspection; runtime verification deferred)
**Spike concluded. Ship findings, design the H3 fix.**