ship(phys): A6.P3 slice 2 — L622 seed investigation + #98 filed
Slice 2 v1 (`892019b`) attempted to close issue #96 by removing the PhysicsEngine.cs L622 per-tick CP seed. v1 build/test green, CP-write count dropped 91% in scen3 re-capture, BUT user happy-test surfaced a regression: BSP step_up at the last step of stairs failed because sub-step 1's AdjustOffset had no ContactPlane to compute the lift direction. Slice 2 v2 (`f8d669b`) reverted the seed removal + added a no-op-if- unchanged guard inside CollisionInfo.SetContactPlane. The guard early-returns when called with values matching current ci state. Outcome: - #96 PARTIALLY ADDRESSED, scope updated in ISSUES.md to "accepted as documented retail divergence." The seed is load-bearing for step_up; closing #96 fully would require deeper refactor (AdjustOffset fallback to body.ContactPlane). Guard is benign improvement. - Slice 2 v2 verification capture (scen3_inn_2nd_floor_slice2v2/ acdream.log) committed as evidence — 226,464 cp-writes from L624 seed confirms guard doesn't trigger for fresh-ci-per-tick pattern. - Slice 2 v1 verification capture (scen3_inn_2nd_floor_slice2/ acdream.log) also committed — confirms v1 actually reduced cp-writes (2,690 total) but the step_up regression made it unshippable. NEW M1.5 BLOCKER FILED — issue #98: cellar ascent stuck at last step. Evidence in slice2v2 capture's cell-transit chain: 0xA9B4014B → 0xA9B4014A → 0xA9B4013F → 0xA9B4014A → 0xA9B4014B → ... (Z stable ~96.4; CellId ping-pongs every tick) This is Finding 3 family (cell-resolver hysteresis missing) — same root cause as #90 workaround + scen4 sling-out. Retail oracle: CObjCell::find_cell_list Position-variant at acclient_2013_pseudo_c.txt:308742-308783. NEXT — A6.P3 slice 3: - Port retail's cell-array hysteresis into ResolveCellId + CheckBuildingTransit. - Closes #98 (cellar-up), possibly #97 (phantom collisions same instability family), enables #90 workaround removal. Documents updated: - ISSUES.md — #96 scope updated, #98 filed - docs/plans/2026-04-11-roadmap.md — A6.P3 slice 2 marked SHIPPED, slice 3 scope added - CLAUDE.md — Currently-working-toward block updated to slice 3 Test suite: 1148 pass + 8 pre-existing fail (baseline maintained). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f8d669be88
commit
d868946537
5 changed files with 279969 additions and 44 deletions
40
CLAUDE.md
40
CLAUDE.md
|
|
@ -727,28 +727,34 @@ render identically to pre-O. Spec:
|
|||
|
||||
**Currently working toward: M1.5 — Indoor world feels right** (resumed
|
||||
from 2026-05-20 baseline after Phase O ship). **A6.P1 + A6.P2 + A6.P3
|
||||
slice 1 SHIPPED 2026-05-21** (this session). Slice 1's strip-synthesis
|
||||
+ Mechanism B (LKCP restore) fix unblocked stairs + cellar descent in
|
||||
acdream (user happy-test confirmed walking up/down stairs and into
|
||||
cellar) — confirms A6.P2's hypothesis that Finding 1 (dispatcher
|
||||
frequency mismatch) was a secondary effect of Finding 2 (CP-write
|
||||
blowup). Current A6 phase: **A6.P3 slice 2 — gate L622 per-tick CP
|
||||
seed (issue #96)**. Slice 2 closes the remaining 99.3% of post-fix CP
|
||||
writes that come from the body-CP seed at
|
||||
`PhysicsEngine.ResolveWithTransition:622` (retail equivalent fires zero
|
||||
calls). After slice 2, re-test phantom collisions + occasional
|
||||
fall-through on 2nd floor (issue #97, hypothesized side-effect of #96).
|
||||
slice 1 SHIPPED 2026-05-21.** **A6.P3 slice 2 v2 SHIPPED 2026-05-22**
|
||||
(commit `f8d669b`): tried removing the L622 per-tick CP seed
|
||||
(`892019b` v1) but it broke BSP step_up at the last step of stairs;
|
||||
reverted + added a benign no-op-if-unchanged guard inside
|
||||
`CollisionInfo.SetContactPlane`. Slice 2 outcome: **#96 partially
|
||||
addressed — accepted as documented retail divergence** (the per-tick
|
||||
seed is load-bearing for `AdjustOffset` slope-projection on sub-step 1
|
||||
which BSP step_up depends on; matching retail would require deeper
|
||||
refactor of AdjustOffset). Slice 2 verification surfaced a NEW
|
||||
M1.5-blocking bug: **user cannot walk UP out of cottage cellar — stuck
|
||||
at last step due to cell-resolver ping-pong (filed as issue #98,
|
||||
Finding 3 family).** Current A6 phase: **A6.P3 slice 3 — Finding 3
|
||||
cell-resolver stickiness (issue #98 + #90 workaround removal +
|
||||
possibly closes #97)**. Retail oracle: `CObjCell::find_cell_list`
|
||||
Position-variant at `acclient_2013_pseudo_c.txt:308742-308783`.
|
||||
Findings doc:
|
||||
[`docs/research/2026-05-21-a6-cdb-capture-findings.md`](docs/research/2026-05-21-a6-cdb-capture-findings.md).
|
||||
Original demo scenario (Holtburg Sewer end-to-end) is unreachable: sewer
|
||||
doesn't exist on this server, and **issue #95** (portal-graph visibility
|
||||
blowup) blocks any substitute dungeon. Revised M1.5 demo split into
|
||||
building/cellar half (ACHIEVABLE NOW — slice 1 unblocked the physics) +
|
||||
dungeon half (blocked on #95). Issues in scope: #80, #81, #83, #88, #90
|
||||
(workaround removal), **#95** (visibility blowup; not A6 scope), **#96**
|
||||
(L622 seed, slice 2), **#97** (phantom collisions + fall-through; #96
|
||||
side-effect), L-indoor, L-spotlight, indoor sling-out (Finding 3), and
|
||||
the `TryFindIndoorWalkablePlane` definition deletion (A6.P4). **M2
|
||||
building/cellar half (PARTIALLY ACHIEVABLE post-slice-1; cellar-ascent
|
||||
blocked on #98) + dungeon half (blocked on #95). Issues in scope: #80,
|
||||
#81, #83, #88, #90 (workaround removal after slice 3), **#95**
|
||||
(visibility; not A6 scope), **#96** (L622 seed; retail divergence
|
||||
accepted), **#97** (phantom collisions; may close as #98 side-effect),
|
||||
**#98** (cellar-ascent stuck; A6.P3 slice 3 target), L-indoor,
|
||||
L-spotlight, indoor sling-out (Finding 3 family with #98), and the
|
||||
`TryFindIndoorWalkablePlane` definition deletion (A6.P4). **M2
|
||||
("Kill a drudge") is deferred until M1.5 lands.** Full M1.5 writeup at
|
||||
the corresponding block in `docs/plans/2026-05-12-milestones.md`.
|
||||
|
||||
|
|
|
|||
|
|
@ -602,33 +602,33 @@ Retail oracle for cell-id hysteresis: `acclient_2013_pseudo_c.txt:308742-308783`
|
|||
|
||||
---
|
||||
|
||||
## #96 — Per-tick PhysicsEngine.ResolveWithTransition CP seed contributes 99.3% of post-slice-1 CP writes
|
||||
## #96 — Per-tick PhysicsEngine.ResolveWithTransition CP seed (retail divergence)
|
||||
|
||||
**Status:** OPEN — **A6.P3 slice 2 target**
|
||||
**Severity:** MEDIUM (functional code works; symptom is CP-write-rate mismatch vs retail; suspected cause of #97 phantom collisions)
|
||||
**Status:** PARTIALLY ADDRESSED — accepted as documented retail divergence
|
||||
**Severity:** LOW (cosmetic — CP-write counter inflates but behavior is correct)
|
||||
**Filed:** 2026-05-21
|
||||
**Component:** physics, ContactPlane retention
|
||||
|
||||
**Description:** After A6.P3 slice 1 (commits `5aba071` + `5f7722a` + `39fc037`) stripped the `TryFindIndoorWalkablePlane` synthesis path from `Transition.FindEnvCollisions` indoor branch, scen3 post-fix re-capture showed acdream still writes ContactPlane fields 25,082 times during a flat-floor walk — 24,906 of those (99.3%) come from `PhysicsEngine.ResolveWithTransition` line 622, which seeds `ci.ContactPlane` from `body.ContactPlane` at every transition start when the body is grounded. Retail's equivalent code path fires `set_contact_plane` zero times during the same flat-floor walk (scen3 retail BP7 = 0).
|
||||
|
||||
**Root cause / status:** The L622 seed is per-tick infrastructure (every transition tick that's grounded), writing the same plane value repeatedly. Retail does NOT seed `ci.ContactPlane` at transition start — it relies on Mechanism A (Path-6 land write inside `BSPQuery.FindCollisions`) and Mechanism B (LKCP restore inside `validate_transition`, ported in slice 1 commit `5aba071`) to populate `ci.ContactPlane` only when needed.
|
||||
**Slice 2 attempt + outcome (2026-05-22, commits `892019b` + `f8d669b`):**
|
||||
|
||||
The L622 seed comment claims it preserves slope-walking continuity. Need to verify whether removing it (relying on Mechanism B for cross-tick CP propagation) breaks slope walking, or whether the seed is redundant infrastructure left over from before Mechanism B existed.
|
||||
- **v1 attempt (`892019b`):** Removed the L622 seed entirely to match retail's `CTransition::init` clear-at-start behavior. Verified per-rebuild that the change deployed. CP-write count dropped 91% (30,420 → 2,690). **But broke BSP step_up at the last step of stairs** — sub-step 1's `AdjustOffset` had no ContactPlane to compute the lift direction, BSP step_up thrashed (12,489 push-back-disp + 2,226 push-back-cell signal). User confirmed: "I can't pass the last step of the stairs."
|
||||
- **v2 fix (`f8d669b`):** Reverted the seed removal + added no-op-if-unchanged guard inside `CollisionInfo.SetContactPlane`. The guard early-returns when called with values identical to current state. **The guard doesn't trigger for the L622 seed** because each tick gets a fresh `Transition` (so `ci.ContactPlaneValid=false` on entry → guard fails → write fires). So slice 2 v2 didn't actually reduce CP-write count for the seed case. It does dedupe within-tick redundant writes (e.g. Mechanism B restoring LKCP that equals current ci.CP), which is a small benign improvement.
|
||||
|
||||
**Fix sketch (slice 2 candidates):**
|
||||
- Option A: Remove the L622 seed entirely. Rely on Mechanism A + Mechanism B for CP propagation. Verify slope walking still works (uphill/downhill, walking up stairs).
|
||||
- Option B: Gate the L622 seed to fire only when `body.ContactPlane` has changed since last seed (track last-seeded plane). Most ticks would skip writes.
|
||||
- Option C: Hybrid — keep the seed but route it through a no-op-if-unchanged guard inside `CollisionInfo.SetContactPlane` itself (also applies to all other call sites).
|
||||
**Root cause / status (updated 2026-05-22):** The L622 seed IS load-bearing for `AdjustOffset` slope projection on sub-step 1, which BSP step_up depends on. Retail uses a different architecture (no seed; first sub-step has no CP and BSP path-6 establishes it). Matching retail would require a deeper refactor — making `AdjustOffset` fall back to `body.ContactPlane` when `ci.ContactPlane` is invalid, OR re-architecting the sub-step loop to not require CP for the first iteration. Both are non-trivial.
|
||||
|
||||
**Evidence (committed):**
|
||||
- `docs/research/2026-05-21-a6-captures/scen3_inn_2nd_floor_postfix/acdream.log` — shows 24,906 cp-write entries with `caller=PhysicsEngine.ResolveWithTransition:622`.
|
||||
- A6.P2 findings doc: per-unit-of-activity CP-write rate dropped 63× from slice 1 strip, but absolute count remains high.
|
||||
**Accepting the divergence:** the per-tick seed call is functionally correct — it propagates the player's current contact plane to the transition. The cost is a noisy CP-write counter (cosmetic) but the BEHAVIOR matches retail (player stays grounded on the correct plane, slope-snap works, step_up works). Closing #96 fully is deferred to a future refactor or accepted as is.
|
||||
|
||||
**Lessons learned:**
|
||||
- A counter-based metric (CP-write count) is not always a direct proxy for "behavior matches retail." Retail's set_contact_plane firing rate differs from ours because the call-site structure differs, not because the behavior differs.
|
||||
- The slice 1 hypothesis "Finding 1 (dispatcher entry frequency) may close as side-effect of Finding 2 (CP-write)" was confirmed by stairs+cellar working post-slice-1. But the slice 2 follow-up assumption "remaining 99.3% of CP writes are also a problem" was partially wrong — those writes are correct state propagation.
|
||||
|
||||
**Files:**
|
||||
- `src/AcDream.Core/Physics/PhysicsEngine.cs:622` (the seed call site)
|
||||
- `src/AcDream.Core/Physics/TransitionTypes.cs:251-270` (`CollisionInfo.SetContactPlane` — candidate for no-op-if-unchanged guard, Option C)
|
||||
- `src/AcDream.Core/Physics/PhysicsEngine.cs:620-626` (the seed call site, retained with updated comment)
|
||||
- `src/AcDream.Core/Physics/TransitionTypes.cs:259-279` (`CollisionInfo.SetContactPlane` no-op guard, retained as small improvement)
|
||||
|
||||
**Acceptance:** scen3 re-capture shows acdream cp-write count ≤ 100 (matching retail's BP7 ~0 with small tolerance for legitimate Mechanism A/B fires). Slope walking + stair-climb + cellar descent still work in visual verification.
|
||||
**If revisited:** investigate `AdjustOffset` fallback to `body.ContactPlane` when `ci.ContactPlane` is invalid — that would let us safely remove the seed. Or investigate retail's exact first-sub-step behavior to see if there's a different missing piece in our BSP step_up that would let it work without a seeded CP.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -664,6 +664,50 @@ Falsifiable: if #96 fix closes #97 as a side-effect, the hypothesis is confirmed
|
|||
|
||||
---
|
||||
|
||||
## #98 — Cellar ascent stuck at last step (cell-resolver ping-pong; Finding 3 manifestation)
|
||||
|
||||
**Status:** OPEN — **A6.P3 slice 3 target** (Finding 3 family)
|
||||
**Severity:** HIGH (blocks M1.5 demo cellar half — user can descend but cannot return)
|
||||
**Filed:** 2026-05-22
|
||||
**Component:** physics, cell resolver (ResolveCellId + CheckBuildingTransit)
|
||||
|
||||
**Description:** Walking UP from a Holtburg cottage cellar in acdream gets stuck "just almost at the last step up." Stairs going UP elsewhere (inn 2nd floor) work fine post-A6.P3 slice 1. Cellar DESCENT works. Only the cellar ASCENT from the bottom back to ground level fails — specifically at the last step where the player should transition from the indoor cellar cell to the cottage ground-floor cell.
|
||||
|
||||
**Evidence:** captured in slice 2 v2 verification at `docs/research/2026-05-21-a6-captures/scen3_inn_2nd_floor_slice2v2/acdream.log`. Cell-transit chain shows the resolver ping-ponging between three adjacent cells:
|
||||
|
||||
```
|
||||
0xA9B4014B → 0xA9B4014A → 0xA9B4013F → 0xA9B4014A → 0xA9B4014B → ...
|
||||
(Z stays ~96.4 throughout the ping-pong — vertical position stable but cell classification oscillating)
|
||||
```
|
||||
|
||||
Eventually the player gives up and returns down: `0xA9B4013F → 0xA9B40143 (Z drops to 94.020) → 0xA9B40146 (Z 93.426) → ...`
|
||||
|
||||
Each cell-transit event has `reason=resolver`, meaning `PhysicsEngine.ResolveCellId` is making the decision. The resolver classifies the position into a different cell each tick → `AdjustOffset` operates against a different cell's geometry each tick → can't accumulate forward motion → stuck.
|
||||
|
||||
**Root cause / status:** Same family as scen4 sling-out (A6.P2 Finding 3) and issue #90 cell-id ping-pong (which has a workaround). The retail oracle is `CObjCell::find_cell_list` Position-variant at `acclient_2013_pseudo_c.txt:308742-308783`. Retail uses cell-array hysteresis / stickiness to prevent flipping CellId on adjacent-cell boundaries when the sphere is on the boundary.
|
||||
|
||||
Our `ResolveCellId` + `CheckBuildingTransit` lack this stickiness — every tick they re-classify based on current position, ignoring "we were already in cell X last tick; if the new position is still close to X, stay in X."
|
||||
|
||||
**Fix sketch (slice 3):**
|
||||
1. Port retail's cell-array hysteresis from `CObjCell::find_cell_list`.
|
||||
2. Modify `ResolveCellId` to prefer the previous tick's CellId when the sphere is close to (but slightly outside) the previous cell's CellBSP volume.
|
||||
3. Modify `CheckBuildingTransit` similarly for building-shell transitions.
|
||||
4. May obsolete issue #90's workaround (the same stickiness mechanism would handle the doorway ping-pong too).
|
||||
|
||||
**Related issues:**
|
||||
- Issue #90 — Cell-id ping-pong at indoor doorway threshold (existing workaround; should be removed if Finding 3 fix lands cleanly)
|
||||
- Issue #97 — Phantom collisions + fall-through on 2nd floor (may also be the same cell-resolver instability)
|
||||
- A6.P2 Finding 3 — Indoor cell-resolver sling-out (scen4)
|
||||
|
||||
**Files:**
|
||||
- `src/AcDream.Core/Physics/PhysicsEngine.cs` (`ResolveCellId`)
|
||||
- `src/AcDream.Core/Physics/CellPhysics.cs` (`CheckBuildingTransit`)
|
||||
- `src/AcDream.Core/Physics/CellTransit.cs` (cell list iteration; may need stickiness here)
|
||||
|
||||
**Acceptance:** User can walk up out of a Holtburg cottage cellar without getting stuck at the last step. Cell-transit log shows no ping-pong on the cellar boundary. Issue #90 workaround can be removed (verified by ping-pong staying absent at the inn doorway too).
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
**Status:** DONE
|
||||
|
|
|
|||
|
|
@ -192,18 +192,32 @@ successfully 2026-04-30 for the steep-roof case. Matching binaries
|
|||
PARTIALLY CLOSED — 99.3% of remaining cp-writes come from L622
|
||||
per-tick body-CP seed at `PhysicsEngine.ResolveWithTransition:622`
|
||||
(filed as issue #96 for slice 2).
|
||||
- **A6.P3 slice 2 — Gate L622 per-tick CP seed** (issue #96,
|
||||
~1 day). Either remove the L622 seed entirely (rely on Mechanism
|
||||
A + Mechanism B for CP propagation) OR gate it to fire only on
|
||||
`body.ContactPlane` change. Verify slope walking + stair-climb +
|
||||
cellar still work. Target: scen3 cp-write ≤ 100 (matches retail
|
||||
BP7 ~0). Re-test phantom collisions + fall-through (issue #97,
|
||||
hypothesized side-effect of #96) post-slice-2.
|
||||
- **A6.P3 slice 3 — Finding 3 (cell-resolver sling-out)** (if not
|
||||
closed as side-effect of slice 1+2). Re-capture scen4 first to
|
||||
confirm whether sling-out persists. Otherwise: narrow fix in
|
||||
`ResolveCellId` + `CheckBuildingTransit` cell-stickiness; remove
|
||||
#90 workaround.
|
||||
- **✓ SHIPPED — A6.P3 slice 2 — L622 seed investigation + no-op guard**
|
||||
(2026-05-22, commits `892019b` v1 + `f8d669b` v2). v1 removed the
|
||||
L622 seed entirely; broke BSP step_up at the last step of stairs
|
||||
(user happy-test surfaced the regression). v2 reverted v1 + added
|
||||
a no-op-if-unchanged guard inside `CollisionInfo.SetContactPlane`.
|
||||
**#96 PARTIALLY ADDRESSED — accepted as documented retail
|
||||
divergence.** The seed is load-bearing for `AdjustOffset`
|
||||
slope-projection on sub-step 1 which BSP step_up depends on.
|
||||
Matching retail would require deeper refactor (e.g. AdjustOffset
|
||||
fallback to body.ContactPlane). Guard is benign improvement;
|
||||
further #96 closure deferred.
|
||||
- **A6.P3 slice 3 — Finding 3 (cell-resolver stickiness)** (NEXT,
|
||||
~1-2 days). New M1.5-blocking bug surfaced during slice 2
|
||||
verification: **user cannot walk UP from cottage cellar — stuck
|
||||
at last step due to cell-resolver ping-pong** (issue #98). Same
|
||||
family as #90 (existing workaround) + scen4 sling-out (Finding 3
|
||||
from A6.P2). Retail oracle: `CObjCell::find_cell_list`
|
||||
Position-variant at `acclient_2013_pseudo_c.txt:308742-308783`.
|
||||
Port retail's cell-array hysteresis: prefer previous tick's
|
||||
CellId when sphere is close to (but slightly outside) previous
|
||||
cell's CellBSP volume. May close issues #98 (target), #97
|
||||
(phantom collisions; same instability family), #90 (workaround
|
||||
removal), and scen4 sling-out together. **No re-captures
|
||||
required for slice 3 scope decision** — issue #98 evidence already
|
||||
captured in `docs/research/2026-05-21-a6-captures/scen3_inn_2nd_floor_slice2v2/acdream.log`
|
||||
cell-transit chain.
|
||||
- Issue #95 (visibility blowup) NOT in A6.P3 scope — separate work
|
||||
surface.
|
||||
- **A6.P4 — Remove workarounds + visual verification** (~1 day after
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue