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>
This commit is contained in:
parent
a2e7a87c25
commit
bb1e919ef2
3 changed files with 1157 additions and 0 deletions
252
docs/research/2026-05-21-walk-miss-capture-findings.md
Normal file
252
docs/research/2026-05-21-walk-miss-capture-findings.md
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
# 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.0–0.2 m | 18 |
|
||||
| 0.2–0.4 m | 7 |
|
||||
| **0.4–0.5 m** | **792** |
|
||||
| 0.5–1.0 m | 141 |
|
||||
| 1.0–2.0 m | 427 |
|
||||
| > 2.0 m | 370 |
|
||||
| negative | 0 |
|
||||
|
||||
The massive 792-event spike at 0.4–0.5 m is the standing-on-the-floor
|
||||
position. The 1.0–2.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.0–0.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.**
|
||||
Loading…
Add table
Add a link
Reference in a new issue