# C4 route 4 — scoping, and a stop-and-re-plan (2026-08-03) Scoped at HEAD `e3b766d9`, after route 2 shipped. **This is not a contract.** The scoping says route 4 is materially larger than the budget it was given, so the correct next action is a user decision, not an implementation pass. ## The budget, and how it failed After route 2 I pinned a falsifiable bet: *routes 4-7 reuse the seam route 2 built, so their marginal cost should be far lower — well under 400 production lines. If route 4 also costs ~900, the bet is dead and that is the signal to stop and simplify.* **Estimate: 1,500-2,500 lines of new/changed production code**, plus ~1,700 lines of test re-modelling. The bet failed before implementation started. But it failed for a reason worth stating precisely, because it changes what to do about it: **the seam generalises fine. Route 2 was just not a representative unit.** | | route 2 | route 4 | |---|---|---| | entities | 1 (local player) | N (every visible remote) | | dispositions | 1 (ForcePosition) | 4 (SetPosition / SetPositionSimple / Interpolate / NoPositionOperation) | | execution paths | 1 | 2 (canonical SetPosition **and** the interpolation queue) | | teleport hook | never | `BeforePositionOperation` | | constrain phase | never | `AfterPositionOperation` | | duplicate authorities to delete | 2 | **6** | | outbound ack | owns it | none | | frequency | ~never | 5-10 Hz x N | I chose route 2 first because it was simplest to reason about, then used it to calibrate everything after it. That was the error: a one-entity, one-disposition, no-interpolation route is the floor, not the median. ## What is genuinely reusable (the good news) - The begin/prepare/submit/commit chain is **entity-kind agnostic** — no local-player precondition anywhere in `TryBeginExclusiveAuthoredPlacement` → `TryPrepareAndSubmitAuthoredPlacement` → `SubmitPreparedPlacementCore`. - The classifier's remote branches are **retail-exact already** (verified against `CPhysicsObj::MoveOrTeleport` @0x00516330 on all five behaviours). - The placement receipt → presentation path is already entity-agnostic; C3c drives remote Creates through it today. - **All remote physics state already lives in Runtime** (J5.5) — `RemoteMotion`, `EntityPhysicsHost`, the interpolation queue. No assembly boundary to cross. - **`RuntimeAcceptedPositionDriveController` itself is ~15-20% liftable** — the architecture, not the code. It is single-entity and ack-owning by construction (`_pending` is one slot and `RetainPending` throws on a second). ## Four findings that change the campaign plan, not just route 4 **1. Route 4's Create half is already done.** C3b/C3c landed `RuntimeRemoteFirstEntryState` + `RuntimeRemoteBodyDescription` and flipped both hosts' Creates onto the residence conductor. Route 4's remaining work is **steady-state remote Position plus the deletions**. The route title is misleading and will send an implementer hunting for a flip that already happened. **2. AP-131 cannot be retired by route 4.** The row covers two halves: the `!HasAnims` gate on `SetPlacementFrame`, and the FORCE_POSITION branch skipping `unset_parent`. Route 2 did **not** fix the second — it consumes an already-merged snapshot, so a ForcePosition still passes `installPlacementFrame: true, clearParent: true`. Retiring AP-131 needs the unconditional merge gone for *every* Position: remote (route 4), local ForcePosition (a route-2 residual), **and local ordinary-Apply — which no route in the eight-route inventory owns.** Either widen route 4 or move AP-131 to C5 with those two as prerequisites. Pinning "route 4 retires AP-131" pins something route 4 alone cannot deliver. **3. #277's bound breaks.** Its safety argument is that the collision window is strictly larger than ACE's Create-broadcast group, so the far set is empty. That is a statement about *Creates*. A steady-state Position can carry a remote out of the window with no Create at all — a creature wandering off, or the >=96 m far-snap branch by definition targeting something distant. Route 4 introduces a second producer of far parks the radius argument never covered. Needs a Position-time service-window guard on **both** hosts; headless has the predicate, the graphical host has none and one must be built. **4. N3 stops being latent.** The route-2 round-2 finding — headless never calls `RetryPending` after construction, so a declined `Place` would wedge the ordered stream — was filed as latent because headless remotes produced no placement receipts. Route 4 makes them produce receipts for the first time. **Fix N3 in route 4 or the headless stream wedges.** ## Three unfiled divergences found during scoping Our production remote path diverges from the retail classifier on five behaviours; three have no register row: - **NPC airborne hard-snap** (`LiveEntityNetworkUpdateController.cs:1819-1823`) writes `Body.Position`/`Orientation` and never consults `update.IsGrounded` at all — it branches on the client-tracked `rmState.Airborne`. Retail returns 0 and writes nothing (@0x0051636D). - **`ConstrainTo` armed before the operation, unconditionally** (`:1522-1529`), so it arms on the airborne no-op retail skips and anchors to the *pre*-move position on the far branch. Retail arms it *after* a nonzero `MoveOrTeleport`, anchored to the post-move `m_position` (@0x00454254-@0x00454272). - **`ConstrainTo` never armed on the remote teleport branch** (the path returns at `:1504`, before `:1522`), where retail *does* re-arm after `teleport_hook`'s `UnConstrain`. Route 4 fixes all three by construction — which means **route 4 is a behaviour change to every visible creature in the game, not a refactor.** ## Allocation: not the blocker anyone feared The inventory's "frame-frequency routes are blocked on placement allocation" warning was written pre-C2 and before anyone checked which branch remotes take. The steady state — nearby, grounded, non-teleporting — classifies to **`Interpolate`, which runs no SetPosition at all**. The 944 B/op cost applies only to teleports, cellless first placements, and >=96 m remotes. Arithmetic (assumptions, not a capture): 40 remotes x 7 Hz with ~10% on a SetPosition branch ~= 26 KB/s. Pathological dense-town worst case ~= 264 KB/s, about 2.4% of the Slice-G5 profile. **Not a blocker — provided the near branch stays out of `SetPosition`.** The contract must forbid routing every remote Position through it "for uniformity". Unmeasured and worth measuring before pinning a budget: the legacy path's actual cost (no gate covers `PhysicsEngine.SetPosition`), and the real near/far split in live play. ## Route 4's one big advantage: it is trivially observable Route 2 cost five review rounds largely because nothing a user could do would trigger it. Route 4 is the opposite — every remote entity exercises it continuously. Six checks, all within two minutes of ordinary play with a second character: 1. **Near interpolate** — second character walks/runs in a circle 5-15 m away. Smooth motion, no per-packet stepping. 2. **Airborne no-op** — they jump, and jump off a ledge. Clean parabola, clean landing. Watch for invisible-but-solid, the #184 signature. 3. **Far-snap** — they run >96 m and back. No freeze at the boundary, no wrong Z. 4. **Remote teleport** — portal/recall out and back, plus an admin `@teleto`. Arrive grounded, animating, immediately targetable. 5. **The leash** — route 4 moves `ConstrainTo` from before to after, stops arming it on the airborne no-op, starts arming it on teleport. #167 was a leash bug. 6. **Creature blast radius** — pull a drudge, let it chase, melee it, let it die. Weight this gate heavily. All four route-2 states were green and three were defective; route 4 finally has a real oracle. ## The decision Route 4 is worth doing — it fixes three unfiled divergences affecting every creature, retires the largest duplicate-authority cluster in the codebase, and builds the seam route 5 needs (the classifier gives Projectile *identical* branches to Remote). But it is a 1,500-2,500 line behaviour change, not a wiring job, and it should not start on a budget everyone knows is wrong. Options, in the order I would rank them: **A. Split route 4 into 4a and 4b.** 4a = the interpolation/near path plus the airborne no-op (the steady state, highest visible value, no park hazard, no service-window work). 4b = teleport/far/cellless, which is where the parks, the service-window guard, N3 and #277 all live. Each gets its own contract, gate and review. Roughly halves the blast radius per commit and puts the observable win first. **B. Do route 4 whole against a corrected budget** (~2,000 lines, 2-3 implementation passes, one adversarial review per pass). Honest, but it is a large single commit touching every visible creature. **C. Defer route 4; do routes 5-7 first.** Rejected — route 5 needs the same seam, so this just moves the cost. **D. Stop the campaign here and simplify instead.** The complexity concern that prompted the budget is real, and `RuntimeSetPositionState` at 5,652 lines is the obvious target. But route 4 is where three live divergences get fixed, so stopping leaves known-wrong behaviour in the client. **Recommendation: A.** It respects the budget's intent — keep each landing small enough to review — without abandoning work that fixes real bugs. It also front- loads the part with a clean live gate and defers the part with the known traps.