docs: #265/#166 - ledger updates and capture-bisect as-fixed addendum

docs/ISSUES.md: #265 and #166 updated with the root cause and fix from
the prior two commits; closure of both pends the user's visual-gate
acceptance. #265 also records the confirmed-separate uphill-bounce
finding (AD-25, byte-exact retail, out of scope). #166 records that the
Campaign P visual-matrix recheck it was waiting on DID happen and found
the glide/bounce still missing even with AD-25/AP-7/AD-55/TS-4 all
landed - that negative result is what triggered the #265 capture bisect
and this fix.

docs/architecture/retail-divergence-register.md: AP-7's retirement note
corrected. The row's original claim ("no horizontal velocity to hammer")
undersold the gap - calc_friction was structurally unreachable with
meaningful data on any grounded path, not just inert on the root-motion
path. No new row filed: this change ports retail's mechanism faithfully
and does not introduce a new deviation.

docs/research/2026-07-30-265-capture-bisect.md: full "as-fixed" addendum
(new section 9) recording the implementation - the fix mechanism, fixture
results (freeze reproduced under the old model, slide+decay proven under
the new one), the downhill-direction derivation for the synthetic decay
case, the two separate mechanisms found while building the Runtime tests
(LeaveGround's edge-timing recompute, AP-77's no-sink fallback), the
uphill-bounce orthogonality proof, and final test totals.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-07-30 18:45:43 +02:00
parent 9910838fa4
commit 7fcc7db1d1
3 changed files with 388 additions and 30 deletions

View file

@ -183,23 +183,98 @@ ACE's >= reading" warning.
## #265 — Steep-slope response set: uphill-jump bounce, roof slides lost, edge wedge (TS-4 removal fallout — REVERTED)
**Status:** OPEN — the TS-4 removal is reverted (`2e27d066`+`a8a7d64b`);
re-test confirms expected. The underlying oracle work is re-opened.
**Status:** FIX IMPLEMENTED 2026-07-30 (this session) — closure pends the
user's visual-gate acceptance. The named culprit for symptoms (b) and (c)
was capture-bisected to a THIRD, pre-existing (frozen-phase, predates
Campaign P by ten days) mechanism — neither the S1 nor S2 suspects named
below — and is now ported. Symptom (a) is confirmed a SEPARATE,
pre-existing, already-closed retail-faithful mechanism (AD-25); see the
"as-fixed" addendum for the full trace.
**Severity:** HIGH (matrix live gate 2026-07-30, scenarios 4/5)
**Component:** physics — steep-poly response (BSPQuery Path-6 family)
**Component:** physics — grounded residual-velocity ownership
(`PlayerMovementController.cs`), not BSPQuery/Path-6 (see below)
**Symptoms (user report, on the shortcut-removed build):** (a) bouncing
when jumping INTO an uphill slope — retail does not; (b) house-roof
slides no longer happen ("as I used to"); (c) occasionally stuck sliding
on an edge — the historical wedge, live, refuting the oracle plan's
"pure-vertical degenerate only" convergence claim. The fixture-gated
removal under-modeled real trajectories. DO-NOT-RETRY the removal
without a live-capture-driven rework of the oracle plan §7 analysis
(capture a real roof-slide + uphill-jump trajectory with
ACDREAM_CAPTURE_RESOLVE and replay them as the gate). Note: the
downhill-landing sled remains #166 (pre-existing, unfixed — the cos(10°)
constant is necessary but not sufficient; the Sledding state never
engages).
TS-4 removal under-modeled real trajectories, but a full capture-driven
bisect (`docs/research/2026-07-30-265-capture-bisect.md`) cleared BOTH
of the two named Campaign-P suspects for the two concrete mined freeze
events:
- **S1** (`db2889af`, BSPQuery Path-6 `hasSphere1`) — reverting it locally
produced byte-identical replay output; its site is provably unreached
by either mined trajectory (`hit1` never true across an 80-tick
replay). Real, narrow, retail-faithful — NOT reverted.
- **S2** (`calc_friction`'s 0.25 threshold, AP-7) — proven inert by static
analysis before this session (zero production call sites at the time).
**Root cause (this session, capture-bisect + fix):**
`PlayerMovementController.cs`'s per-tick grounded block (the R6
"animation-root-motion-owned grounded movement" architecture, landed
`f961d700`, 2026-07-20 — ten days before Campaign P, so not a Campaign-P
regression) hand-zeroed `Velocity.X/Y` to EXACTLY zero every single tick
once `OnWalkable`, whenever animation root motion drives the walk (the
production graphical local-player path). This discarded any residual
horizontal momentum a fall/landing left on the body BEFORE
`calc_friction` (AP-7, already correctly ported) or
`PhysicsBody.UpdatePhysicsInternal`'s Euler integrator ever got a chance
to act on it — a mover that landed on a walkable roof/slope with residual
velocity had that velocity vanish the very next tick and never moved
again. A second, previously-unwired gap compounded this: `PhysicsBody.
GroundNormal` (what `calc_friction` dots the velocity against) had ZERO
production writers anywhere — it silently defaulted to `Vector3.UnitZ`
forever, so even without the zero, friction would have treated every
slope as flat ground.
**Fix:** (1) `src/AcDream.Core/Physics/PhysicsEngine.cs` now syncs
`body.GroundNormal` from the committed `ContactPlane.Normal` at the same
commit point that already publishes `ContactPlane` itself (Core-level,
so player/remote/ordinary/projectile all benefit uniformly — "the
mechanism is general," not roof-specific). (2)
`src/AcDream.Runtime/Gameplay/PlayerMovementController.cs`'s grounded
block no longer reconstructs `Velocity` at all for the animation-root-
motion case (only the headless/test-controller `get_state_velocity`
fallback still does, unchanged — that model has no separate root-motion
channel to compose with). Root motion still fully owns commanded
locomotion; this only stops DESTROYING whatever residual `Velocity`
already holds, letting it compose with root motion through the SAME
`ResolveWithTransition` sweep exactly as retail's
`CPhysicsObj::UpdatePositionInternal` composes both channels.
**Symptom (a) — NOT addressed, confirmed separate:** the "uphill bounce"
traces to `PhysicsObjUpdate.HandleAllCollisions`'s `shouldReflect`
gate (`!(prevOnWalkable && nowOnWalkable && !sledding)`), re-verified
BYTE-EXACT against the raw retail decomp (`handle_all_collisions`,
pc:282647-282760) this session. For any FRESH landing from airborne
(`prevOnWalkable=false`), retail itself reflects whenever the collision
normal shows "moving into the surface" (`dot &lt; 0`), regardless of
whether the destination is walkable — this is the SAME mechanism AD-25
closed (2026-07-30, Campaign P Slice P3, docs/ISSUES.md #166) for both
local and remote movers. Per CLAUDE.md's "do not fix code that matches
retail" rule, this is out of scope for a fix. A synthetic 30°-uphill test
(`UphillLanding_Synthetic_ReflectionDecisionUnaffectedByResidualVelocityFix`,
`Issue265SteepSlopeCaptureBisectTests.cs`) confirms the residual-velocity
fix above changes NOTHING about this reflection decision (same input,
same output, with or without the fix) — it is orthogonal, not
introduced or worsened. If the user's live repro still shows an
unwanted bounce after this fix lands, it needs its own dedicated
capture + brainstorm against `HandleAllCollisions`/`BSPQuery`, not a
reopening of this root cause.
**Evidence:** `docs/research/2026-07-30-265-capture-bisect.md`'s
as-fixed addendum; `Issue265SteepSlopeCaptureBisectTests.cs`'s new
`ComposedRoofLanding_*` fixtures (freeze reproduced under the old model,
survives+advances under the new one, exponential decay demonstrated in a
synthetic dot&lt;0.25 case); `PlayerMovementControllerTests.cs`'s new
`Update_AnimationRootMotion_WalkSpeedUnaffectedByResidualVelocityFix`
(ordinary walking is a no-op under the fix) and
`Update_RunningJumpLandsOnFlatGround_ResidualVelocitySurvivesAndDecays_NotFrozen`
(a real running jump's residual velocity survives landing and decays on
the actual production `PlayerMovementController`, not just the Core-level
model).
---
@ -4495,9 +4570,17 @@ wall from acdream, matching the retail-observer view side-by-side.
## #166 — Slope-landing glide + bounce absent (retail "sled" on downhill jumps)
**Status:** OPEN (post-R6 polish — user: "we could polish later"); all
four register-predicted composite deviations (AD-25, AP-7, AD-55, TS-4)
are now landed as of 2026-07-30 — pending only a visual-matrix recheck
**Status:** FIX IMPLEMENTED 2026-07-30 (this session) — closure pends the
user's visual-gate acceptance. The visual-matrix recheck this note asked
for DID happen (Campaign P matrix scenario 5) and found the glide/bounce
still missing even with all four register-predicted deviations
(AD-25/AP-7/AD-55/TS-4) landed — that negative result is exactly what
triggered the #265 capture bisect, which found a FIFTH, previously-
unnamed mechanism: `PlayerMovementController.cs`'s grounded block was
hand-zeroing residual `Velocity.X/Y` every tick, discarding any landing
momentum before AD-25/AP-7/AD-55's now-correct machinery ever got a
chance to act on it. See #265 for the full root cause and fix (same
commit); this issue is the "downhill sled" half of that same mechanism.
**Severity:** LOW (feel/polish)
**Filed:** 2026-07-03 (user observation during the R2-R4 visual pass)
**Component:** physics, landing
@ -4570,27 +4653,57 @@ against `0x0050ee70`, not the previously-carried ACE-derived `0.99999536f`
completion the "sled deceleration differs" framing above was waiting on —
all four of AD-25, AP-7, AD-55, and TS-4 are now landed.
**The fifth deviation found and fixed (2026-07-30, this session,
docs/research/2026-07-30-265-capture-bisect.md):** with AD-25/AP-7/
AD-55/TS-4 all landed, the matrix recheck STILL found no glide/bounce —
the composite framing above was correct as far as it went, but it
missed a pre-existing (2026-07-20, ten days before Campaign P) R6
architectural fact: `PlayerMovementController.cs`'s grounded quantum
block hand-zeroed `Velocity.X/Y` to exactly zero every tick once
`OnWalkable`, for the production animation-root-motion path. This ran
regardless of AD-25/AP-7/AD-55/TS-4's correctness — it simply erased the
residual velocity those fixes would otherwise have had something to act
on. A second gap compounded it: `PhysicsBody.GroundNormal` (the vector
`calc_friction` dots velocity against) had no production writer and
silently defaulted to `Vector3.UnitZ`, so slopes behaved like flat
ground even when velocity DID survive. Fixed by (1) syncing
`body.GroundNormal` from the committed `ContactPlane.Normal` in
`PhysicsEngine.cs`'s existing per-resolve commit block, and (2) no longer
reconstructing `Velocity` in the grounded block for the animation-root-
motion case (root motion still fully owns commanded locomotion; only the
residual-momentum zero is gone). A synthetic case with the real mined
roof polygon but a velocity/normal pairing under retail's 0.25 threshold
(`ComposedRoofLanding_NewFix_SyntheticGrazingApproach_DecaysViaCalcFriction`)
demonstrates genuine exponential decay via `calc_friction`; the real
captured landing's own velocity happens to fall in the "moving away fast
enough, no friction" band (dot ≥ 0.25), producing a constant-velocity
glide across the roof instead — both are correct per retail's ported
formula for their respective geometries.
Closure of #166 therefore pends only re-checking Campaign P's final
visual matrix item 5 ("Downhill jump landing: sled glide + bounce")
against a fresh capture — if the glide/bounce still visibly mismatches
retail now that every register-predicted composite deviation is retired,
that capture, not a guess, is what should drive any further work here,
and it should go through cdb against live retail before any client-side
Sledding-state mechanism is written (recall: no client-side
`PhysicsState.Sledding` auto-toggle exists in retail per the reattribution
above — a data-authored toggle exists only server-side).
against a fresh capture of THIS fix — if the glide/bounce still visibly
mismatches retail, that capture, not a guess, is what should drive any
further work here, and it should go through cdb against live retail
before any client-side Sledding-state mechanism is written (recall: no
client-side `PhysicsState.Sledding` auto-toggle exists in retail per the
reattribution above — a data-authored toggle exists only server-side).
**Where:** `src/AcDream.Runtime/Physics/RuntimeRemotePhysicsUpdater.cs`
(remote reflect, AD-25 — DONE 2026-07-30),
`src/AcDream.Core/Physics/PhysicsBody.cs` (`calc_friction`, AP-7 and AD-55
— both DONE 2026-07-30), `src/AcDream.Core/Physics/BSPQuery.cs` +
`FlatBspQuery.cs` (Path 6 steep branches, TS-4 — DONE 2026-07-30).
`FlatBspQuery.cs` (Path 6 steep branches, TS-4 — DONE 2026-07-30),
`src/AcDream.Runtime/Gameplay/PlayerMovementController.cs` (grounded
residual-velocity zero, the fifth deviation — DONE 2026-07-30),
`src/AcDream.Core/Physics/PhysicsEngine.cs` (`GroundNormal` wiring —
DONE 2026-07-30).
**Acceptance:** side-by-side downhill jump: acdream glides/bounces like
retail; flat-ground landings unchanged; no micro-bounce death spiral
(the reason AD-25 existed) reintroduced. Every code-side composite
deviation is now landed; only the visual-matrix recheck remains before
this issue can close.
deviation, including the fifth one found this session, is now landed;
only the visual-matrix recheck remains before this issue can close.
## #164 — UM action-replay dispatches drop the per-action Autonomous bit