# C4 route 4b — scoping, a three-way split, and two doc corrections (2026-08-04) Scoped at HEAD `44830a0e` (route 4a landed). **4b is 4-6x route 4a and must be split again.** This also corrects two errors in documents I wrote yesterday. ## Correction 1 — the plan is wrong about AP-135 `docs/plans/2026-08-02-placement-cutover.md` says AP-135 *"retires with 4b's transition machinery, not before."* **That is wrong.** AP-135's own retirement condition is *"retire together with the free-fall sweep gate, when the remote arc is resolved by the same transition machinery the local player uses"* — that gate is `RuntimeRemotePhysicsUpdater.cs:342`, which 4b does not touch. Worse, AP-135's sites are the **airborne no-op** branches, which are **4a-owned dispositions**, not 4b's. 4b owns far-snap, teleport and cell-less. **AP-135 stays open after 4b.** The trap is real: its two writes sit physically inside `OnPosition`, which 4b rewrites heavily, so an implementer will assume they go. The contract must say they do not. ## Correction 2 — retail has ONE `ConstrainTo` on the remote arm, not two My route-4 scoping described the third divergence as retail *"re-arming after `teleport_hook`'s `UnConstrain`"*, implying a distinct remote-teleport site. There is none. Retail has exactly one: `ConstrainTo` @0x00454272, and **all three** nonzero-returning `MoveOrTeleport` branches — teleport (returns 1 @0x00516438), near-interpolate (@0x005163BE), far-snap (@0x005163E8) — funnel through it. Consequence for the contract: **4b must not add a second arming site.** The single post-operation arm 4a introduced becomes the ONLY arm, with `remotePlacementRequired` folded into it rather than returning ahead of it. The behaviour claim still stands: today a remote hard-teleport arms the leash nowhere, because the `remotePlacementRequired` block returns before every arming site while `RemoteTeleportHook` has already called `UnConstrain`. Severity is narrow — the next Position re-arms ~100-200 ms later — except for a remote that teleports and then stands still, since ACE stops broadcasting for a stationary entity. ## The new failure mode 4b must not create **A Runtime `DeferredCell` park WITHDRAWS the entity from the world.** `ParkDeferred` sets `body.InWorld = false`, clears `Active`, suspends the object clock, calls `WithdrawCanonical`, and publishes a `Withdraw`. `Forget`-on-every-accepted-Position then kills the park **without restoring any of it**. `CancelCoreDeferred` removes the operation and rewrites the `Withdraw` into a `Discard`; it does not set `InWorld` back, resume the clock, or re-enter residency. So: packet N parks E (now invisible AND intangible). Packet N+1 ~150 ms later Forgets the park. **If N+1 classifies `Interpolate` — near, in contact, committed cell — no placement runs and E stays withdrawn indefinitely.** The sequence "remote appears beyond 96 m, walks toward you, crosses inside 96 m" produces it. That is the #184 class through a third door, and it is worse than #184 because the entity is intangible too. **Required direction: refuse rather than park.** A remote whose destination is not placeable keeps its last committed pose and waits for the next packet — the next packet IS the retry, because remote Positions are a 5-10 Hz stream. That is retail-shaped (retail's world is fully resident; "arrived but not placeable" is unrepresentable) and avoids withdrawal-restore surgery inside a 5,652-line class. **Do NOT port route 2's re-issue funnel.** It exists because a ForcePosition is a one-shot correction ACE never repeats. Re-issuing remote packet N after N+1 has merged would apply a pose the newer packet already superseded — the same class of route-2-to-4a transfer error that cost a review round, in reverse. **Do NOT port the ack machinery.** Retail's remote arm has no `SendPositionEvent`; the whole `PositionEventOwed` apparatus has no analogue. ## Two traps that will bite an implementer **T1 — `remotePlacementRequired` and the classifier's `cellless` are different predicates.** `remotePlacementRequired` derives from `wasCellless` measured BEFORE the merge, plus `projectionRequiresTeleportHook` — which on the graphical host includes `!IsSpatiallyVisible`, so **it fires for every remote that is not currently visible**, a routine hot path rather than a teleport rarity. The classifier's `cellless` reads `FullCellId` AFTER the merge. They disagree in both directions. Reconciling them is a design decision, not a rename. **T2 — deleting the legacy near/far blocks removes the only handler for `null` and `Rejected*`.** `ClassifyRemoteAcceptedPosition` returns null whenever `_playerController` is null — **which during the login window is every remote packet.** Today those fall into the legacy blocks and hard-snap. Delete the blocks without a replacement and remotes will not move at all until the local controller exists. Retail has no analogue; this needs a stated acdream policy. ## The split Estimate: **1,300-2,200 non-comment production lines, centred ~1,700**, plus ~2,500-3,500 lines of test work. That is 4-6x route 4a and ~2x route 2 — the two largest landings in this campaign, which took 4 and 5 review rounds. - **4b-1 — infrastructure, no remote behaviour change (~700-1,000 lines).** The per-entity remote placement owner (route 2's controller minus the ack and re-issue funnel, plus an N-way pending map and ledger); the service-window guard (Runtime interface + a graphical implementation over `GpuWorldState`/`StreamingController`, which does not exist today); the refuse-rather-than-park policy; N3's headless `RetryPending` pump; and the `report.json` parked-count wiring #277 asks for. **No production caller, or called for zero classifications.** Gate: focused Runtime tests, Release suite, existing connected routes unchanged, and a proof that `ParkCollisionResidents`'s overlap throw is unreachable. *This is where the park semantics get decided and reviewed on their own, without a behaviour change confusing the signal.* - **4b-2 — the far branch only (~350-500 lines).** `SetPositionSimple`, `PlayerDistance >= 96 m`. Deletes both duplicated `96f`/`4f` constant pairs and both fabricated `Vector3.Zero` reads. Trivially observable. - **4b-3 — teleport and cell-less (~400-700 lines).** Deletes `RemoteTeleportController` (605), `RemoteTeleportPlacement` (85), `RemoteShadowPlacementSynchronizer` (49) and 7 wiring sites, plus 1,709 lines of their tests. Largest blast radius; needs the two-client teleport gate. If the campaign will not tolerate three landings, merge 4b-2 into 4b-3 — but **keep 4b-1 separate.** It is where the invisible-remote failure decides, and it must not be reviewed at the same time as a 700-line deletion. ## What 4b does NOT do - **AP-131 is not retired.** Its site is one shared merge call serving every entity kind; threading classified flags there necessarily changes the local player's ordinary Apply, which no route owns. Stays for C5. - **AP-135 is not retired.** Correction 1. - **#276 is not closed.** 4b retires its remote-placement half by construction (the canonical transaction owns cell and contact together), but `SeedRemoteSpawnPlacement` is not classification-gated and still fires under `Interpolate`, and the AD-61 local-player settle is untouched. Narrow its scope; do not pin it closed. ## Two collateral hazards - **`ParkCollisionResidents` throws on overlap** — for every spatial root in a retiring landblock prefix holding an active operation. With N remotes holding operations, an ordinary streaming retirement becomes session-fatal. Unreachable today only because steady-state remotes hold no operations. Any 4b design must prove it stays unreachable. - **Lost-cell deadlines leak their family.** `ArmLostFamilyDeadlines` arms root + equipped children; the cancel path clears the root only, and no caller passes `cancelLostFamily: true`. Inert today because the reaper has no production caller — do not let 4b be the commit that makes it live. ## Undetermined — flagged, not guessed 1. Whether `GpuWorldState.IsNearTier` residency is exactly co-extensive with collision publication (gating sites are consistent; the retirement side is unverified). 2. Whether `EntityPhysicsHost.NotifyTeleported()` covers retail's `TargetManager::ClearTarget` @0x00514F1B. 3. Whether the merge can zero a previously-nonzero `FullCellId`, which decides whether classifier-`cellless` is a strict subset of `remotePlacementRequired`. 4. Allocation cost of the current legacy far/teleport path — no gate covers `PhysicsEngine.SetPosition`.