diff --git a/docs/research/2026-07-03-r5-entry-handoff.md b/docs/research/2026-07-03-r5-entry-handoff.md new file mode 100644 index 00000000..39968e42 --- /dev/null +++ b/docs/research/2026-07-03-r5-entry-handoff.md @@ -0,0 +1,112 @@ +# R5 entry handoff — fresh-session entry point (2026-07-03, night) + +Successor to `2026-07-03-r4-verify-session-handoff.md` (now fully closed +out — its items 1-4 are all struck through with outcomes). Worktree +`vigorous-joliot-f0c3ad`, branch `claude/vigorous-joliot-f0c3ad`, tree +CLEAN at `304327b0`. Full suite green: **3,964**. + +## State — the R2-R4 arc is DONE + +R4 shipped + user-verified; the whole follow-up queue closed **today, +all user-verified live**: + +| Item | Outcome | +|---|---| +| #161 landing pose | FIXED `b1cf0102` — retail's apply pass runs with `ModifyInterpretedState=false` (the BN decomp SMEARS the params bitfield store into the mush at raw 305778: `(word & 0x37ff) \| cancelMoveTo<<15 \| disableJump<<17`; ACE MotionInterp.cs:444-449 confirms). Fwd is PRESERVED through a fall; HitGround re-dispatches it → landing link. Plus: HitGround must run BEFORE the DR gravity-bit clear (its verbatim `state&0x400` gate — AP-81); K-fix17 SetCycle blocks deleted; `copy_movement_from` now copies `current_style` (raw 0051e757); ALL apply_current_movement caller polarities fixed (raw arg3 = DisableJumpDuringLink → `(N,0)` = allowJump TRUE). Lesson: [[feedback-bn-decomp-field-names]] (2nd artifact class). | +| #162 glide class | CLOSED, **no adaptation**. User A/B: retail does NOT glide — and post-#161 acdream matches (walk/run-by-distance movetos + clean mid-chain key takeover). Head-interrupt + CancelMoveTo re-audited retail-verbatim; ACE's mt-0 reflections carry the mover's REAL locomotion, so cancels never strand the legs. Evidence: ISSUES Recently-closed #162 + the launch-162 capture analysis. | +| #163 temp diags | STRIPPED `5ebe2be3` ([autowalk-gate]+clock, [autowalk-feed], [MOVETO-CANCEL]). Durable ProbeAutoWalk family kept. | +| R2-R4 visual pass | **PASSED** (jump/ledge momentum, run-in-circles, stop settle, retail-observer view). Filed #165 (remote wall-swallow) + #166 (slope-landing sled — the AD-25+AP-7+TS-4 composite, post-R6 per user). | + +## NEXT: R5 — MovementManager facade + PositionManager (Sticky/Constraint) + TargetManager + +**Retires: TS-39** (MoveToManager `StickTo`/`Unstick` are unbound no-op +seams — sticky movetos complete-and-stop instead of sticking; retail +hands off to `PositionManager::StickTo(tlid, radius, height)`, and every +`PerformMovement` head calls `unstick_from_object` → +`PositionManager::UnStick`) **and AP-79** (the P4 TargetTracker minimal +adapter — GameWindow feeds `HandleUpdateTarget` from the entity table; +retail's TargetManager is a full voyeur-subscription system with +per-target quantums). + +### Retail function inventory (grep'd, addresses verified) + +- **MovementManager** (facade, mostly thin): 00524000 MakeMoveToManager · + 00524020 SetWeenieObject · 005240d0 PerformMovement · 00524170 + move_to_interpreted_state · 005241b0 CancelMoveTo · 005241c0 + EnterDefaultState · 00524260 IsMovingTo · 00524280 motions_pending · + 005242d0 MotionDone · 005242f0 UseTime · 00524300 HitGround · 00524320 + LeaveGround · 00524340/00524350 HandleEnter/ExitWorld · 00524360 + ReportExhaustion · 00524440 unpack_movement · 00524790 + HandleUpdateTarget (routes TargetInfo → MoveToManager). Much of this + facade's BEHAVIOR is already ported piecemeal (funnel, RouteServerMoveTo, + TickRemoteMoveTo, the UseTime/CheckForCompletedMotions slots) — R5's job + is the STRUCTURE: one owner object per entity replacing the GameWindow + wiring sprawl, without changing dispatch behavior pinned by the + 183-case + funnel + moveto suites. +- **StickyManager**: 00555400 UnStick · 00555430 adjust_offset · 00555610 + UseTime · 005556e0 SetPhysicsObject · 00555710 StickTo · 00555780 + HandleUpdateTarget. +- **ConstraintManager**: 00556090 SetPhysicsObject · 005560c0 UnConstrain · + 005560d0 IsFullyConstrained · 00556180 adjust_offset · 00556240 + ConstrainTo. (`jump_is_allowed` already calls an `IsFullyConstrained` + stub — find it before re-porting.) +- **PositionManager** (owns the two): CPhysicsObj::MakePositionManager + 00510210 (also sets transient bit 0x80 + update_time). `stick_to_object` + / `unstick_from_object` on CPhysicsObj are the entry seams (today + Action seams on MotionInterpreter, wired per-entity in GameWindow). +- **TargetManager**: 0051a370 ctor · 0051a4a0 SetTargetQuantum · 0051a4f0 + SendVoyeurUpdate · 0051a5e0 GetInterpolatedPosition · 0051a650 + CheckAndUpdateVoyeur · 0051a6f0 NotifyVoyeurOfEvent · 0051a7e0 + ClearTarget · 0051a830 AddVoyeur · 0051a930 ReceiveUpdate · 0051aa90 + HandleTargetting · 0051ac30 SetTarget · 0051ad90 RemoveVoyeur. + AP-79's seams (`_setTarget`/`_clearTarget`/quantum get/set + + GameWindow's tracker blocks for remotes AND the `_playerMoveToTarget*` + player twin) are the integration points to replace. + +### Already ported — do NOT re-port + +MoveToManager (R4, full incl. the queue model), MotionInterpreter (R3 +full; #161 corrected the apply-pass params), MotionTableManager + +GetObjectSequence (R2), CSequence (R1), RemoteWeenie + PhysicsBody.InWorld +(R4-V5), DefaultSink binding order (bind BEFORE SetPosition — the +`LoginQueue_DrainsToEmpty_UnderProductionFeed` invariant). + +### R5-adjacent verification items (found reading unpack_movement this session) + +1. **Head stance-change dispatch**: raw @00524502-0052452c — before the + type switch, for ALL movement types: `if (InqStyle() != + command_ids[stanceIdx]) CMotionInterp::DoMotion(style, defaults)` (BN + mislabels InqStyle as `CBaseFilter::GetPinVersion`). Verify our UM + path does this for types 6-9 (mt-0 handles style inside the funnel). +2. **mt-0 header flags**: 0x100 → read sticky guid → `stick_to_object` + (raw @00524589); 0x200 → `standing_longjump` store (raw @0052458e). + `UpdateMotion.cs` PARSES both (cited §2f) — verify they're APPLIED + (sticky needs TS-39's machinery = this phase; longjump flag should + reach `MotionInterpreter.StandingLongJump`). +3. **#164** (filed): action-replay dispatches drop the per-action + Autonomous bit (raw 305982 sets params bit 0x1000 → AddAction). Fix + while touching the action path. + +## The load-bearing lessons (compressed) + +- **The queue model** (unchanged from R4): every `pending_motions` + enqueue needs a manager completion partner; one orphan = permanent + MotionsPending wedge. +- **BN smeared bitfield stores**: a fresh local struct + later "mush" + expression = decode the mush BEFORE porting "ctor defaults". ACE's C# + port of the same function is the fastest cross-check. Cost of missing + it: #161 + the wrong W6 entry-cache theory + a wrong pinned test. +- **Tests can pin bug values**: `AirborneBody_NoCycleDispatches_...` + asserted the CLOBBERED fwd (0x40000015) with a comment admitting it was + untested. When flipping behavior, audit what the old assertions PINNED. + +## Session gotchas + +- Launch recipe unchanged (CLAUDE.md). Client LOCKS builds — ask the user + to close it, never kill. Graceful close clears the ACE session in ~5 s. +- PowerShell `Tee-Object` writes UTF-16 → grep the background task's + plain-text output file instead of the tee'd log. +- ACDREAM_DUMP_MOTION=1 + ACDREAM_REMOTE_VEL_DIAG=1 give the full UM/seq + evidence chain ([UM_RAW] hex incl. autonomy byte, [FWD_WIRE], + [SEQSTATE], [MOTIONDONE]); #165's first step is ACDREAM_PROBE_RESOLVE + at a wall.