# C4 route 5 — architecture / adversarial review (2026-08-04) **Verdict: FAIL.** Scope reviewed: the uncommitted working tree at HEAD `30d3d114`, branch `claude/acdream-physics-divergence-5aa784` — `git diff HEAD` over 8 files plus the untracked `tests/AcDream.Runtime.Tests/Entities/RuntimeProjectilePositionKindTests.cs`. `docs/research/2026-08-04-c4-route-5-contract.md` is the contract, not under review. Line numbers below are as-of the working tree and will go stale; every citation also names the symbol (process rule 6). Independent verification performed for this review: `dotnet build AcDream.slnx -c Release` (succeeded, 0 warnings / 0 errors), focused Runtime suites 58/58 green, focused `ProjectileControllerTests` 41/41 green. The complete Release suite was NOT run here. The design is sound and the Runtime seam is largely a faithful, well-argued reproduction. The FAIL rests on four things: two real defects in the ~35 lines of App dispatch glue (A1, A2), one pinned contract obligation left unwired with a concrete failure scenario (A3), and the fact that the presentation invariant (§5 item 2, "#312's layer — tests must assert it") and the entire App dispatch have **zero** test coverage (A4, A5) — which is also the direct answer to the scoping-gap question at the end. --- ## MAJOR findings ### A1 — the App projectile ack ignores the seam's status and writes cell identity on the outcomes the design pins as "write nothing" **Severity: MAJOR.** `src/AcDream.App/Physics/LiveEntityNetworkUpdateController.cs:2137-2144` (`OnPosition`, the D-P6 projectile arm): ```csharp if (earlyRemoteRoute is { } route) { _remotePlacementDrive.ApplyAcceptedProjectilePosition( acceptedPositionCanonical, route); _projectileController?.SyncPresentationFromResolvedBody( acceptedPositionRecord); } return; ``` The returned `RuntimeRemotePlacementExecutionStatus?` is discarded. The Runtime half is deliberately selective — `RuntimeRemotePlacementDriveController.ApplyAcceptedProjectilePosition:975-983` skips `SyncProjectilePresentation` for `Deferred`/`RejectedByPlacement` ("Invariant 2: presentation advances on every committed/stored outcome only") and returns `null` without touching anything for `Interpolate`, `NoPositionOperation`, `RejectedAuthority`, `RejectedData`, and every ownership mismatch. App re-projects presentation for **all** of them. Concrete failure scenario (the near no-op, D-P2's pinned `Interpolate` row): 1. An in-flight arrow's canonical body sits in cell A at world P_A. 2. A grounded, <96 m accepted Position for cell B arrives. `RuntimeEntityObjectLifetime.TryApplyPosition` merges it; the merge's `RefreshDerivedState` → `SetFullCell` (`RuntimeEntityRecord.cs:230-251`) stamps `canonical.FullCellId = B` **before** any classification runs. 3. The classifier returns `Interpolate` — a pinned no-op. The Runtime seam writes nothing: body stays at cell A / P_A, prediction version unchanged. 4. App nevertheless runs `ProjectileController.SyncPresentationFromResolvedBody:505-520`, which does `entity.SetPosition(runtime.Body.Position)` (= P_A, correct) and `entity.ParentCellId = record.FullCellId` (= **B**, wrong — `LiveEntityRecord.FullCellId` is `Canonical.FullCellId`, `LiveEntityRuntime.cs:189-191`). The render entity is now parented into a cell it is not geometrically inside, with a position from the other cell. If B is an indoor EnvCell the arrow has not entered, the arrow renders through the wall or is culled. The same write happens on `RejectedByPlacement`, where the Runtime comment says in as many words that the body must be left "at its prior (already-synced) pose". This is a genuine regression, not parity: the deleted `ApplyAuthoritativePosition` had no disposition concept — it always moved the body to the wire cell *and* wrote the same cell into the entity, so position and cell were always consistent. Splitting the body write out by disposition without splitting the presentation write out with it is what creates the divergence. **Fix direction:** return the status through the App boundary and call `SyncPresentationFromResolvedBody` only on the outcomes `SyncProjectilePresentation` itself covers (non-null status that is neither `Deferred` nor `RejectedByPlacement`) — or move the render-entity projection into the same gate inside the Runtime seam and have App acknowledge, not decide. --- ### A2 — a record carrying the Missile bit with no bound `RuntimeProjectile` now has every accepted Position silently dropped, where it previously fell through to the remote tail **Severity: MAJOR (silent, permanent).** Discriminator: `LiveEntityNetworkUpdateController.cs:2122-2146` (`isMissilePacket`); refusal: `RuntimeRemotePlacementDriveController.ApplyAcceptedProjectilePosition:930-936` (`record.Projectile is not RuntimeProjectile projectile` → `return null`). `isMissilePacket` keys purely on the canonical record's Missile bit. It does not require that a projectile component exists. The old short-circuit did: the deleted `ProjectileController.ApplyAuthoritativePosition` returned `false` when `TryGetCurrent` found no bound `RuntimeProjectile`, and `== true` failing meant the packet **fell through** to the generic remote tail. `record.Projectile` has exactly one producer: `ProjectileController.TryBind` → `LiveEntityRuntime.BindProjectileRuntime` → `RuntimePhysicsState.BindProjectile` (verified by grep — no other writer of `Entities.SetProjectile(record, component)`). `TryBind` has several ordinary, non-exceptional failure returns, e.g. `ProjectileController.cs:160-166`: ``` "Missile 0x… Setup 0x… does not have the supported retail one-sphere collision shape." ``` Scenario: ACE spawns a missile whose Setup does not reduce to the supported one-sphere shape (a diagnosed, expected case — it has its own log line). `TryBind` returns false; `record.Projectile` stays null; `FinalPhysicsState & Missile` stays set. Every subsequent accepted Position for that guid now returns at `:2146` having done nothing at all — no `TryApplyGenericRemoteRenderPose`, no `RebucketLiveEntity`, no `GetOrCreateRemoteMotionRuntime`. The object freezes at its create pose for the rest of its life and never follows the server. Before this diff it tracked, via the remote tail. The contract's D-P6 does pin "dispatch the projectile arm and RETURN", so this is a defect in the design as implemented rather than a slip against it — but it is still a new, silent, permanent freeze, and the same defect class (`the frozen entity`) that the 4b-family reviews have been hunting. Reachability caveat, stated honestly: ACE never sends a missile `UpdatePosition` (`WorldObject_Tick.cs:333-334`), so like every other half of this route it is test-reachable only. **Fix direction:** make the discriminator conjunctive — a packet takes the projectile arm only when the arm can actually own it (Missile bit **and** a bound projectile whose `Body` is the canonical `PhysicsBody`); otherwise let it take the ordinary tail, which is what the record's shape actually is. If the swallow is intended, it needs its own register row and a test that pins it, and neither exists. --- ### A3 — D-P4's teleport-hook reduction is unwired for the adopted-body case, which the codebase explicitly supports **Severity: MAJOR (contract obligation unmet; concrete scenario).** `RuntimeRemotePlacementDriveController.ApplyAcceptedProjectilePosition:944` runs exactly one of retail's six `teleport_hook` @0x00514ED0 actions: ```csharp case RuntimeAuthoritativePositionDisposition.SetPosition: _entityObjects.Physics.CollisionReports.LeaveWorld(record); ``` The contract pins more than that: *"The other five actions execute iff their owning component exists — for the adopted-body case (a missile that also carries a `RemoteMotion`), the existing hook actions' per-manager guards already express retail's shape; the implementer wires this without per-packet closures"* (D-P4). Nothing wires them, and no note records the omission. The adopted-body case is not hypothetical. `ProjectileController.TryBind`'s shared-body branch exists precisely for it — *"If a non-missile incarnation already created its MovementManager or entered the Physics-Static animation workset, classification adopts that same body"* — and `RuntimePhysicsState.BindProjectile:757-830` does not exclude a record that has a `RemoteMotion`. `RuntimePhysicsState.cs:697-708` shows the same key can sit in both `_spatialRemotes` and `_spatialProjectiles`. Scenario: an object is a live remote with a populated `RemoteMotion.Interp` queue and a `ConstrainTo` leash armed by route 4a's `RuntimeRemoteSteadyStatePosition.TryArmConstraintAfterOperation`. ACE then sets the Missile bit (a State packet); `TryBind` adopts the shared body. The next teleport/cell-less Position takes the projectile arm: collisions are force-ended and the body is teleported, but `StopInterpolating`, `UnConstrain`, `CancelMoveTo`, and `UnStick` never run. The `RemoteMotion` survives in the remote workset with a stale waypoint and a live leash anchored at the pre-teleport position, and drags the newly-placed missile body back. Retail @0x00514EFD/@0x00514F31 runs all six, each guarded on its own manager, so retail's own guards would no-op for a pure arrow and would fire here. Note this is not a regression from HEAD — the old short-circuit did not run the hook either — but D-P4 made wiring it an explicit obligation of this slice, and the faithful port (`RemoteTeleportHook.Execute` + `RemoteTeleportHookActions`, `src/AcDream.App/Physics/RemoteTeleportHook.cs`) is sitting in-tree unused for this arm. That is exactly the 4b-3 round-2 R2 defect class the contract cited by name. **Fix direction:** drive the projectile teleport arm through the same six-action bundle (cached delegates, #315 pattern), letting each action's per-component guard decide — for an ordinary arrow all five extra actions are no-ops, and the adopted-body case gets retail's shape for free. --- ### A4 — `SyncProjectilePresentation`, the highest-risk reproduced code in the diff, has no test that would fail if it were deleted **Severity: MAJOR (test gap on the invariant the contract singles out).** `RuntimeRemotePlacementDriveController.SyncProjectilePresentation:987-1039`. Contract §5 invariant 2: *"Presentation still advances… the shadow registry is synced (spatial+visible) or suspended (hidden/non-spatial) exactly per the current tail's semantics. A projectile is never left rendered a packet behind its body (#312's layer — **tests must assert it**)."* Across all seven new Runtime tests, the only assertion that even touches this method is `Assert.True(body.InWorld)` in `ApplyAcceptedProjectilePosition_Refused_StillAdvancesPoseNoParkPredictionInvalidated`. That assertion is vacuous with respect to the sync: the fixture's `AttachBody` (`RuntimeRemotePlacementDriveControllerTests.cs:2860-2880`) calls `body.SnapToCell(...)`, and `PhysicsBody.SnapToCell` sets `InWorld = true` (`PhysicsBody.cs:201-205`). Nothing in the Refused path clears it. So the assertion passes with `SyncProjectilePresentation` entirely removed. Unasserted, at any layer: the shadow-registry publication (`ShadowObjects.UpdatePosition`), the spatial+hidden `Suspend`, the non-spatial `InWorld = false` + `Active` clear + `Suspend`, the `!body.InWorld` activation edge, and the three currency guards at the method's head. The committed-outcome tests (`…TeleportCommit…`, `…FarCommit…`) assert status, position, prediction version, velocity, `RemoteMotion is null`, and collision-owner count — none of which the sync produces. **Fix direction:** one test per branch of the sync, keyed on the observable the old tail produced: shadow row present at the resolved pose (spatial+visible), `Suspend` called (spatial+hidden and non-spatial), `Active` cleared (non-spatial). These are cheap; the fixture already registers shadows in `SeedCollisionOwner`. --- ### A5 — no App-layer test exercises `OnPosition` with a missile packet at all; §7 items 8 and 9 are unwritten **Severity: MAJOR (this is the scoping-gap answer).** `grep -rn "Missile" tests/AcDream.App.Tests/Physics/LiveEntityNetwork*.cs` returns **zero** hits. The only App file mentioning `Missile` at all is `ProjectileControllerTests.cs`, which never calls `OnPosition`. So none of the following is covered anywhere: - the D-P1/D-P6 discriminator itself in its production call site; - invariant 8's mutual exclusion (Missile-set ⇒ projectile arm and **no `RemoteMotion` afterward**; Missile-clear ⇒ remote tail and no projectile effect); - the null-classification swallow (`earlyRemoteRoute is null` with the Missile bit set) and its positive half (timestamps consumed, merge advanced); - the "no early wire-pose write / no `RebucketLiveEntity`" pins; - the presentation ack the App owns — i.e. #312's layer at the layer it renders. Worse, the retired App test `MalformedFreshUpdates_DoNotPoisonCanonicalBodyOrPose` justifies its Position half's deletion by pointing at *"LiveEntityNetworkUpdateController's own 'invalid-payload swallow' test"*. **That test does not exist.** The underlying claim (the shared `CanAcceptPositionPayload` gate rejects the payload upstream) is correct and I verified it independently — but the cited successor coverage is not there, so the assertion was retired against a coverage claim that is false. Both A1 and A2 live inside the ~35 lines the implementer argued were "thin glue between two well-tested layers". That is the empirical refutation of the argument. --- ## MINOR findings ### A6 — the `!body.InWorld` gate is read after the placement, where the deleted tail captured `wasInWorld` before it `SyncProjectilePresentation:1012-1018` reads `body.InWorld` *after* `TryExecuteAcceptedRemotePosition` has run. The canonical commit calls `body.SnapToCell(...)` (`RuntimeSetPositionState.cs:4974`), which sets `InWorld = true`. The deleted tail captured `bool wasInWorld = body.InWorld;` **before** its `SnapToCell`. Consequence: on every committed outcome the re-activation branch (`body.LastUpdateTime = clock` + `TransientState |= Active`) is now dead — a projectile that had left the world and comes back through a committed accepted Position is marked `InWorld` but never re-flagged `Active`, and its legacy `LastUpdateTime` is not rebased. It self-heals on the next tick because `RetailObjectActivityGate.Evaluate` re-sets the flag (`RetailObjectActivityGate.cs:61-67`), which is the only reason this is MINOR rather than a frozen body. Fix: capture `body.InWorld` before dispatching the placement and pass it in. ### A7 — the spatial+hidden branch silently drops `body.LastUpdateTime = currentTime` Deleted tail: `else if (spatial) { body.InWorld = true; body.LastUpdateTime = currentTime; Suspend(); }`. New (`SyncProjectilePresentation:1031-1035`): the `LastUpdateTime` write is gone. `ProjectileController.TryBind`'s equivalent branch documents why the write exists — *"consume the hidden clock so UnHide cannot replay a time backlog"*. Bounded by `TryBegin` refusing `Hidden` and by the `RetailObjectQuantumClock` being canonical post-R6, but it was a deliberate write and its removal is unremarked. ### A8 — the shadow publication is silently skipped when the Runtime world frame is unavailable `SyncProjectilePresentation:1019-1030` wraps the shadow update in `if (physics.TryGetWorldFrameOffset(...))` and does nothing on false. The deleted `ShadowPositionSynchronizer.Sync` always published, using App's live centre, and returned early only for `cellId == 0`. `TryGetWorldFrameOffset` additionally returns false when `_worldFrameCenterLandblockId == 0` (`RuntimePhysicsState.cs:613-632`). Two methods away, `StoreAcceptedDestinationPose:1113-1119` treats exactly this case as `ThrowIfWorldFrameUnreachable` — #284's "a frame that can never arrive is terminal, never silent" policy. The new code neither publishes nor escalates. ### A9 — the null-route fallback is not fenced off the local player `LiveEntityNetworkUpdateController.cs:2122-2126`: when `earlyRemoteRoute` is null, `isMissilePacket` falls back to a bare `FinalPhysicsState & Missile` test. `earlyRemoteRoute` is *always* null for `update.Guid == _playerServerGuid`. A local-player record that ever carried the Missile bit would therefore swallow its own accepted Position and freeze the player. Currently impossible only because ACE never sets Missile on a player — a one-token `update.Guid != _playerServerGuid &&` would make it structurally impossible. ### A10 — `OwnsFarSnap`'s doc is now false in the kind dimension `src/AcDream.Runtime/Physics/RuntimeRemoteFarSnapPosition.cs:78-79` still states this predicate is *"a strict narrowing of `OwnsPlacement` to its far half"*. After the widening it narrows only `OwnsPlacement`'s **remote** far half. `RuntimeRemoteTeleportPosition.cs:33-35` hedges correctly ("`OwnsPlacement`'s remote scope"); the far-snap doc does not. D-P3 asked for exactly this correction where a comment would otherwise mislead (process rule 6). ### A11 — the cutover route ledger still lists the deleted methods as live routes `docs/research/2026-08-02-cutover-route-inventory.md:666-694` still describes `ProjectileController.ApplyAuthoritativePosition` → `RuntimeProjectilePhysicsUpdater.ApplyAuthoritativePosition` as a live route with `SnapToCell` + `CommitProjectileCell`. D-P7 scoped the re-point grep to `src/` and `docs/architecture/`, so this is outside the letter of the obligation — but that file is the C4 route ledger this campaign reads from, and it is now wrong about route 5. --- ## Verified — no finding These were hunted and came back clean; recorded so the next round does not re-litigate them. - **P3 (`RuntimeSetPositionState` kind-agnosticism) is CORRECT, and I verified it independently rather than accepting the argument.** `TryBeginExclusiveAuthoredPlacement:1444-1472` returns `default` (invalid token → `Contention`) whenever `_operations.ContainsKey(key)` or `HasRetainedCompletion(key)`. Both constructors that stamp `Kind = RemoteAuthoritative` — `ParkCollisionResidents`'s window-departure park (`:3833`/`:3866`, which itself `continue`s past any record already in `_operations`) and `CreateWithdrawalOperation` (`:5338`/`:5350`, reached only from `Cancel` after `CancelCoreDeferred`) — install an operation into `_operations` under the record's key. A projectile accepted Position arriving while one is live cannot capture or be relabelled by it; it refuses with `Contention`, which `StoresAcceptedDestination()` puts on the **storing** side, so the pose still advances and no ledger column is mislabelled. The two kind-conditional stage sites (`IsExactDormantLocalActivationCurrent:5445-5446`, `IsDormantLocalActivationPrephaseCurrent:2554-2600`) additionally require `InitialLogin`/`LocalAuthoritative` **and** `record.Projectile is null`, and the family has zero production callers. Residual, benign and pre-existing: a park operation created for a projectile record carries `Kind = RemoteAuthoritative`, but it never reaches the drive controller's ledger. - **The three ownership fences hold.** `OwnsPlacement` has exactly one production reader — `TryExecuteAcceptedRemotePosition:640` (verified by grep across `src/`; every other hit is a doc comment or a test). `OwnsFarSnap` and `OwnsTeleportPlacement` keep their `RemoteAuthoritative` gate, and a projectile route cannot reach `ApplyAcceptedRemoteFarSnap` / `ApplyAcceptedRemoteTeleport` / `TryArmConstraintAfterOperation`: the App returns before the remote tail whenever `isMissilePacket`, and `isMissilePacket` is derived *from* `route.OperationKind` whenever a route exists, so the two cannot disagree. The throwing guards stay unreachable, and no `RemoteMotion` is ever created on this arm. - **The remote route is unchanged.** Moving the `ClassifyRemoteAcceptedPosition` call earlier hops only a pure-read guard block (`TryGetRecord` / `ReferenceEquals` / `IsCurrentPositionAuthority`) with no reentrancy in between; classification itself is side-effect-free. Zero remote classifier tests changed expectation, which is the contract's own tripwire. - **`StoreAcceptedDestinationPose` never writes a cell**, so on `Refused`/`Contention`/`RejectedPreparation` the projectile body's `CellPosition` stays at the source cell while `record.FullCellId` is the wire cell. This is identical to the accepted remote behaviour (AP-138's residual) and the quantum stepper takes the cell from `record.FullCellId` explicitly (`RuntimeProjectilePhysicsUpdater.TryBegin`), so it is parity, not a new defect. Recorded, not filed. --- ## Judgment on the four implementer claims **Claim 1 — both `ApplyAuthoritativePosition` overloads and the Runtime updater's copy deleted; nothing else called them; no behaviour lost.** **Deletions and callers: VERIFIED.** No code reference survives anywhere in `src/` or `tests/` (only comments, the contract, and the two research docs noted in A11); Release build green with 0 warnings. **"No behaviour lost": PARTIALLY FALSE.** Three deliberate behaviours went with them and only one is accounted for: the origin-translated world-position finiteness check (genuinely redundant — `worldPos` is a finite wire triple plus an integer landblock offset, so it cannot be non-finite when `CanAcceptPositionPayload` passed); the `_lastFiniteGameTime` rebase on a Position packet (App tick clock — unremarked, low impact); and the `wasInWorld` activate edge plus the hidden-branch `LastUpdateTime` write (A6, A7 — unremarked). **Claim 2 — 7 call sites modified, 2 assertions retired as obsolete.** **Mostly legitimate, with one false coverage claim.** The velocity assertions in `FreshVectorAndPositionCorrectionsMutateSameBody` are genuinely obsolete under D-P5 and have a real positive successor (`…TeleportCommit…` / `…FarCommit…` assert `body.Velocity` bit-identical after a placement). The `AuthoritativeMutation.Position` retirement has a genuine, load-bearing successor I read and checked — `ApplyAcceptedProjectilePosition_DuringOpenQuantum_CompleteAbortsAfterPredictionInvalidated` opens a real quantum, runs the arm, and asserts `Complete` returns false with the committed pose intact. The rewritten `SyncPresentation_ReentrantGuidReuseNeverTouchesTheReplacement` is an improvement, not a dilution: it correctly notes that `RebucketLiveEntity` self-suppresses its own guid's visibility callback and switches to a genuine spatial edge. **But** the malformed-payload retirement in `MalformedFreshUpdates_DoNotPoisonCanonicalBodyOrPose` cites a successor test in `LiveEntityNetworkUpdateController` that does not exist (A5). The upstream gate does cover the scenario; the citation does not. **Claim 3 — `OwnsPlacement_FalseWhenOperationKindIsNotRemoteAuthoritative` legitimately changed its expected outcome.** **TRUE, not a test bent to fit the code.** The widening makes `ProjectileAuthoritative` a positively-owned kind by construction, so leaving it in a "these kinds are refused" list would assert the opposite of the design. The positive case is separately and explicitly asserted (`OwnsPlacement_TrueForProjectileAuthoritative_…`, including the Create exclusion), and the two remaining negatives (`InitialLogin`, `LocalAuthoritative`) still pin the predicate's kind dimension. The rename is accurate. **Claim 4 — the new tests use airborne destinations to remove a contact-response confound.** **Legitimate confound removal, with a named residual.** The claim is accurate: the shared placement pipeline's contact response is retail landing behaviour, shared verbatim with the remote arms, and outside route 5's scope; D-P5's pin is specifically "no velocity *from the packet*", which an airborne destination isolates cleanly. It is not a test dodging a defect. The residual worth stating: **no test now places a projectile into ground contact at all**, so the interaction between the projectile arm and contact response — including whether the resulting velocity change is the right one for a ballistic body — is entirely unexercised. That is a gap, not a dishonesty. --- ## Judgment on the known scoping gap (§7 item 8, the dual-kind `OnPosition` matrix) **It is a FAIL-level gap. Definite answer: not acceptable.** The implementer's two arguments are (a) the collapse's `LiveEntityNetworkOnPositionCollapseMatrixTests` fixture unconditionally constructs a `RemoteMotion` a projectile lacks, and (b) the D-P6 dispatch is ~35 lines of thin glue between two well-tested layers. (a) is a fixture limitation, and a fixture limitation is a reason to extend the fixture, not to ship zero coverage. The projectile half of the matrix needs a record with the Missile bit, a bound projectile, and **no** `RemoteMotion` — which is precisely the assertion the matrix exists to make. (b) is refuted by this diff itself. Two of the five MAJORs above (A1, A2) are defects **inside those 35 lines**, and neither is visible from either well-tested layer: A1 is an App/Runtime disagreement about which outcomes may write presentation, and A2 is a discriminator whose reachable set is wider than the arm that consumes it. This is the same shape as 4b-3's three MAJORs — a mapping written against one caller's reachable set, and an invariant satisfied on one arm only — and a dual-kind matrix was the structural fix there for the same reason it is here. Additionally, the gap is broader than §7 item 8 alone: item 9 (the App-level invalid-payload swallow) is also unwritten, *and* was cited as existing coverage in a retired assertion's justification. The minimum this needs before landing: a dual-kind theory over `OnPosition` covering at least far-commit, teleport-commit, near, airborne, and null-classification, asserting on the projectile half that no `RemoteMotion` exists afterward, no generic wire-pose write occurred, and the render entity's position **and** `ParentCellId` agree with the resolved body — that last assertion alone would have caught A1.