From 8795655250a665d3c624a7e79687fea925d6d8fe Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 25 May 2026 19:03:58 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20issue=20#101=20=E2=80=94=20broken=20sta?= =?UTF-8?q?irs=20cyl=20phantom=20(post-A6.P7=20finding)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Visual verification of A6.P7 at Holtburg cottage door passed cleanly (1187 [cyl-skip-bsp] guard fires, 0 [cyl-test] on doors, 30/30 axis-aligned hits, smooth NE/SE slide along door face). While exploring post-verification, the user discovered a different staircase in cells 0xA9B40159 + 0xA9B4015A where the sphere cannot climb at all. Captured working baseline (stairs-working.jsonl, cottage cellar stairs in cells 0xA9B40143/146/147 — clean ↔ Z=90.95-94.00 traversal) and broken scenario (stairs-broken.jsonl, Z stays at 94.00 the entire 4216-record capture). Root cause is NOT a regression of A6.P7. It's a different bug shape: the staircase is built as a multi-part EnvCell entity (entityId 0x0040B500, ~150 parts), with 10 of those parts being 0.80m-radius cylinders forming the steps. Each cyl carries state=0x00000000 — no HAS_PHYSICS_BSP_PS — so A6.P7's BspOnlyDispatch guard correctly doesn't fire. Cyl height 0.80m exceeds A6.P6's step-up budget 0.60m so grounded step-over fails. Falls through to wall-slide which produces the same diagonal radial phantom A6.P7 closed for the door. The [resolve-bldg] lines reveal gfxObj=0x0100081A hasPhys=False bspR=0.00 vAabbR=0.82 — the underlying GfxObj has NO physics BSP; we appear to be synthesizing a cyl from the visual AABB radius. That synthesis path is the suspected misregistration. Filed as issue #101 with severity HIGH. Investigation handoff written covering 4 retail-research questions (cdb on retail at this stair location, Setup trace via entity-source probe, ShadowShapeBuilder vAabbR fallback audit, cell BSP poly dump), do-not-retry list, and 3 candidate fix shapes (don't synthesize cyl from vAabbR / cell BSP for stairs / cyl-height-tolerant step-over). The handoff explicitly defers implementation to a later session pending retail evidence. Files: - docs/research/2026-05-25-stairs-cyl-investigation-handoff.md (new) - docs/ISSUES.md — added #101 entry Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/ISSUES.md | 59 +++ ...-05-25-stairs-cyl-investigation-handoff.md | 339 ++++++++++++++++++ 2 files changed, 398 insertions(+) create mode 100644 docs/research/2026-05-25-stairs-cyl-investigation-handoff.md diff --git a/docs/ISSUES.md b/docs/ISSUES.md index e3b4c52..ef344ff 100644 --- a/docs/ISSUES.md +++ b/docs/ISSUES.md @@ -761,6 +761,65 @@ family (sling-out — also likely). --- +## #101 — Stair-step cylinder phantom blocks player on multi-part EnvCell entity + +**Status:** OPEN +**Severity:** HIGH (M1.5 — blocks stair climbing in at least one Holtburg +building; same symptom class as the just-closed A6.P7 door bug but +different root cause) +**Filed:** 2026-05-25 +**Component:** physics, dat-handling + +**Description:** At a Holtburg staircase in cells `0xA9B40159` + +`0xA9B4015A` (XY ≈ 110, 26; user-discovered post-A6.P7 visual +verification), the player sphere cannot climb the stairs. Walking into +the foot of the stairs from the east, the sphere hits a phantom slide +with radial-cyl normals (e.g. `(0.88, -0.47, 0)`) and never gains +altitude. Z stays at 94.00 for the entire 4216-record capture in +`stairs-broken.jsonl`. + +**Root cause / status:** The staircase is built as ~10 stacked cylinder +parts of multi-part entity `entityId=0x0040B500`. Each cyl is +`radius=0.80m, height=0.80m` at `Y=26.60`, stepping up in X+Z by 0.25m +per step. All parts carry `state=0x00000000` — **no +`HAS_PHYSICS_BSP_PS` flag**, so A6.P7's `Transition.BspOnlyDispatch` +gate does NOT fire. Cyls are tested. Cyl height (0.80m) exceeds A6.P6's +step-up budget (0.60m), so grounded step-over fails too. Falls through +to wall-slide → diagonal radial normal → slide tangent driven into +perpendicular cell wall → stuck. + +The `[resolve-bldg]` lines show `gfxObj=0x0100081A hasPhys=False +bspR=0.00 vAabbR=0.82` — the underlying `GfxObj` has NO physics BSP. We +appear to be synthesizing a cyl from the visual AABB radius +(`vAabbR=0.82` → registered as `r=0.80`). This synthesis path is the +suspected mis-registration. + +This is NOT a regression of A6.P7 (the cottage door works correctly +post-A6.P7, visually verified). It's a separate bug shape that A6.P7's +fix didn't address. + +**Files:** +- Captures: `stairs-broken.jsonl`, `stairs-broken.launch.log`, + `stairs-working.jsonl`, `stairs-working.launch.log` (gitignored, on + disk for the next session to read) +- Suspected mis-registration: `src/AcDream.Core/Physics/ShadowShapeBuilder.cs::FromSetup` +- Entity hydration site: `src/AcDream.App/Rendering/GameWindow.cs::BuildInteriorEntitiesForStreaming` + +**Research:** +- [`docs/research/2026-05-25-stairs-cyl-investigation-handoff.md`](research/2026-05-25-stairs-cyl-investigation-handoff.md) + — full investigation handoff with 4 research questions, do-not-retry + list, and 3 candidate fix shapes +- A6.P7 background (closed companion): + [`docs/research/2026-05-25-a6-door-cyl-retail-dispatch-investigation.md`](research/2026-05-25-a6-door-cyl-retail-dispatch-investigation.md) + +**Acceptance:** Walking west into the broken staircase at cells +`0xA9B40159` + `0xA9B4015A`, the player sphere ascends step-by-step to +the top (Z=96.47), then can walk back down to the bottom (Z=94.22) — no +phantom diagonal slide normals attributed to entity `0x0040B500`. +Comparable trajectory to `stairs-working.jsonl` cellar-stairs baseline. + +--- + ## #100 — Transparent rectangular patches around every house (terrain rendering) **Status:** OPEN diff --git a/docs/research/2026-05-25-stairs-cyl-investigation-handoff.md b/docs/research/2026-05-25-stairs-cyl-investigation-handoff.md new file mode 100644 index 0000000..5251545 --- /dev/null +++ b/docs/research/2026-05-25-stairs-cyl-investigation-handoff.md @@ -0,0 +1,339 @@ +# M1.5 — Broken stairs (cyl-only multi-part entity) — investigation handoff + +**Date:** 2026-05-25 PM +**Status:** Filed as issue #101 (post-A6.P7 visual verification surfaced a NEW +bug, not the closed door bug). **Research-only next session.** No +implementation until we know what retail does at this exact stair location. +**Predecessor handoff:** [`2026-05-25-a6-door-cyl-investigation-handoff.md`](2026-05-25-a6-door-cyl-investigation-handoff.md) +(closed by A6.P7 commit `888272a`). + +--- + +## TL;DR + +A6.P7 visual verification at Holtburg confirmed the cottage door is fixed. +While exploring, the user found **a different staircase that doesn't work** — +sphere can't climb at all. Captures show: + +- Stairs are in cells `0xA9B40159` + `0xA9B4015A` (NOT the cottage-cellar + cells `0xA9B40143/146/147` that work post-A6.P3 cellar fix). +- Geometry is a **multi-part entity** `0x0040B500` (entityId; ~150 parts in + the setup; 10 of them are stair-step cylinders). +- Each step is a separate cylinder (`r=0.80m, h=0.80m`) at `Y=26.60`, stepping + up in X and Z (0.25 m per step, Z: 94.22 → 96.47). +- `state=0x00000000` on each cyl part — **no `HAS_PHYSICS_BSP_PS` flag**, so + A6.P7's dispatch gate (`Transition.BspOnlyDispatch`) does NOT skip them. +- The cyls fire 284 `result=Slid` with diagonal radial normals like + `(0.88, -0.47, 0)` — the same phantom shape A6.P7 closed for the cottage + door, but here the cause is per-cyl-without-BSP, not per-entity-with-both. +- **Player Z stayed at 94.00 for the entire 4216-record capture** — never + gained altitude. + +This is **NOT** a regression of A6.P7. The fix did exactly what retail does +for entities with `HAS_PHYSICS_BSP_PS`. The stair bug is a separate class: +**cyl-only entities (no BSP) whose cyl geometry shouldn't physically block +the player but does.** + +--- + +## What today shipped (DO NOT redo) + +### A6.P7 — retail-binary cyl/BSP dispatch (commit `888272a`) +- File: `src/AcDream.Core/Physics/PhysicsBody.cs` (added + `PhysicsStateFlags.HasPhysicsBsp = 0x00010000`) +- File: `src/AcDream.Core/Physics/TransitionTypes.cs` (added + `Transition.BspOnlyDispatch(uint)` predicate + per-entry guard at the + cyl/sphere branch) +- Test: `tests/AcDream.Core.Tests/Physics/A6P7DispatchRulesTests.cs` (7 tests) +- Investigation: + [`docs/research/2026-05-25-a6-door-cyl-retail-dispatch-investigation.md`](2026-05-25-a6-door-cyl-retail-dispatch-investigation.md). +- **Visual-verified at Holtburg cottage door 2026-05-25.** Captures: + `launch-a6p7.log`, `launch-a6p7-v2.log` — 1187 `[cyl-skip-bsp]`, 0 + `[cyl-test]` on the door, 30 axis-aligned hits, no phantom diagonals. + +--- + +## The new bug — captures + evidence + +### Captures (on disk, gitignored — DO NOT commit them; treat as live data) +- **Working baseline** (cellar stairs that work): `stairs-working.jsonl` + (16.9 MB, ~22K records). Z range 90.95 ↔ 94.00 (full cellar climb). 12 + cell transitions; only 23 `hit=yes` events; no diagonal normals; user + ran up + down twice. Cells `0xA9B40143/146/147`. +- **Broken stairs**: `stairs-broken.jsonl` (8.1 MB, 4216 records). Z stayed + at 94.00 for the entire capture. Cells `0xA9B40159` + `0xA9B4015A`. The + player tried multiple approach angles; never climbed any step. +- **Launch logs with probes**: `stairs-working.launch.log`, + `stairs-broken.launch.log`. Contain `[cyl-test]`, `[cyl-skip-bsp]`, + `[bsp-test]`, `[resolve]`, `[resolve-bldg]` probe lines. + +### Reproduction +Login as `+Acdream` at Holtburg. The cellar stairs work (verified). The +broken stairs the user found are at world XY around (110, 26), Z range +94 → 96. Walk west into them — sphere hits something diagonal and gets +stuck oscillating between `n=(0, 1, 0)` and `n=(0.87, -0.49, 0)` slides. + +### Geometry summary (from `stairs-broken.launch.log`) +The blocker is multi-part entity `entityId=0x0040B500`. Ten of its parts +are cylinders forming a staircase at `Y=26.60`: + +| Part | World XY | Z (cyl bottom) | +|---|---|---| +| `0x40B5008C` (part 140) | (108.72, 26.60) | 96.47 | +| `0x40B5008D` (part 141) | (108.97, 26.60) | 96.22 | +| `0x40B5008E` (part 142) | (109.22, 26.60) | 95.97 | +| `0x40B5008F` (part 143) | (109.47, 26.60) | 95.72 | +| `0x40B50090` (part 144) | (109.72, 26.60) | 95.47 | +| `0x40B50091` (part 145) | (109.97, 26.60) | 95.22 | +| `0x40B50092` (part 146) | (110.22, 26.60) | 94.97 | +| `0x40B50093` (part 147) | (110.47, 26.60) | 94.72 | +| `0x40B50094` (part 148) | (110.72, 26.60) | 94.47 | +| `0x40B50095` (part 149) | (110.97, 26.60) | 94.22 | + +Each cyl: `radius=0.80, height=0.80, state=0x00000000`. The entity also +has a BSP part `obj=0xB5008900 gfx=0x01000C16 radius=2.645 pos=(109.30, +26.30, 95.75)` but it's effectively non-physics +(`hasPhys=False bspR=0.00 vAabbR=0.82`) — the `vAabbR` here is the +**visual** AABB radius being borrowed as a cylinder fallback because the +underlying `GfxObj` has no physics BSP. + +### What's blocking the player + +Sphere at `(112.115, 25.995, 94.00)` wants to move west. The closest cyl +`0x40B50095` is at `(110.97, 26.60, 94.22)`: +- `distXY = 1.295m` (just barely outside reach `0.80 + 0.48 = 1.28m`) +- But during sub-stepping the sphere center crosses 1.28m → cyl overlaps +- Radial normal direction from cyl center to sphere: `(0.884, -0.467, 0)` — + matches observed phantom hits `(0.88, -0.47)`, `(0.86, -0.51)`, etc. + +The cyl is **too tall (0.80m) to step over** under A6.P6's grounded +step-over check (step-up budget = 0.60m). Falls through to the +wall-slide branch which produces the diagonal radial normal that drives +the sphere's slide tangent into the perpendicular cell wall, then +re-blocks. Net: stuck. + +### Why A6.P7 doesn't help +A6.P7 gates the cyl branch on `(state & 0x10000) != 0`. These stair cyls +have `state=0x00000000` — bit not set. Guard does NOT fire. Cyls are +tested. Sphere blocks. + +--- + +## What this session needs — retail investigation + +**Mandate:** report-only research, NO implementation. Use the `/investigate` +skill. The fix design comes in a subsequent session once the retail +behavior is settled. + +### Question 1 — What does retail DO at this exact staircase? + +**Use cdb.** The toolchain in `CLAUDE.md` "Retail debugger toolchain" is +ready. The matching binary + PDB are verified. + +Concrete experiment: +1. Have the user run the retail acclient.exe (Microsoft AC official build + v11.4186) at the same world location (cells `0xA9B40159` + `0xA9B4015A`, + XY ≈ (110, 26)). The user needs to be IN the building, AT the foot of + these stairs. +2. Attach cdb with breakpoints: + - `acclient!CCylSphere::collides_with_sphere` at `0x53a880` — counter + `$t0`, log every 100 hits with the `this` pointer and the moving + sphere's position, `gc`. Auto-detach after 5000. + - `acclient!CCylSphere::intersects_sphere` (the dispatch from + `CPhysicsObj::FindObjCollisions` cyl branch) — counter `$t1`, log + entity address. + - `acclient!CObjCell::find_env_collisions` — counter `$t2`. Tells us if + retail uses cell BSP for stair collision. + - `acclient!CPartArray::FindObjCollisions` — counter `$t3`. Confirms BSP + dispatch path. +3. Have the user walk straight into the broken stairs from outside, then + try to climb them. Capture 30 seconds. +4. Detach. Analyze: + - Does `CCylSphere::collides_with_sphere` fire on the stair entity? If + yes → retail's cyls ARE active here, and retail somehow handles them + differently (different step-up threshold? cell-context-aware?). If + no → retail's cyls are excluded by something we don't replicate. + - Does `CObjCell::find_env_collisions` fire heavily? If yes → retail + might be using cell BSP polygons for the stairs (and the entity cyls + are decorative/click-targets only). + +### Question 2 — What's the Setup ID? Compare retail's PhysicsObj construction + +Our `[resolve-bldg]` lines show the entity is built from GfxObj +`0x0100081A` with `hasPhys=False`. **What's the Setup ID for entity +`0x0040B500`?** Trace through our streaming code to find which Setup +emitted the 150-part build. + +Steps: +1. Grep `src/AcDream.App/Rendering/GameWindow.cs` for the + `BuildInteriorEntitiesForStreaming` path (CLAUDE.md says it hydrates + EnvCell static objects with id `0x40xxxxxx`). +2. Add a temporary `[entity-source]` probe that logs the Setup id when an + entity gets registered. Or check existing diagnostic output — the + `gfxObj=0x0100081A` is the part's GfxObj, but we need the parent Setup. +3. With the Setup id in hand, look up retail's behavior: + - Decompile / grep `docs/research/named-retail/acclient_2013_pseudo_c.txt` + for `CPhysicsObj::InitPartArrayFromSetup` or similar to see how retail + builds the part_array from a Setup. Does retail include every part as + a collision shape, or filter by some flag? + +### Question 3 — Why is `vAabbR` becoming a cylinder? + +The `[resolve-bldg]` line shows `gfxObj=0x0100081A hasPhys=False bspR=0.00 +vAabbR=0.82`. We registered a `r=0.80` cyl. The 0.80 ≈ 0.82 match is +suspicious — we're using the **visual AABB radius** as a fallback cyl +radius when there's no physics BSP. + +Steps: +1. Find the code path in our tree that does this fallback. Likely in + `src/AcDream.Core/Physics/ShadowShapeBuilder.cs` `FromSetup` or in + `RegisterMultiPart`. Look for cases where `GfxObj.PhysicsBSP` is null + and a cyl is synthesized. +2. Cross-reference retail: does retail synthesize a cyl from visual bounds + when physics is null? Or does retail skip such parts entirely for + collision (visual-only)? +3. ACE check: `references/ACE/Source/ACE.Server/Physics/PhysicsObj.cs` — + how does ACE construct the part_array from a Setup with mixed + physics/visual-only parts? + +### Question 4 — Cell BSP fallback + +If retail's stairs are walked via cell BSP polygons (not entity cyls), +what's in cell `0xA9B40159`'s BSP at this XY/Z? Is there a walkable +polygon staircase that we're not iterating? + +Steps: +1. Use `ACDREAM_DUMP_CELLS=0xA9B40159,0xA9B4015A` to dump the cell BSPs to + JSON. (Confirm the env var path; see existing `CellDump` infra near + issue #98's apparatus.) +2. Look for inclined polygons in the dump that form the staircase. If + present → retail likely uses these for collision; our entity cyls are + either a setup misinterpretation or redundant. + +--- + +## Files to read FIRST next session + +| Path | Why | +|---|---| +| `docs/ISSUES.md` (#101) | The filed issue with severity + acceptance | +| `docs/research/2026-05-25-a6-door-cyl-retail-dispatch-investigation.md` | A6.P7 background (closed; companion bug) | +| `docs/research/named-retail/acclient_2013_pseudo_c.txt:276776` | `CPhysicsObj::FindObjCollisions` | +| Setup dat reader path in `src/AcDream.Core/Physics/ShadowShapeBuilder.cs` | Cyl synthesis from Setup; the suspected fallback | +| `src/AcDream.App/Rendering/GameWindow.cs::BuildInteriorEntitiesForStreaming` | Entity hydration for EnvCell statics | +| `references/ACE/Source/ACE.Server/Physics/PhysicsObj.cs` | ACE PartArray construction | +| `references/ACE/Source/ACE.Server/Physics/Common/Setup.cs` | ACE Setup → PartArray pipeline | + +--- + +## Tests that must stay green + +Same as A6.P7 list: + +``` +dotnet test tests/AcDream.Core.Tests/AcDream.Core.Tests.csproj --no-build -c Debug --filter "FullyQualifiedName=AcDream.Core.Tests.Physics.CellarUpTrajectoryReplayTests.LiveCompare_FirstCap_FixClosesCottageFloorCap|FullyQualifiedName=AcDream.Core.Tests.Physics.DoorBugTrajectoryReplayTests.Directional_OutsideIn_SouthApproach_BlocksAtSlabSouthFace|FullyQualifiedName=AcDream.Core.Tests.Physics.DoorBugTrajectoryReplayTests.Directional_InsideOut_NorthApproach_BlocksAtSlabNorthFace|FullyQualifiedName=AcDream.Core.Tests.Physics.DoorBugTrajectoryReplayTests.CornerSlide_AlcoveEastToCottageNorth_ShouldBlock|FullyQualifiedName=AcDream.Core.Tests.Physics.DoorBugTrajectoryReplayTests.Geometric_DoorSlabAtSphereHeight_OverlapsInZ|FullyQualifiedName=AcDream.Core.Tests.Physics.DoorBugTrajectoryReplayTests.InsideOut_Tick3254_WithCottageWalls_ShouldBlock|FullyQualifiedName~BSPQueryTests.FindCollisions_Path5|FullyQualifiedName~CellTransitTests.A6P5|FullyQualifiedName~DoorCollisionApparatusTests.Apparatus_DeadCenter|FullyQualifiedName~A6P7DispatchRulesTests" +``` + +Expected: 20/20 pass. + +--- + +## Things NOT to do (do-not-retry) + +1. **Don't lower step-up height** to make A6.P6's grounded step-over fit the + 0.80m cyl. Step-up budget = 0.60m is retail-faithful. Tweaking it would + regress every other surface where 0.60m is correct (curbs, low ledges). +2. **Don't extend A6.P7's `BspOnlyDispatch` to entities with `state=0`.** + That gate is retail-specific (`HAS_PHYSICS_BSP_PS`). Skipping cyls + purely because peer parts exist with BSP would diverge from retail and + break NPC cyl-only entities. +3. **Don't disable cyl fallback when `hasPhys=False` without checking + retail.** Until we know how retail handles `GfxObj` with no physics + BSP, "just skip the cyl" might break other content (small decorative + items that DO collide in retail). +4. **Don't add per-entity workarounds** ("if entity id 0x0040B500, skip + cyls"). Per CLAUDE.md no-workarounds rule. +5. **Don't enlarge the sphere's step-up budget for tall cyls.** Retail's + threshold is what it is. If retail steps over 0.80m cyls in this + scenario, the mechanism is something else. + +--- + +## Three fix-shape candidates (for the FOLLOWING session, not this one) + +Listed in rough order of retail-faithfulness based on the limited evidence +we have. The retail investigation will decide which is right. + +1. **Don't synthesize cyls from visual AABB when `GfxObj.PhysicsBSP` is + null.** Suppress at registration time in `ShadowShapeBuilder.FromSetup`. + Retail-anchored: if retail's `CPartArray` doesn't include such parts in + the collision list, our registration shouldn't either. The cell BSP + would then be the only collision source. +2. **Use cell BSP polygons** for stair geometry; entity cyls are + decorative-only for this entity class. Requires: (a) confirming cell + `0xA9B40159` BSP has walkable stair polys, (b) ensuring our cell BSP + query iterates them. Likely a no-op on our side once (1) is done. +3. **Make `step_sphere_up` cyl-height-tolerant** — if the sphere is on a + walkable plane and a cyl is detected, attempt step-up even when cyl + height > step-up budget IF a walkable surface exists at the top of the + cyl. Retail-anchored ONLY if cdb shows retail does this on these + specific stairs. + +--- + +## Pickup prompt for next session + +``` +A6 — Broken stairs cyl investigation (issue #101). Investigation-only session. + +Read first (in this order): + 1. docs/research/2026-05-25-stairs-cyl-investigation-handoff.md + (this file — full context, captures, geometry, do-not-retry list) + 2. docs/ISSUES.md #101 + 3. docs/research/2026-05-25-a6-door-cyl-retail-dispatch-investigation.md + (A6.P7 background — closed; companion bug) + +State both altitudes: + Currently working toward: M1.5 — Indoor world feels right + Current phase: A6 — broken-stairs investigation (issue #101) + +Session mandate: retail investigation, NOT implementation. Use the +/investigate skill. Specific questions (each must be answered with cited +evidence — retail line numbers, cdb traces, dat dumps): + + 1. Does retail's CCylSphere::collides_with_sphere fire on the stair-step + cylinders at cells 0xA9B40159/0xA9B4015A when a player walks in to + climb them? If yes — how does retail walk past 0.80m-tall cyls? If + no — what excludes them? + + 2. What's the Setup ID for entity 0x0040B500? Trace from + GameWindow.cs::BuildInteriorEntitiesForStreaming. Cross-reference how + retail's CPhysicsObj::InitPartArrayFromSetup (or equivalent) builds + the collision shape list — does retail include parts with + hasPhys=False? + + 3. Why does our ShadowShapeBuilder synthesize an r=0.80 cyl from + vAabbR=0.82 when GfxObj.PhysicsBSP is null? Identify the code path. + Does retail do this? + + 4. Dump cell 0xA9B40159's BSP polygons (ACDREAM_DUMP_CELLS). Does the + cell BSP have walkable stair polygons? If yes — retail's stair + collision is the cell BSP, not the entity cyls. + +Deliverable: a short report (~2-3 pages) covering the 4 questions with +retail line numbers, cdb trace excerpts, code citations. Then propose +which of the 3 fix-shape candidates is most retail-faithful (or a fifth +shape that emerges from the research). + +DO NOT implement the fix this session. Save it for the session after. + +Do-not-retry list (in handoff doc) — read it before starting. + +Tests to keep green if any code changes happen (none expected this +session): see handoff doc. + +Reproduction setup for the broken scenario: + ACDREAM_PROBE_BUILDING=1 ACDREAM_PROBE_RESOLVE=1 + ACDREAM_CAPTURE_RESOLVE=.jsonl + walk to cells 0xA9B40159/A in Holtburg (XY ≈ 110, 26) +```