test(physics): TS-4 fixture-first attempt reproduces the 2026-04-30 wedge; shortcut stays
Campaign P Slice P2 step 2-3
(docs/research/2026-07-30-response-layer-edge-family-pseudocode.md §4, §6
Step 3). Per the research doc's own port order, TS-4's Path-6 steep-poly
shortcut may only be removed after a fixture reproduces the original
"stuck in falling animation on a steep roof" symptom cleanly with the
shortcut disabled. No surviving live-session fixture exists from the
2026-04-30 L.4 commit (b1af56e); this adds a dat-free multi-frame capture
(Ts4SteepRoofWedgeCaptureTests) using BSPStepUpFixtures.SlopedUnwalkable's
63.4 degree slope, replayed at 30 Hz with gravity integrated between
PhysicsEngine.ResolveWithTransition calls -- the same idiom as
Issue185OutdoorStairsSeamReplayTests.
Against today's baseline (shortcut active) the capture is green, as
expected (the shortcut's explicit AddOffsetToCheckPos keeps the body
moving every tick by construction).
Scratch-removed the shortcut (both BSPQuery.cs sphere0/sphere1 branches,
not committed -- reverted after capture) and re-ran the same test: the
body falls and lands cleanly on the steep polygon at tick 17 (InContact,
OnWalkable=false, via retail's own permissive CTransition::check_walkable
LandingZ gate, pc:273202), then freezes at that exact position for the
rest of the run -- the exact historical wedge shape, tripping the test's
own >0.5s-frozen threshold at tick 33.
Root-cause diagnosis via ACDREAM_DUMP_EDGE_SLIDE=1: the freeze is upstream
of EdgeSlideAfterStepDownFailed/CliffSlide entirely (none of that
dispatch's diagnostics fire). TransitionalInsert's Phase 2 object-collision
check returns Adjusted on every retry attempt because Path 6's retail-
faithful SetCollide returns ADJUSTED_TS without repositioning the sphere
(unlike the interim shortcut, which explicitly pushes the sphere off the
face) -- the same steep polygon re-triggers Path 6 on the immediate retry,
forever, and Phase 3 (the sp.Collide handling that contains DoCheckWalkable,
the Placement re-test, and the TS-1 CliffSlide chain) is gated on Phase 1
AND Phase 2 both returning OK, so it is structurally unreachable from this
state. TS-1's completeness is moot here -- the code path that would call
into it never runs.
Per the mission's explicit escape valve: STOP here, keep the shortcut, and
report -- do not improvise a third variant. Full diagnosis, the exact
capture, and the concrete next research question (does retail's own
transitional_insert loop check sphere_path.collide on every iteration
regardless of Phase 2's own return value, or only when Phase 2 returns OK?)
are recorded in the research doc's §7 item 6 and the doc's headline; the
campaign plan's P2 section gets a matching status note.
Physics test suite: 1841 passed, 1 skipped (D4, pre-existing/unrelated), 0
failed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
4f7e29f7cf
commit
65de6921ce
3 changed files with 311 additions and 22 deletions
|
|
@ -84,6 +84,15 @@ character state continuously.
|
|||
|
||||
### P2 — Response-layer edge family — retires TS-1, TS-4, AP-7; closes #166, #116
|
||||
|
||||
**Status (2026-07-30):** TS-1 and AP-7 retired; #166 reattributed in
|
||||
ISSUES.md (no new code — see the research doc §3). **TS-4 is deferred, not
|
||||
retired** — the fixture-first removal attempt this doc's §6 Step 3 requires
|
||||
reproduced the historical 2026-04-30 L.4 wedge (a dat-free capture,
|
||||
`Ts4SteepRoofWedgeCaptureTests`); the shortcut stays in `BSPQuery.cs`. Root
|
||||
cause and the concrete next research step are recorded in
|
||||
`docs/research/2026-07-30-response-layer-edge-family-pseudocode.md` §7 item
|
||||
6. #116 is untouched (oracle-first, own session).
|
||||
|
||||
The collision *response* layer (what happens after a hit): ground
|
||||
friction, cliff edges, downhill landings, near-perpendicular wall
|
||||
slides. One oracle-driven pass; the physics digest's DO-NOT-RETRY table
|
||||
|
|
|
|||
|
|
@ -1,20 +1,30 @@
|
|||
# P2 — Collision response-layer edge family: port-ready pseudocode
|
||||
|
||||
**Status: RESEARCH PASS COMPLETE (2026-07-30).** Research-only doc for
|
||||
Campaign P Slice P2 (`docs/plans/2026-07-29-physics-parity-campaign.md`
|
||||
§P2). Retires TS-1, TS-4, AP-7; closes #166, #116. No source changes made
|
||||
by this doc; it is the pre-port research artifact for a future
|
||||
implementation session. **Headline findings that change the plan's
|
||||
assumptions:** TS-1 is already substantially ported (the register row
|
||||
and plan phrasing are stale — see §2); #166 is very likely NOT about a
|
||||
literal `PhysicsState.Sledding` auto-toggle at all (see §3); AP-7's L.3c
|
||||
regression may no longer reproduce under the post-R6 animation-root-motion
|
||||
architecture for the graphical path, but likely still reproduces for the
|
||||
headless/test path (see §1); TS-4's shortcut removal is coupled to TS-1's
|
||||
completion and must not be done independently (see §6 Step 3-4); #116
|
||||
remains a genuine oracle-first research item needing live cdb/Ghidra, not
|
||||
an implementation item (see §5). Read §6 (port order) before starting
|
||||
implementation — the safe sequence is not the plan's listed item order.
|
||||
**Status: RESEARCH PASS COMPLETE (2026-07-30); IMPLEMENTATION PASS PARTIAL
|
||||
(2026-07-30).** Originally a research-only doc for Campaign P Slice P2
|
||||
(`docs/plans/2026-07-29-physics-parity-campaign.md` §P2); a same-day
|
||||
implementation session landed TS-1's retirement and AP-7's fix, attempted
|
||||
TS-4 per this doc's own §6 Step 3 fixture-first order, reproduced the
|
||||
historical wedge, and stopped — see §7 item 6 for the full capture and
|
||||
root-cause diagnosis. TS-4 is NOT retired; its shortcut stays in place.
|
||||
#166 got a reattribution note in ISSUES.md rather than new code (per §3).
|
||||
#116 remains untouched (oracle-first, out of implementation scope).
|
||||
**Headline findings that change the plan's assumptions:** TS-1 was already
|
||||
substantially ported (the register row and plan phrasing were stale — see
|
||||
§2); the one real gap needed no code change (acdream's unified world-space
|
||||
`SpherePath` design makes retail's per-cell recache a no-op correction here
|
||||
— see §2 and the TS-1 register row's retirement text); #166 is very likely
|
||||
NOT about a literal `PhysicsState.Sledding` auto-toggle at all (see §3);
|
||||
AP-7's L.3c regression does not reproduce on the production graphical
|
||||
root-motion path post-R6, and now ports retail's confirmed 0.25f threshold
|
||||
(see §1); TS-4's shortcut removal is coupled to TS-1's completion and
|
||||
reproduces a wedge even after TS-1 lands — see the §7 item 6 update for the
|
||||
precise mechanism (Phase 3 of `TransitionalInsert` is structurally
|
||||
unreachable from Path 6's unconditional `SetCollide`, which returns
|
||||
`Adjusted` without repositioning the sphere) and what a future attempt
|
||||
needs to check first; #116 remains a genuine oracle-first research item
|
||||
needing live cdb/Ghidra, not an implementation item (see §5). Read §6 (port
|
||||
order) before starting further implementation on this family.
|
||||
|
||||
Every claim below is tagged **FACT** (grep/read-verified against the
|
||||
named-retail decomp, the register, ISSUES.md, or current acdream source
|
||||
|
|
@ -959,13 +969,81 @@ it blocks.
|
|||
walkable-steepness reroute as real compensating adaptations) or
|
||||
whether they're unnecessary inventions. Needs a fresh, focused
|
||||
named-decomp read (not Ghidra/cdb-gated — just not done this pass).
|
||||
6. **[TS-4 / Step 3]** Whether TS-1's Step 1 fix alone is sufficient to
|
||||
let TS-4's shortcut be safely removed, or whether the 2026-04-30 L.4
|
||||
session's wedge had additional causes not yet identified. Answerable
|
||||
only by the capture in §6 Step 3 — not a Ghidra/cdb question, but
|
||||
listed here because it's the single highest-risk unresolved item in
|
||||
this document (deleting a load-bearing shortcut based on an unproven
|
||||
assumption).
|
||||
6. **[TS-4 / Step 3] ANSWERED 2026-07-30 (implementation session) — NOT
|
||||
sufficient; the wedge reproduces, and its mechanism is now precisely
|
||||
characterized.** TS-1's gap #1 fix (this document's §2, landed the same
|
||||
session) does NOT unblock TS-4. A dat-free multi-frame capture
|
||||
(`tests/AcDream.Core.Tests/Physics/Ts4SteepRoofWedgeCaptureTests.cs`,
|
||||
using `BSPStepUpFixtures.SlopedUnwalkable`'s 63.4° slope,
|
||||
`PhysicsEngine.ResolveWithTransition` replayed at 30 Hz with gravity
|
||||
integrated between resolves — the same replay idiom as
|
||||
`Issue185OutdoorStairsSeamReplayTests`) reproduces the EXACT historical
|
||||
shape with the Path-6 steep shortcut temporarily removed (both
|
||||
`BSPQuery.cs` sphere0/sphere1 branches): the body falls cleanly (30
|
||||
ticks, position advancing every tick), lands at
|
||||
`(0.500, 0.000, 1.247)` at tick 17 with `InContact=true, OnWalkable=false`
|
||||
(confirming the steep polygon WAS accepted via the permissive
|
||||
`CTransition::check_walkable(0.0871556997f)` / `LandingZ` gate exactly
|
||||
as predicted from the retail source read below), then **freezes at that
|
||||
exact position for the remaining 16+ ticks with zero movement** — the
|
||||
test's own wedge-detection threshold (>15 consecutive frozen ticks =
|
||||
>0.5s) trips at tick 33. With the shortcut restored, the same test is
|
||||
green (the shortcut's explicit `AddOffsetToCheckPos` keeps the body
|
||||
moving every tick by construction). **The shortcut stays; TS-4 is NOT
|
||||
retired this session.**
|
||||
|
||||
**Root-cause diagnosis (`ACDREAM_DUMP_EDGE_SLIDE=1` capture against the
|
||||
scratch shortcut-removed build):** the freeze is NOT inside
|
||||
`EdgeSlideAfterStepDownFailed`/`CliffSlide` at all — none of that
|
||||
dispatch's diagnostic lines (`DumpEdgeSlideBranch`,
|
||||
`DumpStepDownBranchGate`, the `[steep-roof] PHASE3-RESET*` lines) fire
|
||||
even once during the frozen ticks. Every frozen tick instead logs only
|
||||
`edge-slide: phase2 attempt=0 env=OK obj=Adjusted` followed by
|
||||
`attempt=1 env=OK obj=Adjusted` — i.e. `TransitionalInsert`'s Phase 2
|
||||
object-collision check (`FindObjCollisionsInCell`, `TransitionTypes.cs:1572`)
|
||||
returns `Adjusted` on BOTH retry attempts, and per
|
||||
`TransitionTypes.cs:1591-1596` an `Adjusted` `objState` unconditionally
|
||||
`continue`s (retries Phase 1/2 from the top) rather than falling through
|
||||
toward Phase 2.5/Phase 3. **Phase 3 — the `if (sp.Collide) { ... }`
|
||||
block at `TransitionTypes.cs:1625` that contains the `DoCheckWalkable`
|
||||
Placement re-test AND (on walkable failure) the reset-with-conditional-
|
||||
`kill_velocity` path — is gated on Phase 1 AND Phase 2 BOTH returning
|
||||
`OK` simultaneously (`TransitionTypes.cs:1568-1621`). Path 6's own
|
||||
unconditional `SetCollide` (the retail-faithful code path TS-4 would
|
||||
restore) returns `Adjusted`, not `OK` (matching retail's own
|
||||
`return 3; // ADJUSTED_TS` at pc:323783, quoted in §4 above) AND does
|
||||
NOT itself reposition the sphere** — unlike the interim shortcut, which
|
||||
explicitly calls `AddOffsetToCheckPos` to push the sphere off the face
|
||||
every time it fires. With no repositioning, the SAME steep polygon at
|
||||
the SAME distance re-triggers Path 6 on the immediate retry, which
|
||||
again returns `Adjusted`, forever — an Adjusted↔retry oscillation at a
|
||||
fixed point that the loop's 2-attempt-per-resolve budget silently
|
||||
absorbs (returning the frozen position as though the resolve
|
||||
succeeded), repeating identically on every subsequent tick's fresh
|
||||
resolve call. **Phase 3 (and therefore `DoCheckWalkable`,
|
||||
`CliffSlide`, and the TS-1 chain entirely) is structurally unreachable
|
||||
from this state** — TS-1's completeness is moot here because the code
|
||||
path that would call into it never runs.
|
||||
|
||||
**What this means for a future attempt:** the missing piece is NOT
|
||||
(only) in `EdgeSlideAfterStepDownFailed`/`CliffSlide` — it is in how
|
||||
`TransitionalInsert`'s Phase 1/2/2.5/3 dispatch (`TransitionTypes.cs
|
||||
:1568-1710`) distinguishes "Phase 2 found a NEW collision, retry from
|
||||
the top" from "Phase 2 registered a touch via `sp.Collide` and should
|
||||
fall through toward Phase 3 regardless of its own `Adjusted` return."
|
||||
Retail's own `transitional_insert` (pc:273137, `0050b6f0`) has NOT been
|
||||
read closely enough this pass to say definitively whether it treats a
|
||||
Path-6-sourced `ADJUSTED_TS` differently from an ordinary Adjusted
|
||||
result before this session's `continue`-on-Adjusted structure was
|
||||
written — that fresh, close read (specifically: does retail's loop
|
||||
check `sphere_path.collide` on EVERY iteration regardless of the
|
||||
latest Phase-2 return value, or only when Phase 2 returns OK?) is the
|
||||
concrete next step, not a second speculative code change. Do not retry
|
||||
the plain shortcut-deletion variant without that read; do not invent a
|
||||
third variant (e.g. teaching Path 6 to reposition the sphere itself)
|
||||
without confirming that's what retail actually does — that would be
|
||||
exactly the kind of guess CLAUDE.md's workflow forbids twice in a row
|
||||
on the same item.
|
||||
7. **[#116 shape-1]** Where exactly, in the BSP/environment hit-test
|
||||
dispatch (candidate: `BSPTREE::find_collisions`'s `PathClipped`/
|
||||
`collide_with_pt` arm, pseudo-C ~323700-323830, sibling to the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue