acdream/docs/research/2026-07-03-r4-verify-session-handoff.md

98 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# R4 live-verify session handoff — fresh-session entry point (2026-07-03, evening)
Successor to `2026-07-03-phase-r-session-handoff.md` (whose postscripts
summarize the same arc — this doc is the current entry point). Worktree
`vigorous-joliot-f0c3ad`, branch `claude/vigorous-joliot-f0c3ad`, tree CLEAN
at `3c866f95`. Full suite green: **3,963** (+15 since the R4 handoff).
## State
**R4 is SHIPPED and user-verified live** (V0V6 + five root-cause fixes the
live pass exposed). Confirmed working by the user's eyes: local use-walk
(turn-first, walk/run by distance, arrival, door opens once), door open +
door SWING animation, remote (retail-client) close-range moveto walking,
and remote run-distance movetos at correct animation pace AND speed (#160).
## The five live fixes (trail, newest first — each is a real mechanism, no adaptations added)
| Commit | Root cause fixed |
|---|---|
| `41006e79` | **#160 slow-motion remote runs**: remote interps had NO weenie → retail's `apply_run_to_command` rate chain (`weenie ? (InqRunRate() ?: my_run_rate) : 1.0`, raw 305062-305076) took the degenerate 1.0 branch; the wire's `MoveToRunRate` (stored in `MyRunRate` by the mt-6/7 unpack, M13) was never consumed. Fix: `RemoteWeenie` (Core) — retail's per-object ACCWeenieObject stand-in; `InqRunRate` FAILS → `my_run_rate` fallback; `IsThePlayer`=false ends the "null weenie counts as the player" A3 reading. |
| `350fb5e3` | **Door-swing snap** (register TS-40): CMotionInterp's detached-object link-strip guard (`physics_obj->cell == 0`, raw @305627) was proxied by `CellPosition.ObjCellId==0`, seeded only by the local player's `SnapToCell` → every REMOTE body read "detached" and every dispatched transition link was stripped same-tick. Fix: explicit `PhysicsBody.InWorld` set by SnapToCell + RemoteMotion construction. |
| `006cf659` | **Remote-player glide (partial)** + **door UMs dropped**: (a) remote PLAYERS' MoveToManagers were never ticked (the V4 UseTime slot was NPC-only) — extracted `TickRemoteMoveTo`, now also in the grounded player-remote (L.3 M2) pipeline; (b) UP-less entities (doors) never got a RemoteMotion → since the S2b funnel cutover their UM motions were parsed and dropped — rm now created on first UM, with a `LastServerPosTime>0` gate keeping UP-less bodies OUT of the dead-reckoning tick. |
| `24569fd2` | **Moveto stall #2**: login `SetPosition` ran before the DefaultSink bind in `EnterPlayerModeNow` → its StopCompletely A9 node had no completion partner → ONE immortal pending_motions node → wait-for-anims gate never opened. Fix: bind block moved above SetPosition; `LoginQueue_DrainsToEmpty_UnderProductionFeed` pins the invariant. |
| `c2dc1a88` | **Moveto stall #1 (the big one)**: the R3 port MISREAD `StopCompletely_Internal` (0x0050ead0) as a velocity zero — it is `CPartArray::StopCompletelyInternal` (0x00518890) = `MotionTableManager::PerformMovement(type 5)`, the ANIMATION-side stop whose UNCONDITIONALLY-queued entry is the matched pop for the A9 pending_motions node. Fix: `IInterpretedMotionSink.StopCompletely()` → the R2 manager's already-ported type-5 op. Also: retail's per-tick `CheckForCompletedMotions` slot (CPartArray::HandleMovement 0x00517d60) added to the controller tick, and the P1 autonomous-STORE family completed (unpack store 00509730, input-edge stores DoMotion@00510030/StopMotion@005100e0, section-2 stops per-frame stamping, speculative install stores false). |
## The queue model (the load-bearing mental model — do not relearn)
`pending_motions` (interp) pops HEAD-ANY on each `MotionDone`; completions
come from the manager's `pending_animations` entries. **Every enqueue must
have a completion partner** (dispatch → manager entry). One orphan =
permanent ≥1 backlog (later completions just relabel it) = `MotionsPending`
true forever = `BeginTurnToHeading`'s verbatim wait-for-anims gate wedges
every moveto. Orphan producers found: StopCompletely-before-sink-bind and
the misread StopCompletely_Internal. The `[autowalk-gate]` probe prints the
whole gate state — it is how both wedges were pinned.
## OPEN — next session picks up here (full trails in docs/ISSUES.md)
1. **#161 — remote jump landing stuck in the falling pose** (retested
post-TS-40: still broken). Evidence in the issue: `VU.land` fires but
the first post-landing UM still sees `seq now motion=0x40000015`; the
char recovers only via subsequent wire UMs; window-focus change also
unsticks it (a frame-advance/big-dt hint). PRIME SUSPECT: the funnel's
airborne branch dispatches Falling via `DoInterpretedMotion` with
ctor-default `ModifyInterpretedState=true` → interpreted fwd BECOMES
0x40000015 → `HitGround`'s `apply_current_movement` (interpreted branch
for remotes) re-dispatches FALLING instead of the pre-fall wire command.
CHECK RETAIL FIRST: does raw ~305730's falling dispatch really write
interpreted fwd; what exits Falling on a retail REMOTE landing
(HitGround raw @305949, the `apply_current_movement(this, 1, ...)`
arg semantics); does K-fix17's landing SetCycle even execute (probe it).
2. **#162 — glide-class adjudication (PENDING USER)**: ACE re-broadcasts
every mover MoveToState to observers as autonomous mt-0
(MovementData.cs:162 — ACE's own "this is a hack" comment;
GameActionMoveToState.cs:36 unconditional; chain sends ONE mt-6, no
re-emit, nothing on arrival — all ACE-source-verified, see the issue).
Retail-verbatim head-interrupt cancels the observer moveto on each.
The user's retail-observing-retail A/B decides: parity (accept or a
NARROW registered adaptation — **explicit user approval required**) vs
new fact.
3. **#163 — strip the TEMPORARY diagnostics** once #161/#162 close:
`[autowalk-gate]` + `_lastAutowalkGateLogTime` (PlayerMovementController)
and `[autowalk-feed]` (GameWindow player tracker feed).
4. Then the **combined R2+R3+R4 visual-pass remainder** (jump/ledge
momentum, run-in-circles blend, stop settle, retail-observer view of
+Acdream) and **R5** (MovementManager facade + StickyManager +
ConstraintManager + full TargetManager — retires TS-39 + AP-79's
adapter).
## Session gotchas worth keeping
- **Evidence-first**: two plausible-but-wrong theories (a per-tick
apply_current_movement "pump" — retail's apply callers are EVENT-driven
only; and "ACE chain broadcasts mt-0 locomotion" — it doesn't) were
killed by the raw + wire captures. The `[autowalk-gate]` probe and the
UM raw hex dumps (autonomy byte at offset +14) were the decisive tools.
- **Retail object-model completeness matters**: two of the five fixes were
"retail gives every placed object X and we only gave it to the player"
(a weenie; a cell/InWorld). When a dispatch path behaves differently for
remotes, check what retail's CPhysicsObj carries that our RemoteMotion
lacks BEFORE suspecting the ported logic.
- The launch recipe + graceful-close rules are unchanged (CLAUDE.md); the
client locks builds — ask the user to close it, never kill.
- Register: TS-40 added; TS-33 extended (orientation-diff gap); AP-79
covers both tracker instances; TS-36/AD-26 retired earlier today.
## Key files this session touched
`MotionInterpreter.cs` (StopCompletely sink dispatch, InWorld guards),
`MoveToManager.cs` (MoveToComplete widened to BeginNextNode completion),
`MotionTableDispatchSink.cs` + `InterpretedMotionFunnel.cs`
(StopCompletely sink op), `PhysicsBody.cs` (InWorld), `RemoteWeenie.cs`
(new), `PlayerMovementController.cs` (UseTime slot + per-tick Check +
edge autonomy stores + temp probe), `GameWindow.cs` (P1 gate + store,
RouteServerMoveTo, TickRemoteMoveTo, rm-on-UM, EnterPlayerModeNow bind
order, player tracker feed), tests: `PlayerMoveToCutoverTests`,
`MoveToManagerCompletionSeamTests`, `InWorldLinkGuardTests`,
`RemoteWeenieRunRateTests`, `W6EdgeDrivenMovementTests` (apply-pass form).