# C4 route 4b-3 — architecture / adversarial DELTA review, round 2 (2026-08-04) Reviewer lens: **adversary**. Round 1's findings are [`2026-08-04-c4-route-4b-3-architecture-review.md`](2026-08-04-c4-route-4b-3-architecture-review.md); the retail lane's are [`2026-08-04-c4-route-4b-3-retail-review.md`](2026-08-04-c4-route-4b-3-retail-review.md). Subject: the same uncommitted working tree on `claude/acdream-physics-divergence-5aa784` at base `3e002993`, after the round-1 fix pass. This review re-attacks the **fixes**, not the whole slice — the round-1 "verified sound" list is carried forward except where a fix touched it. ## Verdict: **PASS** Both round-1 MAJORs (A1, A2) are fixed, and I verified each fix produces the retail-correct outcome rather than merely silencing the symptom. The retail lane's R1/R2/R3 are fixed. **The `RunRemoteArmTail` / `ApplyWireAirborneLeftoverBookkeeping` extraction — the thing most likely to introduce a new defect — is behaviour-identical at all five call sites**; I walked each one against the pre-fix code and found no reordering, no dropped step, and no widened or narrowed guard. Six MINORs below, none blocking. Build `dotnet build AcDream.slnx -c Release`: **succeeded, 0 warnings**. Focused suites at Release `--no-build`: **Runtime 1,125 / 0 skips**, **App 4,081 / 3 skips** (up 3 from round 1's 4,078 — the three new App tests). --- ## Part 1 — the extraction, call site by call site The blast radius of the fix pass is small and I confirmed it: only `LiveEntityNetworkUpdateController.cs`, one new method on `LiveEntityRuntime.cs`, the App teleport test file, and three docs carry round-2 markers. ### `RunRemoteArmTail` — 3 call sites Signature returns `RemoteContactRouting?`; `null` means "the currency guard tripped, write nothing further". The guard inside it is `(Arm is FarSnapPlacement or TeleportPlacement) && (!isCurrentPositionOwner() || !ReferenceEquals(positionRecord.RemoteMotionRuntime, remote))`. | site | pre-fix sequence | post-fix | verdict | |---|---|---|---| | player teleport dispatch (`:2333`) | routing → **unconditional** guard → return | routing → **arm-gated** guard → return | **identical.** `ApplyRemoteContactRouting` returns `TeleportPlacement` iff `OwnsTeleportPlacement(route)`, which the caller has already evaluated on the *same* `earlyRemoteRoute` with the same pure static predicate. The arm at this site is provably always `TeleportPlacement`, so arm-gating the guard cannot weaken it. | | player grounded routing (`:2566`) | routing → arm-gated guard → return | same, via helper | **identical**, including the R5 guard-before-arm order (`TryArmConstraintAfterOperation` is still the first statement after the null check, `:2596`). | | NPC dispatch (`:2818`) | routing → arm-gated guard → `return` from `OnPosition` | routing → helper returns `null` → `if (npcRoutingResult is null) return;` inside the same `if (!snapSuppressedByStick \|\| isTeleportRoute)` block | **identical**, same lexical position, same effect. | Specific things I checked for and did **not** find: - **`willBeDrTicked` moved inside the helper.** The player grounded site used to hoist it into a local (`HEAD:2317`). I grepped every use in both the HEAD and current file: its only consumer was ever the routing call itself. Nothing downstream reads it. No loss. - **Delegate identity.** `runTeleportHook` now closes over `canonical`/`remote` and reuses the caller's `isCurrentPositionOwner` for both the hook's per-step currency checks and the post-routing guard. Previously two separately allocated but semantically identical lambdas. No change in what is tested. - **Guard-before-arm (R5) at all three sites.** Held. The helper cannot arm — it has no access to `ToConstraintArm` and its doc says so — and all three callers arm immediately after the null check. - **The NPC sticky-suppressed path still arms.** `npcArm` is initialised to `UnroutedCatchUp` and `TryArmConstraintAfterOperation` sits *outside* the `if (!snapSuppressedByStick || isTeleportRoute)` block, exactly as at HEAD. The extraction did not pull it inside. ### The LANDING TRANSITION block — confirmed untouched and non-conflicting `:2445-2532` still gates on `!rmState.Body.InContact`, still `return`s, and still arms with its own hard-coded `NearInterpolate`. It therefore cannot double-run with the shared tail, and — importantly — it is what makes `playerArm` provably never `AirborneSnap` at the grounded-routing site (the free-flight case has already returned). The only edit is its comment, which round 1 flagged as false (A5) and which is now correct **and complete**: it enumerates `SetPositionSimple` / `null` / `Rejected*` as classifications that also reach the block, and states why `NearInterpolate` is the right arming value for every one of them. ### `ApplyWireAirborneLeftoverBookkeeping` — 2 call sites The helper writes exactly three fields: `remote.CellId`, `remote.LastServerPos`, `remote.LastServerPosTime`. - **NPC site (`:2717-2722`)** — new, and the R1 fix. Correctly gated `!update.IsGrounded && !isTeleportRoute`, placed after the `IsAirborneNoOperation` return (so `NoPositionOperation` never reaches it) and before the synth-velocity block, the sticky probe, and routing. I confirmed the gate is exhaustive: for `RuntimeAcceptedPositionSource.PositionEvent` a wire-airborne packet can only classify `NoPositionOperation` (handled above), `null`, or `Rejected*` — the classifier's `effectiveContact` widening applies only to `SameIncarnationCreate`, which this path never carries. - **Player site (`:2390-2395`)** — the helper adds a `remote.CellId` write the hand-rolled block did not have. **I chased this specifically as the classic extraction hazard and it is benign**: `RuntimePhysicsState.CommitCanonicalCell:2145` short-circuits on `fullCellId == record.FullCellId` *before* `SetFullCell` and before raising `CellCommitted`, and the player arm wrote the identical `p.LandblockId` ~180 lines earlier with no placement in between. The redundant write is a true no-op — no spurious rebucket event, no version bump. --- ## Part 2 — the individual fixes ### A1 — `ToConstraintArm(AirborneSnap) → NearInterpolate`, switch made total Correct, and correct for the right reason: `AirborneSnap` is acdream's *body*-contact carve-out for a packet whose **wire** contact bit said grounded, so retail's `MoveOrTeleport` returns nonzero and `HandleReceivedPosition` arms @0x00454272. Mapping it to an arming value restores the 1-arm count and makes the App-layer partition exactly the contract's D4 table. **The throwing default cannot be reached in production.** `RemoteContactArm` has exactly five members (`AirborneSnap`, `SteadyStateInterpolate`, `FarSnapPlacement`, `TeleportPlacement`, `UnroutedCatchUp`) and all five have explicit cases; `default(RemoteContactArm)` is `AirborneSnap` (value 0), which is handled. The only two producers of the argument are `ApplyRemoteContactRouting`'s returns and the NPC arm's `RemoteContactArm.UnroutedCatchUp` initialiser. Reaching `_` requires an out-of-range cast, which nothing performs. The throw is a genuine "can't-happen" guard, not a live hazard — this was the right call over a silent zero-arm fallback. **The test genuinely discriminates.** `NpcAirborneSnap_LandingPacket_StillArmsTheLeash` asserts `host.PositionManager.Constraint` goes from `null` to non-null across the packet. I verified `PositionManager.Constraint` is lazily created *only* inside `ConstrainTo` (`PositionManager.cs:27-28`, `:62`), so its presence is direct proof of an arm rather than an inference. Under the pre-fix mapping the packet arms zero times and `Constraint` stays null — the test fails. ### A2 / R3 — NPC synth-velocity and cycle-apply gated on `!isTeleportRoute` Correct. `isTeleportRoute` is hoisted above both the D2 check and the velocity block, and both the install (`:2741`) and the `RemoteServerControlledVelocityCycle.Apply` call (`:2886`) carry it. The fix's own claim that leaving `ServerVelocity` stale is safe holds up: its only consumers are the now-excluded cycle-apply and `RuntimeRemotePhysicsUpdater`'s stale-velocity watchdog, which only *zeroes*. Two incidental changes in the same block are behaviour-neutral and I checked them rather than assumed: the `!IsPlayerGuid(update.Guid)` guards were dropped from the synth condition and the `else if` collapsed to `else`. Both were already dead — the whole NPC section sits after `if (IsPlayerGuid(update.Guid)) { … }` and every path inside that block `return`s (the last statement before its closing brace at `:2666` is `return;`), so the section is unreachable for player guids. `NpcTeleport_DoesNotInstallASynthesizedVelocity` seeds `LastServerPos`/ `LastServerPosTime` so a broken implementation has a real distance and interval to synthesise from. It fails pre-fix (`HasServerVelocity` would be `true`). ### R1 — the D2 shape on the NPC arm Correct and now genuinely shared. The AP-137 sentence *"unifies player and NPC remotes on one behaviour"* is true against the code as of this round — I re-read the row and the two call sites. `NullClassifiedNpc_WireAirbornePacket_WritesOnlyBookkeepingNoBodyOrShadow` discriminates: pre-fix the packet reaches `ApplyRemoteContactRouting`'s free-flight carve-out and hard-snaps `Body.Position = worldPos`, which the test's `Assert.Equal(spawnBodyPose, …)` catches. ### R2 — the teleport hook's collision-end action → `ForceEndCollisionReporting` **This was the fix I was asked to attack hardest, and it does not carry any of this family's failure modes.** `RuntimeCollisionReportingState.LeaveWorld` is named for the *collision-reporting* state machine's own leave transaction, not the entity lifecycle's. I traced it end to end: - `LeaveWorld:870-887` → `_admissionBlocked.Add(key)` → `ForceEnd:1435-1454` → `EndExpiredObjectCollisions(force: true)` → `TrimEmptyOwner`. **The only mutations are the owner's collision table, the reverse-owner index, and the report queue.** Nothing writes `body.InWorld`, `TransientStateFlags.Active`, `record.ObjectClock`, `FullCellId`, spatial residency, or visibility. **Invariant 3 is not at risk.** - `_admissionBlocked` is released in a `finally`, so the subsequent placement's `TryPrepareSetPositionBatch:230-235` — which *does* refuse while a key is in `_leaving` or `_admissionBlocked` — is not blocked by the hook that ran immediately before it. - `_mutationRevision` is bumped by `LeaveWorld`, but the placement's batch captures `expectedMutation` *after* the hook (`:238`) and the prepare→install window is entirely inside the placement, so the bump cannot invalidate the teleport's own batch. - `Publish` swallows observer exceptions (`:1489-1494`), and `_leaving` guards re-entrancy, so a force-end cannot escape as a throw into the packet path. - `TrimEmptyOwner` runs on the exit path, so `CaptureOwnership`'s owner count cannot leak an empty entry — no ledger regression. Dropping the old `ShadowObjects.Suspend` is also safe and is arguably the better half of the fix: the far-snap arm (shipped, user-accepted) has never suspended the shadow before its own placement, so the teleport arm is now *consistent* with it rather than uniquely different, and the arm tail's `TryPublishRemote` → `SyncRemoteShadowToBody` (ungated at this call site) still re-seeds the registry at the resolved pose. See MINOR B2 for the one thing about R2 that should be stated rather than left implied. --- ## MINOR findings ### B1 (MINOR) — contract test-plan item 11 is still unwritten, and it is now the *only* thing guarding the #42 class `NullClassifiedNpc_WireAirbornePacket_…` asserts what must NOT happen (no body write, no shadow republish) but never asserts what MUST happen: that `ApplyWireAirborneLeftoverBookkeeping` actually wrote `remote.CellId` and `LastServerPos`/`LastServerPosTime`. A regression that emptied the helper body would pass every test in the tree. Those writes are exactly AP-135 — the free-fall sweep gates on `rm.CellId != 0` and without it "an airborne remote falls through the floor" (#42), and the first grounded packet after an arc would synthesise its velocity across the whole jump. Two `Assert.Equal`s on the existing test close it. ### B2 (MINOR) — R2's fix is correct plumbing, but its *observable* half still goes nowhere; say so before someone records it as closed `IRuntimeCollisionReportObserver` has **zero production implementations** — `git grep OnCollisionReport` returns the interface, the dispatch loop, and four test doubles. `CollisionReports.Subscribe` has no production caller. So the bidirectional `DoCollisionEnd` notification retail fires at @0x00514620 still reaches no gameplay consumer; what the fix actually delivers today is the **table clear** (real, and it does prevent the later ~1 s stale-end and any mis-attribution) plus a correctly-wired channel for when a consumer appears. The hook's new doc doesn't overclaim, but the retail review's phrasing ("neither side receives the immediate `ObjectCollisionEnd`") will read as closed. State the residual — it is a pre-existing gap in the collision-report plumbing, wider than this slice, and it deserves one sentence rather than a silent inheritance. ### B3 (MINOR, disclosed) — no test for R2 Judged **acceptable but worth filing**. A test would assert against a channel nothing production reads (B2), so its value is pinning the retail *mapping*, not a behaviour. The cheap version is one assertion that the teleporting owner's collision table is empty after an `OnPosition` teleport packet — that would fail if a future edit reverted the action to `ShadowObjects.Suspend`, which is the actual regression risk. Not blocking. ### B4 (MINOR, disclosed) — the per-packet `runTeleportHook` closure Two delegates + two display classes per remote accepted Position (the outer `runTeleportHook` lambda and the caller's `isCurrentPositionOwner`), allocated whether or not the packet is a teleport. The implementer's stated blocker — that `ApplyRemoteContactRouting`'s `Func` parameter is injected by existing tests — is real but the parameter is `internal`, so it is a refactor cost, not a compatibility one. Judged **acceptable to defer**: this is the per-packet network path (5-10 Hz per remote), not Slice I's per-frame resolve path where the 0-B target applies, and the objects are gen0-transient. File it with the probe-family cleanup rather than churning the seam now. ### B5 (MINOR, disclosed) — the stress test's teleport step Round 1's A3 stands as written and is judged **acceptable to leave**. The scenario it covers (Hidden → DeferredShadowRestore → landblock churn → UnHide) no longer has any teleport-specific machinery to interact with, because this slice deletes `_activePlacementOwners` and the placement is now synchronous inside `OnPosition`. The hand-built step is honestly documented as reproducing the arm tail's observable state, and the arm tail itself is directly covered by `LiveEntityNetworkRemoteTeleportPresentationTests`. The residual loss — the *interaction* between a mid-teleport Hidden edge and the placement — is a degenerate case now, not an untested one. ### B6 (MINOR, carried) — proof obligation 1 is still unstated Unchanged from round 1's A9. The contract requires the `ParkCollisionResidents`-overlap-throw unreachability argument (with 4b-1's B2 caveat that the guarded property is `TryAcquireCollisionPrefixMutationPermission`'s `HasOldPrefixPlacementDebt` refusal — a stall, not a throw) in the implementation commit's contract-conformance section. The work is still uncommitted so I cannot verify it; the property itself holds by reading (the teleport arm adds packets to the same one-operation-per-key machinery and opens no new operation shape). --- ## Round-1 MINORs — spot-check Verified fixed, not re-derived: **A4** (`TeleportRefused_…` now arms sticky before the packet and asserts `GetStickyObjectId() == 0` afterward — a real discriminator, since only the teleport arm's hook calls `UnStick`, so the `UnroutedCatchUp` look-alike I identified can no longer pass it); **A5** (the landing-block comment is now correct *and* complete); **A7** (R1, above); **A8** (`wasCellless` deleted). **A10** was accepted and restated correctly. The retail lane's **R4** (the prologue comment's `CommittedCellId` claim) and **R5** (`acdream-architecture.md` / `code-structure.md` still describing the deleted classes) are both fixed in the diff. --- ## Gate status observed - `dotnet build AcDream.slnx -c Release` → **succeeded, 0 warnings, 0 errors**. - `tests/AcDream.Runtime.Tests -c Release --no-build` → **1,125 / 0 / 0**. - `tests/AcDream.App.Tests -c Release --no-build` → **4,081 / 3 skipped / 0**. - Complete Release suite: **still not run and not recorded.** The contract is explicit that the new figure must be measured, not inherited from 11,027. Outstanding. - Two-client connected teleport gate: outstanding. **It must use an NPC target** (`@teleto` a creature into view), not a second player character — both round-1 MAJORs lived on the NPC arm, and `RemoteServerControlledVelocityCycle.Apply` early-returns for `0x50xxxxxx` guids, so a player-remote teleport exercises neither fix. Confirm at least one `[remote-teleport]` line per teleport with the expected `cause`.