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>
22 KiB
C4 route 4b-3 — architecture / adversarial review (2026-08-04)
Reviewer lens: adversary. Retail conformance is a separate reviewer's scope; this review asks only "does this break something, leak something, deadlock something, or leave an entity in a bad state?"
Subject: the uncommitted working tree on claude/acdream-physics-divergence-5aa784
at base 3e002993 (git diff HEAD plus the three untracked non-contract
files). Contract:
2026-08-04-c4-route-4b-3-contract.md.
Verdict: FAIL
Two MAJOR findings. Both are behaviour REGRESSIONS against 3e002993 — not
pre-existing residuals — and both sit on code paths the contract's own
invariant list names as load-bearing (invariant 5, the arm-count partition;
invariant 6 / D3, "the hook's whole point is that no locomotion state
survives a teleport"). Both are invisible to the specified two-client
connected gate, because that gate teleports a player character and both
defects are on the NPC/creature arm.
Everything else I checked held. Build is green (0 warnings); focused suites
pass Runtime 1,125 / 0 skips and App 4,078 / 3 skips at Release with
--no-build.
What I verified and found sound (do not churn)
- Invariant 1 (
StoresAcceptedDestination).ApplyAcceptedRemoteTeleport(src/AcDream.Runtime/Session/RuntimeRemotePlacementDriveController.cs:820-848) is byte-for-byte the far arm's shape minus theStopInterpolatingclear, including the currency-guardedStoreAcceptedDestinationPose. The partition is extended, not re-litigated. - Invariant 3 (no stranded entity). The teleport arm reaches
SubmitAndResolvethrough the sameTryExecuteAcceptedRemotePositionthe far arm uses; theDeferredCellarm still cancels synchronously withrestoreCancelledPark: true. No new park shape, no new withdrawal site. - Invariant 10.
TickLostCellDeadlines/TryDequeueExpiredLostCell/ArmLostFamilyDeadlinesstill have zero production callers (4 references, all insideRuntimeSetPositionState.cs; the rest are tests). The reaper stays inert. - Invariant 11 (route 1 unchanged). The shared builder got an overload,
not a third copy, and the route-1 overload forwards
committedCellId: canonical.FullCellId— its pre-slice value verbatim (RuntimeAcceptedPositionRouteRequests.cs:41-56). - D1's plumbing is honest.
PreMergeCommittedCellId = hadCanonical ? beforeCell : nullis measured beforeRefreshSnapshot(RuntimeEntityObjectLifetime.cs:1725-1770), never re-read, andnull("no prior canonical record") makesTryBuilddecline rather than fabricate a 0.RuntimeRemoteTeleportClassificationTestsgenuinely discriminates the fix from the shipped dead predicate — the companion test fails under the post-merge read. This is the strongest test in the diff. - The
_activePlacementOwnersdeletion is legitimate — see item 1 below. - Shadow suspend/restore across the hook.
RemoteTeleportHook'sReportCollisionEnd→ShadowObjects.Suspendremoves the entity from_entityToCells; the arm tails'LiveEntityShadowPublisher.TryPublishRemote→ShadowPositionSynchronizer.Sync→RefreshPositionRowsun-suspends it (ShadowObjectRegistry.cs:1336-1393). I specifically checked for a pose-gate that could skip the restore on a zero-distance teleport: the OnPosition-tail overload is ungated (RuntimeRemotePhysicsUpdater.cs:1175-1191); only the per-tick loop gates.TeleportRefused_…'sAssert.Single(fixture.Shadows.AllEntriesForDebug(), …)is a real proof of this, becauseAllEntriesForDebugenumerates_entityToCells. No invisible-and-intangible entity on this path. - Deletion completeness. All eight wiring sites in the contract's
inventory are cut, including the two the handoff missed
(
SessionPlayerComposition,LiveSessionResetManifest). Reset coverage is genuinely preserved:GraphicalSessionEventRoute.cs:152calls_remotePlacementDrive.DetachRoute. - Register bookkeeping. AD-42, AP-136, AP-137, AP-138 all updated in the diff; AP-138's Risk column does gain the teleport arm as a second producer, and AP-137 is rewritten rather than deleted. (One factual defect in the AP-137 rewrite — see A7.)
MAJOR findings
A1 (MAJOR) — the NPC arm silently stops arming ConstrainTo for every airborne-body packet; the code documents the opposite
File: src/AcDream.App/Physics/LiveEntityNetworkUpdateController.cs:1290-1311
(ToConstraintArm), consumed at :2643.
ToConstraintArm maps RemoteContactArm.AirborneSnap through its _ arm to
RuntimeRemoteAcceptedPositionArm.AirborneNoOperation — the one value
TryArmConstraintAfterOperation never arms — and justifies it at :1296-1297
with:
"There is no Runtime analogue of
AirborneSnap(a caller reaching it always returns before any arming call) … production never calls this with that arm."
That claim is false for the NPC arm. After
npcRouting = ApplyRemoteContactRouting(…) at :2600, the only early return
is the currency guard at :2619-2626, which is gated on
FarSnapPlacement or TeleportPlacement. Control then falls out of the
if (!snapSuppressedByStick || isTeleportRoute) block straight into
TryArmConstraintAfterOperation(ToConstraintArm(npcRouting.Arm), rmState) at
:2641-2644. AirborneSnap is returned by
ApplyRemoteContactRouting's free-flight carve-out at :1123-1150 for any
non-teleport classification whenever !remote.Body.InContact.
Concrete failure scenario. A creature remote (guid not 0x50xxxxxx) is
knocked off a ledge or jumps, so its canonical body has no contact plane
(Body.InContact == false). ACE broadcasts a wire-grounded UpdatePosition
(IsGrounded: true) at 5-10 Hz. The classifier returns Interpolate
(playerDistance < 96 m). Routing takes the airborne carve-out →
AirborneSnap → ToConstraintArm → AirborneNoOperation → no arm.
At 3e002993 the same packet armed: the post-operation site was passed the
ROUTE (earlyRemoteRoute), and OwnsAfterOperationConstraint(Interpolate) was
true with ConstrainAfterRouting true. So the arm count for this row goes
1 → 0, which is the "never zero times" half of contract invariant 5 and a
yes row of D4's partition table turning into a no. Retail's near branch
returns 1 @0x005163BE, so HandleReceivedPosition arms unconditionally
@0x00454272 — the divergence is in the wrong direction. The same 1→0 applies
to a far (SetPositionSimple) classification and to a null/Rejected*
classification whenever the NPC's body is out of contact.
Observable consequence: a creature that is knocked airborne on the first
accepted Position after spawn never sets IsConstrained at all, so the
ConstraintManager brake and IsFullyConstrained() (which gates
jump_is_allowed) stay dead for it; a creature already armed loses the
per-packet re-anchor (ConstraintPosOffset reset to 0) for the whole airborne
interval.
Why no test caught it. Proof obligation 3 asked for "a test that counts
arming calls per packet across the partition table's rows". What was delivered
(RuntimeRemoteSteadyStatePositionTests.TryArmConstraintAfterOperation_MatchesTheCompletePartition)
tests the Runtime predicate given an already-chosen arm value. Nothing tests
ToConstraintArm, and nothing counts arms per packet through OnPosition.
The mapping — the only new code in the arming path — is 100 % uncovered.
Fix direction. AirborneSnap is not AirborneNoOperation: retail's
airborne no-op is arg4 == 0 (the WIRE contact bit, return 0), while
AirborneSnap is acdream's carve-out keyed on the BODY's contact for a packet
whose wire bit said grounded — retail returns nonzero for that packet and
arms. Either map AirborneSnap to an arming value, or (better) make
ToConstraintArm total with an explicit AirborneSnap case plus a
_ => throw, and add the missing end-to-end arm-count test across the D4 rows
including the body-airborne ones.
A2 (MAJOR) — a teleported NPC gets a ~1,000 m/s synthesized ServerVelocity and an animation cycle planned from it
File: src/AcDream.App/Physics/LiveEntityNetworkUpdateController.cs:2526-2543
(synthesis) and :2683-2709 (consumption).
The NPC arm synthesizes a locomotion velocity for any packet that carries no wire velocity:
serverVelocity = (worldPos - rmState.LastServerPos) / (float)elapsed; // :2532
… rmState.ServerVelocity = authoritativeVelocity; HasServerVelocity = true;
rmState.LastServerPos is only advanced at :2681, i.e. after routing, so
at :2532 it still holds the pre-teleport position. For an admin teleport the
delta is the whole teleport distance over one packet interval — e.g. 192 m /
0.15 s ≈ 1,280 m/s. The tail then calls
RemoteServerControlledVelocityCycle.Apply(update.Guid, ae, rmState, rmState.ServerVelocity)
at :2704-2709, which runs ServerControlledLocomotion.PlanFromVelocity and
Sequencer.SetCycle(style, plan.Motion, plan.SpeedMod).
At 3e002993 this was unreachable for a teleport: the remotePlacementRequired
block sat in the SHARED section, above the IsPlayerGuid split and above
the NPC synth-velocity code, and it always returned. The diff deleted that
block and routed the teleport through the NPC tail instead, so the synthesis
now runs on every NPC teleport.
Concrete failure scenario. @teleto a drudge 200 m away while an acdream
client observes it. The teleport arm places it correctly, then the same packet
installs ServerVelocity ≈ 1.3 km/s and calls SetCycle(Run, speedMod≈huge).
RemoteServerControlledVelocityCycle.Apply's three guards do not help: the
body is grounded after the commit (rm.Airborne == false), the guid is not a
player guid, and rm.MoveTo was just set to Invalid by the teleport hook's
own CancelMoveTo — so the hook actively removes the last thing that would
have suppressed this. The creature stands at the destination playing a
run/sprint cycle at an absurd speed multiplier until the stale-velocity
watchdog fires (ServerControlledVelocityStaleSeconds = 0.60) or the next
packet's small delta replaces it.
This is directly contrary to teleport_hook's purpose (@0x00514ED0 exists so
that no locomotion state survives a teleport) and is exactly the
"stands with correct animation" clause of the contract's live gate — which
cannot see it, because the gate teleports a player character and
RemoteServerControlledVelocityCycle.Apply early-returns for 0x50xxxxxx
guids. The player-remote arm is accidentally immune because its own teleport
block returns at :2185, before the player synth-velocity at :2432-2443.
Fix direction. The teleport arm must suppress the synthesized velocity for
its own packet on the NPC arm too — either by advancing
rmState.LastServerPos to the destination before the synthesis, or (cleaner,
and symmetric with the player arm) by giving the NPC teleport its own tail
that skips the synth/cycle step. Whichever is chosen, add a test that asserts
the NPC sequencer's cycle is unchanged across a teleport packet; today nothing
in the tree looks at the animation layer for this arm.
MINOR findings
A3 (MINOR) — the stress test's teleport step now drives zero production code
File: tests/AcDream.App.Tests/World/LiveEntityLifecycleStressTests.cs:710-730.
The contract's deletion inventory says LiveEntityLifecycleStressTests
"constructs the controller and calls TryApply — its scenario must be
re-expressed against the canonical teleport arm, not dropped."
BeginDeferredTeleport is now four hand-written field assignments
(_remote.Body.Position = destination; _remote.CellId = …; Entity.SetPosition(…); Entity.ParentCellId = …). It exercises no placement,
no hook, no routing. RepeatedRetailRecallMotion_HiddenTeleportUnhide_…
(:252-296) would pass identically if the entire teleport arm were deleted.
The Hidden/DeferredShadowRestore half of the scenario still discriminates, so
this is a coverage loss rather than a false pass — but the mid-teleport
Hidden/UnHide interaction the fixture exists for is no longer tested against
the mechanism that now performs the teleport.
A4 (MINOR) — TeleportRefused_… does not discriminate the teleport arm from UnroutedCatchUp
File: tests/AcDream.App.Tests/Physics/LiveEntityNetworkRemoteTeleportPresentationTests.cs:157-206.
Every assertion in this test (body.Position == destination + offset,
Entity.Position == body.Position, IsSpatiallyVisible, one shadow entry at
body.Position) is also satisfied if the packet had classified null and
taken UnroutedCatchUp → ApplyInterpolate, because the body-to-target
distance is >192 m so AP-87's bodyToTarget > 4 m branch hard-places the body
at exactly the same wire pose and the same NPC tail then syncs entity and
shadow. The test does catch removal of the store_position fallback, so it
is not worthless, but the sibling commit test is the only one whose expected
value (+ FootSphereCenterLift) can only come from a canonical placement.
Adding one Assert.Equal(0u, …)-style discriminator (or asserting the hook
ran, as the routing-seam tests do) would close it.
A5 (MINOR) — the landing block's new comment states something false
File: src/AcDream.App/Physics/LiveEntityNetworkUpdateController.cs:2266-2272.
"A landing packet always classifies Interpolate (the teleport/cell-less arm dispatches earlier and returns before this block can be reached — D5)."
The teleport half is true; "always classifies Interpolate" is not. The landing
block is reached for a wire-grounded, body-out-of-contact packet with ANY
non-teleport, non-airborne-no-op classification — including SetPositionSimple
(>= 96 m), null (login window), RejectedAuthority, and RejectedData. The
hard-coded RuntimeRemoteAcceptedPositionArm.NearInterpolate at :2273 still
produces the correct arm count for all of those (every one of them is an
"arms" row), so this is a comment defect, not a behaviour defect — but process
rule 6 is explicit, and this is exactly the shape ("a comment asserting
behaviour the code no longer has") that six consecutive slices have shipped.
A6 (MINOR) — new per-packet allocations on the remote hot path
File: src/AcDream.App/Physics/LiveEntityNetworkUpdateController.cs:2148-2153,
:2370-2374, :2609-2613, and :1080-1088.
runTeleportHook: () => RunRemoteTeleportHook(update.Guid, entity.Id, () => IsCurrentPositionOwner(entity))
captures update, entity and this, so a display-class + delegate pair is
allocated for every remote accepted Position, not only for teleports (the
pre-slice tree allocated the inner lambda only when remoteHardTeleport was
true). Separately, teleportStatus.ToString() at :1087 is evaluated at the
call site, so the probe's own ProbeRemoteTeleportEnabled self-guard does not
prevent the string allocation. Given Slice I's "0 B/resolve" discipline this
is worth a Func<bool> cached per controller, or hoisting the probe behind
if (PhysicsDiagnostics.ProbeRemoteTeleportEnabled) at the call site.
A7 (MINOR) — D2's "unifies the player and NPC arms" is not delivered, and AP-137 now claims it was
Files: src/AcDream.App/Physics/LiveEntityNetworkUpdateController.cs:2186-2210;
docs/architecture/retail-divergence-register.md (AP-137 row, the "D2 — the
wire-airborne leftover shape" sentence).
The retail return-0 shape is implemented only inside the IsPlayerGuid block.
On the NPC arm a wire-airborne packet with a null/Rejected* classification
still falls through the synth-velocity block into
ApplyRemoteContactRouting, which either hard-snaps the body (AirborneSnap,
if the body is also out of contact) or enqueues/places it
(UnroutedCatchUp → ApplyInterpolate, if the body is in contact) and then
syncs the render entity and publishes the collision shadow. That is not
"AP-135's two bookkeeping writes only, no body/queue/render write, no leash
arm, return". The register row's claim that this "unifies player and NPC
remotes on one behaviour" is therefore factually wrong as shipped, which is a
register-rule-1 problem in its own right. (This is NOT a regression against
3e002993 — the NPC arm behaved this way before — so the fix can legitimately
be "scope the row to the player arm" rather than "implement the NPC half".)
A8 (MINOR) — dead local
src/AcDream.Runtime/Entities/RuntimeEntityObjectLifetime.cs:1727:
bool wasCellless = hadCanonical && beforeCell == 0u; has no remaining reader
(only the comment at :1759 mentions it). No compiler warning fires because
the initializer is not a constant. Delete it or the comment will outlive the
variable's meaning.
A9 (MINOR) — proof obligation 1 is not stated anywhere in the tree
The contract requires the ParkCollisionResidents-overlap-throw
unreachability argument (with 4b-1's B2 caveat about
HasOldPrefixPlacementDebt being a stall rather than a throw) to be stated
"in the contract-conformance section of the implementation commit". The work
is uncommitted, so I cannot verify it; nothing in the diff carries the
statement. The underlying property does hold by reading — the teleport arm
adds packets to the same one-operation-per-key machinery and opens no new
operation shape — but the statement is still owed.
A10 (MINOR, partly unverified) — the visibility-edge protection _activePlacementOwners provided has no replacement
I independently traced the deletion the implementer reported (item 1 of the
task). Their conclusion is right, their stated reasoning is not.
_activePlacementOwners was NOT write-never at 3e002993: the writer chain
was LiveEntityNetworkUpdateController (remotePlacementRequired →
_remoteTeleportController.BeginPlacement) → RemoteTeleportController.BeginPlacement
→ RemoteTeleportPlacementPresentation.Begin →
LiveEntityPresentationController.BeginAuthoritativePlacement
(RemoteShadowPlacementSynchronizer.cs:48 at HEAD). It becomes write-never
because this slice deletes that chain, which is exactly what the contract's
deletion-inventory row said. git grep over src + tests at HEAD confirms
no other production writer. So deleting the set and its four IsPlacementActive
gates changes nothing for any surviving path, and the four gates degenerating
to "always act" is correct.
What is not replaced is the protection: during the old multi-frame pending
placement, an intervening Hidden/UnHide edge was prevented from restoring a
shadow at an unresolved pose. The new teleport is synchronous inside
OnPosition, which mostly removes the window — but the canonical placement
publishes its receipt synchronously and the projection sink can raise a
visibility edge from inside it. I could not construct a reachable
Hidden/UnHide-during-placement path in the new design and am flagging this as
unverified, not as a defect.
Judgment on the three reported items
1. _activePlacementOwners deletion — sound, reasoning wrong. See A10.
The set had a real production writer at HEAD; it is the slice's own deletions
that kill it. The four consumers' degeneration is behaviour-neutral for every
surviving path. Not a defect. The commit message should say "its only writer
is deleted by this commit", not "the predicate was permanently false".
2. The two parallel OnPosition copies — a genuine structural hazard, and
it has already produced a defect. This is not a stylistic complaint: A2 is
precisely the two copies disagreeing. The player copy's teleport block returns
at :2185 before its synth-velocity at :2432; the NPC copy has no teleport
block at all and falls through its synth-velocity at :2526 on the way to
routing. A1 is the second instance — the player copy cannot produce
AirborneSnap (its landing block returns first) while the NPC copy can, so
the shared ToConstraintArm was written against the player copy's reality and
is wrong for the NPC one. The implementer's own report that "a sabotage of the
wrong copy left both tests green" is the same signal. The duplication should
be collapsed, or at minimum every arm tail should be one shared helper that
both branches call; shipping a third slice against these two copies without
that is how the next one of these lands.
3. Declining D7 (consolidating the post-placement currency guard into a
Runtime seam) — acceptable, and not the source of either MAJOR. The guard is
now duplicated at three App call sites (:2155-2161, :2393-2399,
:2619-2626) instead of two. I checked all three: the predicate is
character-identical (!IsCurrentPositionOwner(entity) || !ReferenceEquals(positionRecord.RemoteMotionRuntime, rmState)),
and all three sit BEFORE their arming call, preserving the R5 invariant. The
widened arm predicate (FarSnapPlacement or TeleportPlacement) is applied
consistently at the two that need it; the teleport-specific site is
unconditional, which is strictly stronger. So the duplication is not itself a
correctness hazard today. It is, however, the same duplication-of-invariant
pattern as item 2, and a fourth copy (route 5's projectile arm) is next in the
queue — consolidating it is now overdue rather than optional.
Gate status observed
dotnet build AcDream.slnx -c Release→ succeeded, 0 warnings, 0 errors.dotnet test tests/AcDream.Runtime.Tests -c Release --no-build→ 1,125 passed / 0 skipped / 0 failed.dotnet test tests/AcDream.App.Tests -c Release --no-build→ 4,078 passed / 3 skipped / 0 failed.- Complete-solution Release suite: not run by this reviewer (the contract's own gate; the new figure still has to be measured and recorded, not inherited from 11,027).
- Two-client connected teleport gate: not run, and note the finding above
that neither MAJOR is observable through it as specified — A1 and A2 both
need a creature teleport (
@teletoa drudge/mosswart into view), not a second player character.