# Phase R handoff — fresh-session entry point (2026-07-03) > **POSTSCRIPT (2026-07-03, later session): R4 is COMPLETE.** V5 (local-player > cutover) shipped as `b3decdfa` — P1 gate ported verbatim, B.6 auto-walk > deleted, TS-36 bound, run-rate re-anchored to PD skills + mt-6/7 > my_run_rate with NO AD row (the contingency wasn't needed — see the > V0-pins.md P1 postscript), MoveToComplete seam widened to natural > completion for AD-27, plus three adversarial-review fixes (remote > HitGround relay, mt-8 wire_heading degrade, remote curTime clock). V6 > register/roadmap/memory sweep shipped in the follow-up commit. The V4 > smoke log was analyzed CLEAN (mt-6/8 events all dispatched, arrival > stop transitions, zero exceptions/stalls). Suite: 3,956 green. > REMAINING: the ONE combined R2+R3+R4 user visual pass, then R5. > The sections below are the historical entry state, kept verbatim. > > **POSTSCRIPT 2 (2026-07-03, the live verify session):** four live bugs > found+fixed during the user's pass, each root-caused (trail: > `c2dc1a88` StopCompletely's missing StopCompletely_Internal animation > dispatch orphaned pending_motions → wait-for-anims wedge; `24569fd2` > login SetPosition ran before the sink bind → one immortal orphan node; > `006cf659` remote-player movetos never ticked (glide) + door UMs > dropped (no RemoteMotion for UP-less entities); `350fb5e3` the > detached-object guard's ObjCellId proxy stripped ALL remote transition > links → door-swing snap, register TS-40). CONFIRMED WORKING by user: > local use-walk + door open + door swing + remote close-range walk. > STILL OPEN: ISSUES **#160** (remote run-anim pace vs ACE movement > speed), **#161** (remote landing pose — retest first, TS-40 may have > fixed it), **#162** (glide-class ACE autonomous-reflection cancels — > adjudication pending the user's retail-vs-retail A/B; adaptation needs > explicit approval), **#163** (strip the temp diagnostics). **Read this first, then the plan of record** (`docs/plans/2026-07-02-retail-motion-animation-rewrite.md`). This session ran the R2→R4 arc of the Phase R mandate; the fresh session picks up at **R4-V4 smoke verification → R4-V5**. ## The mandate (user, standing) Total overhaul of movement + animation as a VERBATIM retail port — all movement, inbound and outbound, all animation, for players/NPCs/monsters. No guessing, no approximations, no bandaids. The user cancelled the /loop self-scheduling near the end of this session — **work continues on user prompts, not self-scheduled wakeups**, but the drive-autonomously rules in CLAUDE.md still apply within a turn. ## State at handoff (worktree `vigorous-joliot-f0c3ad`, branch `claude/vigorous-joliot-f0c3ad`) - Tree: CLEAN at `7016b26c` (only `v4-smoke.log` untracked — session scratch, delete freely). Full suite green: **3,948 tests** (385 Net + 425 UI + 713 App + 2,425 Core, +4 pre-existing skips). - **R1 ✅ R2 ✅ R3 ✅ shipped** (trail + commit SHAs recorded in the plan doc's stage entries). **R4: V0–V4 shipped, V5+V6 remain.** - A smoke client was launched with the V4 build (`ACDREAM_DUMP_MOTION=1`, log `v4-smoke.log` in the worktree root, UTF-16) — **the user had NOT yet reported an NPC-behavior verdict at handoff time.** First order of business: get that verdict (or relaunch and ask). Watch items: NPC chase/wander legs, turn-to-face, melee-range stop distance (arrival is now retail cylinder distance — the old max() gate is gone). ## What just landed (this session, newest first) | Commit | What | |---|---| | `7016b26c` | **R4-V4** remote cutover: per-remote MoveToManager; RemoteMoveToDriver + PlanMoveToStart DELETED; P4 TargetTracker adapter; retail unpack_movement dispatch (head-interrupt every UM; mt 6–9 never touch the interpreted funnel) | | `a144e873` | **R4-V3** wire: mt 8/9 parsing (TurnToPathData), raw bitfield surfaced, mt-0 sticky trailer | | `addc8e97` | **R4-V2** the verbatim MoveToManager (33 members, 101 tests; seams listed in its final report — ctor takes the interp + 14 providers; the identity-quaternion TDD catch) | | `e0d2492c` | **R4-V1** command-selection family (GetCommand + CanCharge fast-path; MoveToMath incl. the Ghidra-confirmed heading_diff mirror) | | `386b1ce5` | **R4-V0** pins P1–P7 (see below) | | `988304e1` | R4 research base (`docs/research/2026-07-03-r4-moveto/` — decomp, ACE cross-ref, port plan) | | `30115d96` | **W6b fix** — the "press W and stop instantly" live regression (see gotchas) | | `fb7beb70`…`8eff3978` | R3 W0–W7 + R2 Q0–Q6 (see the plan doc's stage trails) | ## NEXT: R4-V5 — LOCAL PLAYER cutover (the delicate one) Spec: `docs/research/2026-07-03-r4-moveto/r4-port-plan.md` §3 V5. **ONE commit, GameWindow + PlayerMovementController, do NOT fan out** (feedback_dont_parallelize_coupled_plan_slices). Shape: - The player gets a MoveToManager bound like remotes (the EnsureRemoteMotionBindings block at GameWindow is the reference wiring; the player's Motion/seams already exist from R3 — DefaultSink is bound, `_playerController.Motion` exposed). - **B.6 auto-walk DELETED**: `DriveServerAutoWalk` + `BeginServerAutoWalk` + the `_autoWalk*` fields + the local mt-6 branch in GameWindow (`BeginServerAutoWalk` call site) + the two relocated constants (`AutoWalkArrivalEpsilon`, `AutoWalkTurnRateFor` — marked V5-scope in the controller). - **P1 pin applies here**: port the missing tail of retail's `CPhysics::SetObjectMovement` gate — DROP any 0xF74C event whose wire `IsAutonomous` byte is set when guid == local player (parsed at `UpdateMotion.cs` isAutonomous, relayed by WorldSession, consumed NOWHERE yet). That's what makes retail's unconditional head-interrupt safe against ACE's mt-0 echo. `V0-pins.md` P1 has the full adjudication + the LoseControlToServer note. This RETIRES the informal "don't cancel on non-MoveTo" comment block in the local UM branch. - TS-36 retires (InterruptCurrentMovement bound for the player); AD-26 retires; AD-27/AP-23 re-anchor. - Movement keys auto-cancel a server MoveTo (retail: the CommandInterpreter DoMotion's cancel bit → interrupt → CancelMoveTo — the edge-driven W6 input already calls DoMotion with ctor-default params whose CancelMoveTo is TRUE, so this may fall out for free — verify, don't assume). - Tests: the V2 harness pattern for the player-side bind; the W6 edge-driven regression suite (`W6EdgeDrivenMovementTests.cs`) is the template for controller-level tests with a real sink. - Then **V6**: register/roadmap/plan/memory sweep (V6 spec in the plan). After V6, R4 is done → R5 (MovementSystem facade + StickyManager + ConstraintManager + TargetManager full port), R6 (per-tick UpdateObjectInternal order — retires the 300ms stop window, PlanFromVelocity AP-80, ObservedOmega AP-76, AP-75/AP-77 velocity adaptations), R7 (outbound cadence TS-33), R8 (audit + final visual pass). ## Pending user verifications (visual passes) 1. **V4 smoke verdict** (see above — NPC movement). 2. **R2+R3 combined visual pass** (task #9): jump instant-Falling engage, walk-off-ledge momentum (NEW behavior), running-in-circles blend, stop settle, strafe pacing (EXPECTED-DIFF: #45's 1.248× factor retired — local now matches remotes), retail-observer view (no rubber-band). Partially confirmed ("works a lot better now") after the W6b fix. ## Load-bearing gotchas from this session (do not relearn these) 1. **BN hoisted-register artifact class** (caused the W6b live bug): the pseudo-C renders pre-call register caches as post-call field reads. `ApplyInterpretedMovement` must ENTRY-CACHE the axis values — the style dispatch's `ApplyMotion(style)` resets forward to Ready (verbatim, raw 0051ea6c) and retail self-heals via the cached fwd re-apply. The 183-case suite can NOT catch sink-result-dependent bugs: its RecordingSink's bool return doesn't mirror the real MotionTableDispatchSink (style dispatches return TRUE → the resetting state-write runs). Regression suite: `W6EdgeDrivenMovementTests.cs`. 2. **The dispatch RESULT gates queue+state writes** (W5 discovery): `IInterpretedMotionSink.ApplyMotion/StopMotion` return bool for this reason — documented on the interface. 3. **Retail's echo discriminator is the wire autonomous byte** (V0 P1): not a workaround — `CPhysics::SetObjectMovement` 0x00509690's final branch. Port it in V5, don't invent gating. 4. **MoveToManager entry points never drain pending_actions** — re-issues MUST route through `PerformMovement` (V2 finding, tested). 5. **`default(Quaternion)` is zero, not identity** — heading math reads 90° off a zero quat (V2 TDD catch; `IdentityPosition` constant). 6. **fail_progress_count is WRITE-ONLY in retail** — there is no MoveTo give-up threshold; do not invent one. 7. **Delegation pattern that works**: sonnet implementers against committed pinned specs (research → pins with adversarial verify → implement → orchestrator independently verifies scope + suite + spot-checks against the raw decomp → commit). GameWindow/controller coupled surgery stays with the orchestrator. Workflow tool for research fan-outs (r3/r4 extraction + pin workflows are the templates). 8. **py (not python) exists on this box**; logs from Tee-Object are UTF-16; heredocs with quotes inside python strings break — write scripts to the scratchpad with the Write tool and run `py file.py`. 9. **Ghidra MCP** (port 8080/8081, needs the user to open patchmem.gpr): used live this session for heading_diff/get_desired_heading (`ghidra-confirmations.md`). Ask the user to open it when a BN garble blocks a pin. 10. **ACE session care**: graceful client close (CloseMainWindow) or ACE holds the session ~3 min (exit 29 on relaunch). Don't kill clients the user launched — ask. ## Key file map (R4 additions) - `src/AcDream.Core/Physics/Motion/MoveToManager.cs` + `MoveToNode.cs` — the V2 port (seams doc'd on the ctor). - `src/AcDream.Core/Physics/Motion/MoveToMath.cs` — heading/cylinder math + `OriginToWorld` (relocated from the deleted RemoteMoveToDriver). - `src/AcDream.Core/Physics/Motion/MovementParameters.cs` — `GetCommand` / `FromWire` / `FromWireTurnTo` / `GetDesiredHeading` / `TowardsAndAway`. - `docs/research/2026-07-03-r4-moveto/` — decomp + ACE cross-ref + port plan + `V0-pins.md` + `ghidra-confirmations.md`. - GameWindow: `EnsureRemoteMotionBindings` (the full per-remote wiring reference), the remote UM dispatch (mt 6–9 vs mt-0 funnel), the per-tick tracker+UseTime block.