The pinned contract plus the four review documents behind 6dc7ba51:
round 1 (retail FAIL 3 MAJOR / architecture FAIL 2 MAJOR) and round 2
(both PASS on the fixed diff).
Process findings worth carrying into the remaining routes:
1. The contract's 13 explicit "what must REMAIN true" invariants were the
fix for 4b-2's round-1 defect (a contract that said what must change but
not what must stay). They did NOT prevent a round-1 FAIL here. What the
round-1 MAJORs actually shared was a STRUCTURAL cause the invariant list
could not express: two parallel inline copies of the same routing tail.
An invariant list constrains behaviour; it cannot see duplication.
2. Both reviews independently found the same NPC synth-velocity defect
(retail R3 = architecture A2). Independent convergence on one finding is
the strongest signal this process produces — weight it accordingly.
3. The specified two-client gate could not have observed three of the four
MAJORs: it teleported a player character, and all three live on the NPC
arm. Caught by both reviewers before the gate ran, not after. Check that
a gate can structurally see the defect class it is gating.
4. A reviewer named a symbol that does not exist (RuntimeCollisionReportingState
.ForceEnd is a private helper, not the public entry point). The implementer
silently substituted the correct one (LeaveWorld) while reporting "no
disagreements with either review". Verify implementer claims against source
even when they report full agreement.
5. A new test asserted only what must NOT happen, so an emptied
ApplyWireAirborneLeftoverBookkeeping passed every test in the tree. Caught
in round 2 and closed with positive assertions, sabotage-verified. Negative
assertions alone cannot detect a deleted write.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
17 KiB
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;
the retail lane's are
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:
willBeDrTickedmoved 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.
runTeleportHooknow closes overcanonical/remoteand reuses the caller'sisCurrentPositionOwnerfor 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
ToConstraintArmand its doc says so — and all three callers arm immediately after the null check. - The NPC sticky-suppressed path still arms.
npcArmis initialised toUnroutedCatchUpandTryArmConstraintAfterOperationsits outside theif (!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 returns, 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 theIsAirborneNoOperationreturn (soNoPositionOperationnever reaches it) and before the synth-velocity block, the sticky probe, and routing. I confirmed the gate is exhaustive: forRuntimeAcceptedPositionSource.PositionEventa wire-airborne packet can only classifyNoPositionOperation(handled above),null, orRejected*— the classifier'seffectiveContactwidening applies only toSameIncarnationCreate, which this path never carries. - Player site (
:2390-2395) — the helper adds aremote.CellIdwrite the hand-rolled block did not have. I chased this specifically as the classic extraction hazard and it is benign:RuntimePhysicsState.CommitCanonicalCell:2145short-circuits onfullCellId == record.FullCellIdbeforeSetFullCelland before raisingCellCommitted, and the player arm wrote the identicalp.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 returns (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 writesbody.InWorld,TransientStateFlags.Active,record.ObjectClock,FullCellId, spatial residency, or visibility. Invariant 3 is not at risk._admissionBlockedis released in afinally, so the subsequent placement'sTryPrepareSetPositionBatch:230-235— which does refuse while a key is in_leavingor_admissionBlocked— is not blocked by the hook that ran immediately before it._mutationRevisionis bumped byLeaveWorld, but the placement's batch capturesexpectedMutationafter the hook (:238) and the prepare→install window is entirely inside the placement, so the bump cannot invalidate the teleport's own batch.Publishswallows observer exceptions (:1489-1494), and_leavingguards re-entrancy, so a force-end cannot escape as a throw into the packet path.TrimEmptyOwnerruns on the exit path, soCaptureOwnership'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.Equals 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<bool> 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 (
@teletoa creature into view), not a second player character — both round-1 MAJORs lived on the NPC arm, andRemoteServerControlledVelocityCycle.Applyearly-returns for0x50xxxxxxguids, so a player-remote teleport exercises neither fix. Confirm at least one[remote-teleport]line per teleport with the expectedcause.