# Phase B.6 — Suppress MoveToState during inbound auto-walk Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Stop sending outbound `MoveToState` while ACE's server-initiated auto-walk is driving the player, then retire the Commit B workarounds that compensated for the resulting `MoveToChain` cancellation. ACE's `TryUseItem` callback fires on arrival; client sends Use exactly once. **Architecture:** One-line guard against the misleading wire packet, two retry-assignment deletions, one revert of the AP-cadence gate to retail's narrow shape. No new files, no new tests (behavioral change is wire-level integration; covered by existing Core.Net suite + user visual verify). **Tech Stack:** C# .NET 10. Edits touch `AcDream.App/Rendering/GameWindow.cs`, `AcDream.App/Input/PlayerMovementController.cs`, `docs/ISSUES.md`. **Spec:** [docs/superpowers/specs/2026-05-16-phase-b6-suppress-movetostate-during-inbound-autowalk-design.md](../specs/2026-05-16-phase-b6-suppress-movetostate-during-inbound-autowalk-design.md). **Retail anchors:** - `Player_Use.cs:205` (ACE) — `CreateMoveToChain(item, (success) => TryUseItem(item, success))`. - `Player_Move.cs:150` (ACE) — chain polls and fires `callback(true)` when within use radius. - `acclient_2013_pseudo_c.txt:700233-700285` — retail `ShouldSendPositionEvent`: narrow gate (cell-or-plane change during sub-interval; frame change after interval; gated on `Contact && OnWalkable`). - `acclient_2013_pseudo_c.txt:700327` — retail `SendPositionEvent`: `(state & 1) != 0 && (state & 2) != 0`. --- ## File structure | File | Responsibility | Touched in tasks | |---|---|---| | `src/AcDream.App/Rendering/GameWindow.cs` | Outbound wire layer. Guard MoveToState build (Task 1); delete two retry assignments + log strings (Task 1); update NotePositionSent call sites to pass contact plane (Task 3). | 1, 3 | | `src/AcDream.App/Input/PlayerMovementController.cs` | Diff-driven cadence state + the auto-walk overlay. Add `_lastSentContactPlane` field; extend `NotePositionSent` signature; replace per-frame `positionChanged` gate with retail's narrow gate (Task 3). | 3 | | `docs/ISSUES.md` | Close `#63` and `#74` to "Recently closed" (Task 5). | 5 | --- ## Task 1: Suppress outbound MoveToState during server auto-walk + delete the retry workarounds **Files:** - Modify: `src/AcDream.App/Rendering/GameWindow.cs` line ~6410 (the MoveToState send block). - Modify: `src/AcDream.App/Rendering/GameWindow.cs` line ~9203 (SendUse far-range path). - Modify: `src/AcDream.App/Rendering/GameWindow.cs` line ~9265 (SendPickUp far-range path). ### Step 1: Guard the outbound MoveToState build Find the block at `GameWindow.cs:6410` that reads: ```csharp if (result.MotionStateChanged) { // HoldKey axis values — retail enum (holtburger types.rs HoldKey): ``` Change the condition to: ```csharp // 2026-05-16 (Phase B.6): suppress outbound MoveToState while // ACE's server-initiated auto-walk is driving the player. // Synthesized Forward+Run input in ApplyAutoWalkOverlay leaks // to MotionStateChanged=true; sending the resulting "user is // RunForward" wire packet makes ACE cancel its own MoveToChain // (Player_Move.cs:150 callback never fires). Retail and // holtburger walk the body locally during inbound MoveToObject // WITHOUT sending an outbound MoveToState — AutonomousPosition // alone is enough for ACE's WithinUseRadius poll. if (result.MotionStateChanged && !_playerController.IsServerAutoWalking) { // HoldKey axis values — retail enum (holtburger types.rs HoldKey): ``` (Only the `if` line changes; the comment above is new. Leave the rest of the block untouched.) - [ ] **Step 2: Delete the SendUse far-range retry assignment** Find the SendUse method's far-range block. Search: ``` grep -n "Far range:" src/AcDream.App/Rendering/GameWindow.cs ``` Expected: line ~9197. The block reads (paraphrased — find the exact text in the file): ```csharp // Far range: send Use immediately so ACE has the request, // AND queue an arrival re-send. Issue #63 (server-initiated // MoveToObject not honored) means ACE's first-Use response // is dropped as too-far and ACE doesn't re-poll // WithinUseRadius when the speculative local walk gets us in // range. The arrival re-send fires a second Use packet once // the body reaches the target — at which point ACE accepts // and executes the action. The retail-faithful path is to // honor MoveToObject and let ACE complete the Use server- // side; until #63 lands, this client-side retry is the // workaround that keeps far-range Use working. var seq = _liveSession.NextGameActionSequence(); var body = AcDream.Core.Net.Messages.InteractRequests.BuildUse(seq, guid); _liveSession.SendGameAction(body); _pendingPostArrivalAction = (guid, false); Console.WriteLine($"[B.4b] use guid=0x{guid:X8} seq={seq} (queued for arrival re-send pending #63)"); ``` Replace with: ```csharp // Far range: send Use ONCE. ACE's CreateMoveToChain // (Player_Use.cs:205) holds a callback (TryUseItem) and fires // it server-side when WithinUseRadius passes during the // MoveToChain poll (Player_Move.cs:150). No client-side retry // needed — the Phase B.6 MoveToState-suppression fix // (GameWindow.cs:6410) keeps ACE's chain alive during the // walk. var seq = _liveSession.NextGameActionSequence(); var body = AcDream.Core.Net.Messages.InteractRequests.BuildUse(seq, guid); _liveSession.SendGameAction(body); Console.WriteLine($"[B.4b] use guid=0x{guid:X8} seq={seq}"); ``` (Removes the `_pendingPostArrivalAction = (guid, false);` line and trims the log to drop the `(queued for arrival re-send pending #63)` suffix.) - [ ] **Step 3: Delete the SendPickUp far-range retry assignment** Find the SendPickUp method's far-range block. Search: ``` grep -n "Far range: same arrival-retry pattern" src/AcDream.App/Rendering/GameWindow.cs ``` Expected: line ~9255. Replace the block: ```csharp // Far range: same arrival-retry pattern as SendUse — fire // PickUp immediately AND queue for arrival re-send. ACE's // first PickUp is dropped if we're outside the use-radius // and isn't re-polled (issue #63 workaround). var seq = _liveSession.NextGameActionSequence(); var body = AcDream.Core.Net.Messages.InteractRequests.BuildPickUp( seq, itemGuid, _playerServerGuid, placement: 0); _liveSession.SendGameAction(body); _pendingPostArrivalAction = (itemGuid, true); Console.WriteLine($"[B.5] pickup item=0x{itemGuid:X8} container=0x{_playerServerGuid:X8} seq={seq} (queued for arrival re-send pending #63)"); ``` With: ```csharp // Far range: send PickUp ONCE. Same auto-fire-via-MoveToChain // callback pattern as SendUse — ACE's chain fires // PutItemInContainer/Move server-side when in range. No // client-side retry; Phase B.6 MoveToState suppression keeps // ACE's chain alive. var seq = _liveSession.NextGameActionSequence(); var body = AcDream.Core.Net.Messages.InteractRequests.BuildPickUp( seq, itemGuid, _playerServerGuid, placement: 0); _liveSession.SendGameAction(body); Console.WriteLine($"[B.5] pickup item=0x{itemGuid:X8} container=0x{_playerServerGuid:X8} seq={seq}"); ``` - [ ] **Step 4: Build** ``` dotnet build src/AcDream.App/AcDream.App.csproj -c Debug ``` Expected: 0 errors, 0 warnings. The deletions remove the only assignment of `_pendingPostArrivalAction` for far-range paths; the close-range path still assigns it (line ~9191 and ~9258). The field declaration at line ~799 stays. - [ ] **Step 5: Run existing tests** ``` dotnet test tests/AcDream.Core.Net.Tests/AcDream.Core.Net.Tests.csproj -c Debug --nologo dotnet test tests/AcDream.Core.Tests/AcDream.Core.Tests.csproj -c Debug --nologo ``` Expected: Core.Net 294/294 pass. Core 1073/1081 pass (baseline; 8 pre-existing physics failures unchanged). - [ ] **Step 6: Visual verification (user-driven)** User stops any running AcDream.App gracefully via the close-window button, waits ~3 seconds, launches: ```powershell $env:ACDREAM_DAT_DIR = "$env:USERPROFILE\Documents\Asheron's Call" $env:ACDREAM_LIVE = "1" $env:ACDREAM_TEST_HOST = "127.0.0.1" $env:ACDREAM_TEST_PORT = "9000" $env:ACDREAM_TEST_USER = "testaccount" $env:ACDREAM_TEST_PASS = "testpassword" $env:ACDREAM_DEVTOOLS = "1" $env:ACDREAM_PROBE_AUTOWALK = "1" dotnet run --project src\AcDream.App\AcDream.App.csproj --no-build -c Debug 2>&1 | Tee-Object -FilePath "launch.log" ``` Scenarios to test: 1. **Far-range Use NPC.** Double-click a Royal Guard / Pathwarden ~8 m away. Expected log shape: ``` [B.4b] use guid=0x... seq=X [autowalk-mt] mt=0x06 isMoveTo=True ... [autowalk-begin] dest=... [autowalk-end] reason=arrived ``` Expected: NO `[B.4b] use-deferred` follow-up. Dialogue fires on arrival from ACE's `TryUseItem` callback. 2. **Far-range PickUp.** F-key a ground item ~5 m away. Same shape — single `[B.5] pickup` line, no `pickup-deferred`, item enters inventory. 3. **Close-range Use NPC behind player.** Within 3 m, press R. Body turns 180°. Expected log: ``` [B.4b] use deferred (close-range, turn-first) guid=0x... [autowalk-end] reason=arrived [B.4b] use-deferred guid=0x... seq=X ``` (Close-range deferred path is unchanged; `use-deferred` is correct here.) 4. **Open inn door from across the room.** ONE `[B.4b] use` line, no retry, door opens once. 5. **User takes manual control mid-auto-walk.** Click far NPC → press W during the walk. Auto-walk cancels (`EndServerAutoWalk("user-input")`). Action does NOT fire on arrival. **STOP and wait for user confirmation that scenarios 1–5 pass.** - [ ] **Step 7: Commit** ```bash git add src/AcDream.App/Rendering/GameWindow.cs git commit -m "$(cat <<'EOF' fix(retail): suppress outbound MoveToState during inbound auto-walk Phase B.6 — retire the Commit B issue-#63 workarounds by plugging the underlying leak that caused them. ApplyAutoWalkOverlay synthesizes Forward+Run input during inbound MoveToObject so the existing motion-interpreter pipeline drives body position + animation locally. That synth set MotionStateChanged=true, so the outbound wire layer (GameWindow.cs:6410) built a MoveToState packet with forwardCommand=RunForward and sent it to ACE. ACE read the packet as "user took manual control" and cancelled its own CreateMoveToChain (Player_Use.cs:205 → Player_Move.cs:150), so the TryUseItem callback never fired on arrival. Our workaround sent Use a second time at local-arrival to bypass ACE's cancelled chain. Fix: one-line guard. The MoveToState send only fires when !_playerController.IsServerAutoWalking. AutonomousPosition keeps flowing during the walk (so ACE's WithinUseRadius poll sees the player approach); ACE's chain runs uninterrupted; callback fires when in range. Retail and holtburger (simulation.rs:178-206) follow the same pattern — no outbound MoveToState during inbound MoveToObject. Deletes the retry workarounds: - SendUse far-range: `_pendingPostArrivalAction = (guid, false);` + the `(queued for arrival re-send pending #63)` log - SendPickUp far-range: same shape Close-range turn-first deferred path (separate code, retail-faithful pre-callback rotation) is unchanged. Spec: docs/superpowers/specs/2026-05-16-phase-b6-suppress-movetostate-during-inbound-autowalk-design.md Plan: docs/superpowers/plans/2026-05-16-phase-b6-suppress-movetostate-during-inbound-autowalk.md Co-Authored-By: Claude Opus 4.7 (1M context) EOF )" ``` --- ## Task 2: Visual checkpoint — confirm Task 1's fix before touching cadence This is not a code task. The Step 6 visual verification in Task 1 establishes that ACE's `MoveToChain` callback fires correctly with the MoveToState suppression in place. Only proceed to Task 3 if all five scenarios pass. If any regresses, STOP and revert Task 1's commit before continuing. --- ## Task 3: Revert AP cadence to retail's narrow gate **Files:** - Modify: `src/AcDream.App/Input/PlayerMovementController.cs` line ~254 (field block), line ~441-449 (`NotePositionSent`), line ~1240-1275 (the gate logic), line ~289-296 (`AutoWalkArrived` doc-comment cleanup). - Modify: `src/AcDream.App/Rendering/GameWindow.cs` line ~6450 (MTS NotePositionSent call), line ~6476 (AP NotePositionSent call). - [ ] **Step 1: Add `_lastSentContactPlane` field** In `PlayerMovementController.cs`, find the diff-tracking field block (around line 535-540 — search for `_lastSentPos`): ```csharp private System.Numerics.Vector3 _lastSentPos; private uint _lastSentCellId; private float _lastSentTime; private bool _lastSentInitialized; ``` Add a new field immediately after `_lastSentCellId`: ```csharp private System.Numerics.Vector3 _lastSentPos; private uint _lastSentCellId; private System.Numerics.Plane _lastSentContactPlane; private float _lastSentTime; private bool _lastSentInitialized; ``` - [ ] **Step 2: Extend `NotePositionSent` to accept the contact plane** Find `NotePositionSent` in `PlayerMovementController.cs` (around line 441). Replace: ```csharp public void NotePositionSent(System.Numerics.Vector3 worldPos, uint cellId, float nowSeconds) { _lastSentPos = worldPos; _lastSentCellId = cellId; _lastSentTime = nowSeconds; _lastSentInitialized = true; } ``` With: ```csharp /// /// 2026-05-16 (Phase B.6). Called by the network outbound layer /// after every AutonomousPosition or MoveToState that carries the /// player's position. Resets the diff-driven heartbeat clock so the /// next evaluation requires a fresh /// state change. Mirrors retail's SendPositionEvent /// (acclient_2013_pseudo_c.txt:700345-700348) which writes /// `last_sent_position`, `last_sent_position_time`, and /// `last_sent_contact_plane` after every send. /// public void NotePositionSent(System.Numerics.Vector3 worldPos, uint cellId, System.Numerics.Plane contactPlane, float nowSeconds) { _lastSentPos = worldPos; _lastSentCellId = cellId; _lastSentContactPlane = contactPlane; _lastSentTime = nowSeconds; _lastSentInitialized = true; } ``` - [ ] **Step 3: Replace the per-frame gate with retail's narrow gate** Find the cadence block in `PlayerMovementController.cs` (around line 1240-1275 — search for `retail diff-driven AP cadence`). Replace the block starting at the `// 2026-05-16 — retail diff-driven AP cadence` comment through the `HeartbeatDue =` line with: ```csharp // 2026-05-16 (Phase B.6) — retail-faithful AP cadence per // CommandInterpreter::ShouldSendPositionEvent at // acclient_2013_pseudo_c.txt:700233-700285. Two-branch: // // Branch 1 — interval NOT yet elapsed (< 1 sec since // last send): send only if cell changed OR contact-plane // changed (mid-walk events that matter). // // Branch 2 — interval HAS elapsed (>= 1 sec): send only // if cell OR position frame changed. Truly idle = no // send (retail's `last_sent.frame == player.frame` check // at line 700248-700265). // // SendPositionEvent (line 700327) gates the actual send on // (state & 1) != 0 && (state & 2) != 0 — Contact AND // OnWalkable both set. We mirror that gate here so airborne // and wall-contact-without-walkable states suppress AP // entirely; MoveToState carries jump/fall snapshots while // airborne. // // Effective rates: // - Truly idle (grounded, no movement) : 0 Hz // - Smooth movement (no cell/plane changes) : ~1 Hz (interval-driven) // - Cell crossings + stair/hill steps : per-event // - Airborne : 0 Hz // // Bootstrap: when NotePositionSent has never been called // (_lastSentInitialized=false), treat every frame as // "anything to send" so the first AP gets a chance to fire. bool intervalElapsed = !_lastSentInitialized || (_simTimeSeconds - _lastSentTime) >= HeartbeatInterval; bool cellChanged = !_lastSentInitialized || _lastSentCellId != CellId; bool planeChanged = !_lastSentInitialized || !_lastSentContactPlane.Equals(_body.ContactPlane); bool frameChanged = !_lastSentInitialized || !ApproxPositionEqual(_lastSentPos, _body.Position); bool sendThisFrame = intervalElapsed ? (cellChanged || frameChanged) : (cellChanged || planeChanged); // Grounded-on-walkable gate per acclient_2013_pseudo_c.txt:700327 // (`(state & 1) != 0 && (state & 2) != 0`). Both flags must be // set simultaneously, NOT a bitwise-OR mask test. bool groundedOnWalkable = _body.InContact && _body.OnWalkable; HeartbeatDue = groundedOnWalkable && sendThisFrame; ``` - [ ] **Step 4: Update the MTS site to pass `contactPlane`** In `GameWindow.cs`, find the MoveToState `NotePositionSent` call (around line 6450). Replace: ```csharp _playerController.NotePositionSent( worldPos: _playerController.Position, cellId: _playerController.CellId, nowSeconds: _playerController.SimTimeSeconds); ``` With: ```csharp _playerController.NotePositionSent( worldPos: _playerController.Position, cellId: _playerController.CellId, contactPlane: _playerController.PhysicsBody.ContactPlane, nowSeconds: _playerController.SimTimeSeconds); ``` If `_playerController.PhysicsBody` doesn't exist as a public accessor, search: ``` grep -n "public.*_body\|public PhysicsBody\|public.*Body" src/AcDream.App/Input/PlayerMovementController.cs ``` If no public accessor, add one in `PlayerMovementController.cs` near the existing public properties (around line 130-160): ```csharp /// 2026-05-16. Read-only access to the controller's /// physics body — needed by the network outbound layer to stamp /// the contact plane into NotePositionSent. public AcDream.Core.Physics.PhysicsBody PhysicsBody => _body; ``` (Verify the field name is `_body` first — search `private.*PhysicsBody`.) - [ ] **Step 5: Update the AP site to pass `contactPlane`** Find the AutonomousPosition `NotePositionSent` call (around line 6476). Apply the same edit: ```csharp _playerController.NotePositionSent( worldPos: _playerController.Position, cellId: _playerController.CellId, contactPlane: _playerController.PhysicsBody.ContactPlane, nowSeconds: _playerController.SimTimeSeconds); ``` - [ ] **Step 6: Build** ``` dotnet build src/AcDream.App/AcDream.App.csproj -c Debug ``` Expected: 0 errors. Any compile error here is a wiring mistake — the field name (`_body` vs `_physicsBody`), the property accessor, or the `Plane` namespace. - [ ] **Step 7: Run existing tests** ``` dotnet test tests/AcDream.Core.Net.Tests/AcDream.Core.Net.Tests.csproj -c Debug --nologo dotnet test tests/AcDream.Core.Tests/AcDream.Core.Tests.csproj -c Debug --nologo ``` Expected: Core.Net 294/294, Core 1073/1081 (baseline unchanged). - [ ] **Step 8: Visual verification (user-driven)** User restarts the client (graceful close + ~3 sec wait + launch). Runs scenarios: 1. **Idle test.** Stand still on flat ground in Holtburg for 10 sec. Watch the dev console / `[autowalk-up]` lines or any outbound packet trace. - Old behavior: 1 AP/sec heartbeat. - New behavior: ZERO outbound packets while truly idle. 2. **Smooth-running test.** Hold W and run in a straight line for 5 sec on flat ground (no cell crossings). - Old behavior: ~60 AP/sec (per-frame while position changed). - New behavior: ~1 AP/sec (interval-driven; cell/plane don't change every frame). - **The character should still appear to remote observers as moving smoothly** — ACE's dead-reckoning fills in the gaps between sparse APs. If remote view becomes jittery, the cadence is too sparse and we'll need to tune. 3. **Cell-crossing test.** Run across a landblock boundary (every ~192 m). Should see a burst of AP packets at the crossing — both the `cellChanged` path and the `intervalElapsed && frameChanged` path can fire here. 4. **Far-range Use re-test.** Repeat Task 1 Step 6 scenario 1 (far-range NPC Use). Should still work — ACE's `MoveToChain` callback fires on arrival, dialogue plays, single Use packet. 5. **Hill / stair test.** Walk up a slope or stairs. Contact-plane changes per step → sub-interval AP sends fire on plane change. Behavior should look smooth to remote observers. **STOP and wait for user confirmation that scenarios 1–5 pass.** If scenario 2 produces visible remote-jitter, the spec's `ApproxPositionEqual` epsilon may need tightening, or we may need a higher heartbeat rate; document the finding and tune before continuing. - [ ] **Step 9: Commit** ```bash git add src/AcDream.App/Input/PlayerMovementController.cs src/AcDream.App/Rendering/GameWindow.cs git commit -m "$(cat <<'EOF' fix(retail): revert AP cadence to retail's narrow gate Phase B.6 — closes #74. With the MoveToState suppression fix in place, the per-frame "send while moving" cadence is no longer needed to compensate for ACE's MoveToChain cancellation. Reverts to retail's two-branch gate per CommandInterpreter::ShouldSendPositionEvent at acclient_2013_pseudo_c.txt:700233-700285: Interval NOT elapsed (< 1 sec): send if cell or contact-plane changed. Interval elapsed (>= 1 sec): send if cell or position frame changed. Bootstrap fires every frame until the first NotePositionSent. Grounded-on-walkable gate (Contact && OnWalkable) unchanged from 700327. Effective rates: Truly idle (grounded, no movement) : 0 Hz (was 1 Hz) Smooth straight-line run : ~1 Hz (was ~60 Hz) Cell crossings + stair/hill steps : per-event Airborne : 0 Hz (unchanged) Adds _lastSentContactPlane field + extends NotePositionSent to accept System.Numerics.Plane. Adds PhysicsBody public accessor so the wire layer can read _body.ContactPlane to pass into NotePositionSent. Both outbound sites (MoveToState at GameWindow.cs:6450, AP at ~6476) updated to pass the plane. Co-Authored-By: Claude Opus 4.7 (1M context) EOF )" ``` --- ## Task 4: Visual checkpoint — confirm Task 3's cadence revert before closing issues Same as Task 2. Only proceed to Task 5 if all five scenarios in Task 3 Step 8 pass cleanly. --- ## Task 5: Close issues #63 and #74 **Files:** - Modify: `docs/ISSUES.md`. - [ ] **Step 1: Move issue #63 to "Recently closed"** Find `## #63 — Server-initiated auto-walk (MoveToObject) not honored` in `docs/ISSUES.md` (around line 425). Currently `Status: OPEN`. Cut the entire `#63` block from the active issues section and paste it into the "Recently closed" section near the bottom of the file with these changes: 1. Change the title line from: ```markdown ## #63 — Server-initiated auto-walk (MoveToObject) not honored ``` to: ```markdown ## #63 — [DONE 2026-05-16 · ``] Server-initiated auto-walk (MoveToObject) not honored ``` (Replace `` with the actual commit SHA from Task 1. Get it via `git log --oneline -5`.) 2. Change `Status: OPEN` to `Status: DONE`. 3. Append a "Resolution" paragraph after the existing "Acceptance": ```markdown **Resolution (2026-05-16):** B.6 slice 2 (2026-05-14) shipped the inbound-MoveToObject auto-walk handling. The remaining "MoveToChain callback never fires on arrival" half was tracked to ApplyAutoWalkOverlay's synthesized Forward+Run input leaking to the wire as an outbound MoveToState packet (forwardCommand=RunForward), which ACE read as "user took manual control" and used to cancel its own MoveToChain. Fix in `` adds a single guard at `GameWindow.cs:6410`: outbound MoveToState only fires when `!_playerController.IsServerAutoWalking`. With ACE's chain running uninterrupted, the `TryUseItem` callback (Player_Use.cs:205) fires server-side on arrival; no client retry needed. Retired the `_pendingPostArrivalAction` retry workarounds from SendUse + SendPickUp far-range paths. ``` - [ ] **Step 2: Move issue #74 to "Recently closed"** Find `## #74 — AP cadence is per-frame-while-moving, more chatty than retail`. Same shape: cut the block, paste in "Recently closed", change title to: ```markdown ## #74 — [DONE 2026-05-16 · ``] AP cadence is per-frame-while-moving, more chatty than retail ``` Change `Status: OPEN` to `Status: DONE`. Append: ```markdown **Resolution (2026-05-16):** With #63 closed (MoveToState no longer cancels ACE's MoveToChain), the per-frame-while-moving cadence workaround is unnecessary. Reverted to retail's two-branch ShouldSendPositionEvent gate per `acclient_2013_pseudo_c.txt:700233-700285` in ``. Effective rate during smooth motion drops from ~60 Hz to ~1 Hz; truly idle drops from 1 Hz to 0 Hz. Cell crossings + contact-plane changes still fire mid-interval. Matches retail bit-for-bit. ``` - [ ] **Step 3: Commit** ```bash git add docs/ISSUES.md git commit -m "$(cat <<'EOF' docs: close #63 (MoveToObject not honored) + #74 (AP chattier than retail) Both retired by Phase B.6. #63 fixed in (MoveToState suppression during inbound auto-walk + retry workaround retirement). #74 fixed in (AP cadence reverted to retail's two-branch ShouldSendPositionEvent gate now that the workaround that needed per-frame APs is gone). Co-Authored-By: Claude Opus 4.7 (1M context) EOF )" ``` (Replace `` and `` with the actual SHAs.) --- ## Self-review checklist **Spec coverage:** | Spec section | Plan task | |---|---| | Wire-level changes: `IsServerAutoWalking` guard at `GameWindow.cs:6410` | Task 1 Step 1 ✅ | | Far-range Use retry deletion | Task 1 Step 2 ✅ | | Far-range PickUp retry deletion | Task 1 Step 3 ✅ | | Log string cleanup | Task 1 Steps 2+3 ✅ | | `_lastSentContactPlane` field + `NotePositionSent` signature | Task 3 Steps 1+2 ✅ | | Retail-narrow gate | Task 3 Step 3 ✅ | | MTS site contactPlane wiring | Task 3 Step 4 ✅ | | AP site contactPlane wiring | Task 3 Step 5 ✅ | | Testing plan (visual scenarios) | Task 1 Step 6 + Task 3 Step 8 ✅ | | Close `#63` + `#74` | Task 5 ✅ | | Out-of-scope `#75` (status messages) | Filed as deferred — not in this plan ✅ | **Placeholder scan:** No "TBD" / "implement later" / vague phrases. Every step has actual code or actual commands. **Type consistency:** - `IsServerAutoWalking` referenced Task 1 Step 1 — already exists in code (verified at PlayerMovementController.cs:273). ✅ - `_lastSentContactPlane : System.Numerics.Plane` defined Task 3 Step 1, used Task 3 Steps 2+3. ✅ - `NotePositionSent(Vector3, uint, Plane, float)` defined Task 3 Step 2, called Task 3 Steps 4+5. ✅ - `_playerController.PhysicsBody` property defined Task 3 Step 4 (conditional add if missing), used Task 3 Steps 4+5. ✅ - `_lastSentPos`, `_lastSentCellId`, `_lastSentTime`, `_lastSentInitialized` — pre-existing from Commit B. ✅ **Risk / rollback:** - Task 1 commit: simple revert restores the workaround. - Task 3 commit: simple revert restores the per-frame cadence. - Task 5 commit: docs-only; trivial. Each task is independently revertable. If Task 3 introduces remote-view jitter (scenario 2), revert Task 3 and re-evaluate (e.g., dial down `HeartbeatInterval` from 1 s to 0.5 s). --- ## Execution handoff Plan saved to `docs/superpowers/plans/2026-05-16-phase-b6-suppress-movetostate-during-inbound-autowalk.md`. Two options for the controller: 1. **Subagent-Driven (recommended)** — Dispatch fresh subagent per task. Two-stage review (spec compliance + code quality) between tasks. ~3 implementer + 6 reviewer dispatches. 2. **Inline Execution** — Execute tasks in this session using `superpowers:executing-plans`. Two visual-verify checkpoints (between Task 1 & Task 3, between Task 3 & Task 5). Given the small scope (~30 LOC of behavior change + docs) and the two mandatory user-driven visual checkpoints, inline execution may be simpler — the subagent overhead exceeds the implementation time for tasks this small.