diff --git a/docs/research/2026-06-04-p2-cellar-corner-stepup-handoff.md b/docs/research/2026-06-04-p2-cellar-corner-stepup-handoff.md new file mode 100644 index 00000000..fbc71f17 --- /dev/null +++ b/docs/research/2026-06-04-p2-cellar-corner-stepup-handoff.md @@ -0,0 +1,91 @@ +# P2 pickup — cellar-top corner wedge = step-up-onto-cottage-floor (retail cdb pinned) + +> **Canonical pickup, 2026-06-04.** Branch `claude/thirsty-goldberg-51bb9b` (do NOT +> branch/worktree; do NOT push without asking; NEVER `git stash`/`gc`). PowerShell on +> Windows; launch logs are UTF-16. + +## State both altitudes +- **Milestone:** M1.5 — Indoor world feels right. +- **Phase:** P2 (door / building-shell collision) of the verbatim spatial-pipeline port. +- **Shipped this session (committed, branch HEAD `0935a31`):** + - `abbd761` — **B1 fix:** Path 5 (Contact) near-miss dispatch ported verbatim — gate + behind `num_sphere > 1`, head-first order, `neg_step_up` mapping (head→false/slide, + foot→true/step-up). Retail `transitional_insert`/`find_collisions` Contact branch + (`acclient_2013_pseudo_c.txt:323838-323881`, `set_neg_poly_hit` :323279). Fixed the + B1 grounded-step-up wedge (the handoff's "climb" localization was WRONG — proved via + `ITestOutputHelper` capture). + - `0935a31` — **slide_sphere fix:** head near-miss (`neg_step_up==0`) now calls the + faithful `CSphere::slide_sphere` (existing `SlideSphereInternal`) + continues the + insert loop, replacing the A6.P4 `Collided` shortcut (`transitional_insert` + pc:273350-273351). + - `f984e92` — docs (corrected the prior P2 handoff). +- **Visual-verified 2026-06-04:** generic step-up climbs; **closed cottage door still + BLOCKS** (slides tangentially, no walkthrough — regression check passed); **cellar + ascent went from ALWAYS-stuck → WORKS-MOSTLY.** +- **Remaining:** an **intermittent corner-wedge** at the cellar-top lip. Retail is + always smooth there (user-confirmed). So it's a real bug. + +## The cdb-pinned finding (retail ground truth) +`tools/cdb/cellar-corner-escape.cdb` traced live retail at the cellar-top corner +(decode: `parse_corner_log.py`; raw: `cellar-corner-retail.log`). Retail escapes the +corner by **STEP-UP, not slide:** +- `step_sphere_up`→`step_up` fired **196×** vs only **38 near-misses**. `step_up` + normals: +X wall ×78, **ceiling `(0,0,-1)` ×36**, +Y wall ×32, −X wall ×18, ramp + slope `(0,−0.62,0.78)` ×11, −Y wall ×10, floor `(0,0,1)` ×10. So retail step-ups + against EVERY grounded full-hit at the corner. +- **Contact plane transitions ramp `N.z=0.78` (×63) → flat cottage floor `N.z=1.0` + (×76).** That's the escape: retail **climbs the lip off the ramp ONTO the cottage + floor.** +- The user's "run in place against the ceiling (not stuck)" = `step_up` failing on the + ceiling normal `(0,0,-1)` → `step_up_slide` (transient; steer out). + +**Divergence pinned:** retail escapes by **stepping up onto the cottage floor**; +acdream **slides at the lip and never makes the ramp→floor transition**. The slide +itself (the `0935a31` fix) is correct + working; the gap is the **final lip-climb**. +This is the **original #98 core** — `DoStepDown`/`step_sphere_down` finding + landing +on the cottage floor — which B1+slide got close to but didn't finish. + +## Next step (evidence-first — #98 saga rule: do NOT guess) +1. **Instrument acdream's OWN corner path.** The captures so far + (`cellar-up-capture*.jsonl`, `door-recheck-capture.jsonl`) have positions/normals but + NOT the path. Need to answer: at the cellar-top lip, does acdream's `step_sphere_up`→ + `DoStepUp` FIRE and FAIL to land on the cottage floor (DoStepDown can't find + `N.z=1.0` within `StepUpHeight=0.6`), or does it not fire (the hit goes to the slide + path instead)? Relaunch acdream with `ProbeBuildingEnabled` (→ `[neg-poly-dispatch]`/ + `[bsp-test]`) + `ACDREAM_DUMP_STEPUP=1` + `ProbeStepWalkEnabled` (→ `[step-walk]`), + reproduce the wedge, read the path. (xunit-swallow doesn't apply to the live app — + Console probes DO surface in the launch log.) +2. **Compare to retail's 196 step_up / ramp→floor transition** and port the missing + lip-climb verbatim. Likely in `DoStepDown` (`TransitionTypes.cs:3074`) / + `BSPQuery.step_sphere_down` (:1206) / `find_walkable` (:693) — the cottage-floor + find+land. Retail anchors: `CTransition::step_up` pc:273099, `step_down` pc:272946, + `BSPTREE::step_sphere_down` pc:323665, `CObjCell::find_env_collisions` (the + walkable-refresh that overwrites the contact plane ramp→floor). +3. **USER VISUAL GATE:** cellar ascent clean (no intermittent wedge); door still blocks; + generic step-up still climbs. + +## Apparatus (committed / available) +- `tools/cdb/cellar-corner-escape.cdb` — retail corner trace (step_up/step_sphere_up/ + neg_poly_hit/contact_plane counts + args; 30K threshold — TOO HIGH for these + lower-frequency BPs, lower to ~3000 next time so it auto-detaches in one wedge). +- `parse_corner_log.py` — decodes the cdb log (hex→float, histograms). +- Captures (UNCOMMITTED, in worktree root, ~32 MB each — do NOT commit): + `cellar-up-capture.jsonl` (v1, pre-slide-fix wedge), `cellar-up-capture-v2.jsonl` + (post-slide-fix: 96 hit-and-advanced slide frames), `door-recheck-capture.jsonl`, + `cellar-corner-retail.log` (the retail cdb trace). +- `analyze_cellar.py` / `analyze_v2.py` — ad-hoc capture analyzers (capture-specific). + +## Test baseline +Core 1310 pass / 4 fail / 1 skip. The 4 fails are pre-existing documents-the-bug / +separate-issue: `DoorCollisionApparatusTests.Apparatus_Grounded_50cmOffCenter` +(synthetic-test artifact — terrain=-1000, no queryable floor; NOT a real door-block +failure — see `memory/project_p2_door_stepup_findings.md`), 2× `DoorBugTrajectoryReplay +LiveCompare_*` (compare against captured-BUGGY-live positions; need re-baseline), and +`BSPStepUpTests.D4` (airborne Path 6 sliding-normal persistence — separate). App 177 green. + +## Do NOT +- Guess a step-up fix without acdream's corner path trace (the #98 saga burned 10+ guesses). +- Flip `Apparatus_Grounded_50cmOffCenter` to `Assert.True(blocked)` — it blocks via a + synthetic-floor artifact, not a faithful door block. +- Re-investigate B1 (the near-miss gate) or the slide_sphere head-near-miss path — both + shipped + verified. diff --git a/tools/cdb/cellar-corner-escape.cdb b/tools/cdb/cellar-corner-escape.cdb new file mode 100644 index 00000000..5ed430ea --- /dev/null +++ b/tools/cdb/cellar-corner-escape.cdb @@ -0,0 +1,51 @@ +$$ +$$ Cellar-top corner-escape capture — 2026-06-04 (P2 cellar wedge) +$$ +$$ Q: at the cellar-top corner, the acdream player (on the ramp, pushing +Y) +$$ wedges against a -Y wall (head-sphere near-miss -> slide, oscillates, no +$$ net +Y). Does RETAIL escape by STEPPING UP over the lip, or by SLIDING, +$$ and is the near-miss a HEAD (neg_step_up=0) or FOOT (neg_step_up=1) hit? +$$ That decides whether the fix is slide-convergence vs step-up-at-the-lip. +$$ +$$ Unique (non-overloaded) symbols only -> robust counts + key args: +$$ BPA CTransition::step_up(this, Vector3* normal) step-up ATTEMPT +$$ BPB BSPTREE::step_sphere_up(this@ecx, CTransition*, V3*) step-up DISPATCH +$$ BPC SPHEREPATH::set_neg_poly_hit(this, int negStepUp, V3* n) +$$ negStepUp 0=head(slide) 1=foot(step-up); also logs n +$$ BPD COLLISIONINFO::set_contact_plane(this, Plane*, isWater) +$$ landing signal: N.z ~1 = cottage floor (escaped), ~0.7 = ramp +$$ +$$ thiscall: ecx=this; args [esp+4],[esp+8],... CSphere center: x+0 y+4 z+8 r+0xc +$$ Plane (set_contact_plane arg): N.x+0 N.y+4 N.z+8 d+0xc +$$ Floats logged as 32-bit hex (dwo + %08X); decode with tools/cdb/decode_retail_hex.py +$$ +$$ Threshold 30000 hits across A+B+C (auto-detach via qd). BPD unbounded. +$$ Walk up the cellar stairs into the corner and let it wedge once or twice; +$$ the wedge generates the hits, so 30K auto-detaches within an attempt or two. + +.logopen C:\Users\erikn\source\repos\acdream\.claude\worktrees\thirsty-goldberg-51bb9b\cellar-corner-retail.log +.sympath C:\Users\erikn\source\repos\acdream\refs +.symopt+ 0x40 +.reload /f acclient.exe + +r $t0 = 0 +r $t1 = 0 +r $t2 = 0 +r $t3 = 0 +r $t4 = 0 + +$$ BPA: CTransition::step_up(this, Vector3* normal) — did retail try to step up the lip? +bp acclient!CTransition::step_up "r $t1=@$t1+1; r $t0=@$t0+1; .printf /D \"[BPA] step_up #%d nx_h=0x%08X ny_h=0x%08X nz_h=0x%08X\\n\", @$t1, dwo(poi(@esp+4)+0), dwo(poi(@esp+4)+4), dwo(poi(@esp+4)+8); .if (@$t0 >= 30000) { qd } .else { gc }" + +$$ BPB: BSPTREE::step_sphere_up — step-up dispatch count. +bp acclient!BSPTREE::step_sphere_up "r $t2=@$t2+1; r $t0=@$t0+1; .printf /D \"[BPB] step_sphere_up #%d\\n\", @$t2; .if (@$t0 >= 30000) { qd } .else { gc }" + +$$ BPC: SPHEREPATH::set_neg_poly_hit(this, int negStepUp, Vector3* n) +bp acclient!SPHEREPATH::set_neg_poly_hit "r $t3=@$t3+1; r $t0=@$t0+1; .printf /D \"[BPC] neg_poly_hit #%d negStepUp=%d nx_h=0x%08X ny_h=0x%08X nz_h=0x%08X\\n\", @$t3, dwo(@esp+4), dwo(poi(@esp+8)+0), dwo(poi(@esp+8)+4), dwo(poi(@esp+8)+8); .if (@$t0 >= 30000) { qd } .else { gc }" + +$$ BPD: COLLISIONINFO::set_contact_plane(this, Plane*, isWater) — landing signal (unbounded). +bp acclient!COLLISIONINFO::set_contact_plane "r $t4=@$t4+1; .printf /D \"[BPD] contact_plane #%d Nz_h=0x%08X d_h=0x%08X\\n\", @$t4, dwo(poi(@esp+4)+8), dwo(poi(@esp+4)+0xc); gc" + +.printf "cellar-corner-escape armed: A=step_up B=step_sphere_up C=neg_poly_hit D=contact_plane; 30K threshold\\n" + +g