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>
27 KiB
C4 route 4b-3 — retail-conformance review (2026-08-04)
Reviewer lens: does this diff do what the retail client does? Architecture, style, and layering are a separate reviewer's lane.
Subject: the uncommitted working tree on claude/acdream-physics-divergence-5aa784
at HEAD 3e002993 (git diff HEAD + four untracked files; the contract
docs/research/2026-08-04-c4-route-4b-3-contract.md is itself untracked and is
not part of the change under review).
Build: dotnet build AcDream.slnx -c Release — green, 0 warnings.
Focused check: AcDream.Runtime.Tests --filter Teleport — 29/29 pass.
Per process rule 3, neither is treated as evidence of conformance.
VERDICT: FAIL
Three MAJOR findings. R1 and R2 are contract requirements that were pinned and not met, and R1 additionally ships a register row asserting behaviour the code does not have — the exact defect class process rule 6 exists to stop. R3 is a newly-reachable retail divergence whose symptom is precisely what the connected gate recipe lists as its acceptance criterion ("stands with correct animation").
None of the three is hard to fix. The core of the slice — the D1 pre-merge cell,
the arm ordering, the single ConstrainTo site, the hook order, the flags — is
correct and verified against the decomp. The failures are at the edges the
findings chain keeps warning about: the NPC copy, and an unported half nobody
re-derived.
Part 1 — independent verification of the contract's retail claims
Every address in the contract's "Retail ground truth" section was re-read in
docs/research/named-retail/acclient_2013_pseudo_c.txt. All five load-bearing
facts confirmed.
| claim | verified |
|---|---|
MoveOrTeleport @0x00516330; branch @0x00516386 if (eax_8 != 0 || this_1->cell == 0) |
✓ line 284304ff. this_1 = this is assigned @0x00516334 from the incoming CPhysicsObj*, so this_1->cell is the body's own current cell, read at entry, before any placement. The whole D1 design rests on this and it is right. |
arg4 is read only @0x0051638E, after the branch |
✓ — the teleport branch's return 1 @0x00516438 executes without arg4 ever being touched. A teleport/cell-less packet places unconditionally: airborne wire bit, airborne body, any distance. |
teleport_hook @0x005163EF runs BEFORE SetPosition @0x00516420 |
✓ |
teleport_hook @0x00514ED0 action list and order |
✓ line 283115ff, exactly: CancelMoveTo(0x3C) @0x00514EDF → UnStick @0x00514EEE → StopInterpolating @0x00514EFD → UnConstrain @0x00514F0C → ClearTarget @0x00514F1B + NotifyVoyeurOfEvent(Teleported_TargetStatus) @0x00514F28 (one target_manager != 0 guard over the pair) → report_collision_end(this, 1) @0x00514F31. Each manager guarded on non-null. |
SetFlags(0x1012) @0x00516414 |
✓ = Teleport(0x002) | Slide(0x010) | SendPositionEvent(0x1000). acdream's AuthoritativeTeleportFlags (RuntimeAuthoritativePositionRouteClassifier.cs:197-200) is bit-for-bit the same against PhysicsSetPosition.cs:64-76. |
ONE ConstrainTo @0x00454272 via HandleReceivedPosition @0x00453FD0 |
✓ line 92896ff. The remote branch (arg2 != this->player @0x0045414D) is if (MoveOrTeleport(...) != 0) @0x00454254 → ConstrainTo(arg2, &arg2->m_position, …) @0x00454272 → return. Single site, shared by all three nonzero-returning branches, anchor read live off arg2->m_position (post-move). MoveOrTeleport discards SetPosition's error and returns 1 regardless, so retail arms even when the placement failed — confirmed. |
ZeroVelocity is local-player-only |
✓ set_velocity(player_2, {0,0,0}, 1) @0x004541B4 sits inside the arg2 == this->player + newer_event(TELEPORT_TS) branch. The remote branch writes no velocity at all. |
Contract errata (does not change any decision)
C1 (MINOR). The contract's pseudo-C excerpt presents return 0 @0x0051636D
as the fallthrough of the arg4 test. It is actually the else-label of an outer
sequence gate at @0x00516364 (if (-((eax_4 - eax_4)) == 0) — a Binary-Ninja-
mangled POSITION_TS/update_times[4] comparison that wraps the entire body).
The arg4 == 0 path does fall into the same label, so the behavioural reading
("writes nothing, returns 0") is correct; the listing just implies a flatter
control flow than the binary has. Worth correcting if the contract is reused.
C2 (MINOR, out of scope but noted). HandleReceivedPosition runs
unset_parent(arg2) @0x00454129 and SetPlacementFrame @0x00454142 (when
!HasAnims) before MoveOrTeleport. The contract's "recorded-not-consumed"
list (UnparentBeforeRouting / ApplyPlacementFrameBeforeRouting) is therefore
accurate — retail really does perform both ahead of the teleport branch, and
acdream still records-without-reading them. Correctly deferred, correctly stated.
Part 2 — answers to the four open questions routed to this review
Q1 — D1's evidence chain: CONFIRMED. The plumbing is necessary, not churn.
Verified end to end in source, not inferred:
RuntimeEntityObjectLifetime.TryApplyPositionmeasures the pre-merge value atsrc/AcDream.Runtime/Entities/RuntimeEntityObjectLifetime.cs:1723-1727(hadCanonical/beforeCell/wasCellless) before callingEntities.TryApplyPositionat:1728.- The merge onto the canonical record happens later, at
:1786-1789:Entities.RefreshSnapshot(canonical, snapshot, refreshPosition: acceptedPosition). RuntimeEntityDirectory.RefreshSnapshot:238→record.RefreshDerivedState(refreshPosition).RuntimeEntityRecord.RefreshDerivedState:230-237→SetFullCell(position.LandblockId, …), andSetFullCell:244-251writesFullCellIdoutright.
So after the merge canonical.FullCellId is the accepted wire cell, and the
classifier — which the App calls after the merge and before the prologue
rebucket (LiveEntityNetworkUpdateController.cs:1832-1840, ahead of
RebucketLiveEntity at :1865) — was reading the wire cell through the
route-1 Build overload. A wire cell of 0 is refused as RejectedData at
RuntimeAuthoritativePositionRouteClassifier.cs:321-322, before the cell-less
test at :402-404. The shipped remote cell-less predicate was unreachable.
D1's characterisation is exactly right and the fix is correct: the pre-merge
value is threaded on AcceptedPhysicsTimestamps.PreMergeCommittedCellId and
consumed through a genuine overload (never a third copy), with null meaning
"no opinion" rather than a fabricated 0.
One consequence D1 did not name — see R8 below.
Q2 — report_collision_end(this, 1) ↔ ShadowObjects.Suspend: there IS an unported half. See R2.
Q3 — the _activePlacementOwners deletion: safe. Confirmed behaviour-preserving.
IsPlacementActive had four consumers (Hidden suspend, UnHide restore,
RestoreOrdinaryShadowInsideProjection, SuspendOrdinaryShadowOutsideProjection).
The set's only writers were BeginAuthoritativePlacement /
CompleteAuthoritativePlacement, whose only production caller was the deleted
RemoteTeleportPlacementPresentation. With the writers gone the set is
permanently empty, so every consumer degenerates to the false branch — which
is precisely what the diff hard-codes. No Hidden/UnHide or visibility-edge
restore changes for non-teleport entities. HasDeferredShadowRestore and
_suspendedShadowOwners are untouched.
Q4 — RemoteTeleportSuffixIsQueuedBehindInitialAdmission: survives, correctly re-pointed.
It pins that a dormant initial-residence FIFO preserves teleport-sequence
ordering (0,1,2) across continuations. The two TeleportHookRequired assertions
were replaced with Assert.Null(...PreMergeCommittedCellId) on the same two
continuations — a real assertion about the new field, not a tautology. But see
R8: what that assertion proves is a behaviour change nobody recorded.
Part 3 — findings
R1 (MAJOR) — the D2 wire-airborne return-0 shape exists only on the player arm; the NPC arm still writes. AP-137 asserts otherwise.
Where: src/AcDream.App/Physics/LiveEntityNetworkUpdateController.cs:2188-2211
(the player-remote D2 block) has no counterpart on the NPC arm. The NPC arm's
only wire-airborne early return is :2516-2523, gated on
IsAirborneNoOperation(earlyRemoteRoute), which
(RuntimeRemoteSteadyStatePosition.cs:81-86) matches only the
NoPositionOperation disposition — never null and never Rejected*.
What happens instead. An NPC remote with a null or Rejected*
classification and a clear wire contact bit falls through :2525-2543, :2560,
and reaches ApplyRemoteContactRouting at :2600, where:
- body not in contact →
:1145-1148writesremote.Body.Position = worldPos; remote.Body.Orientation = rotation;and returnsAirborneSnap; - body in contact →
:1196-1203ApplyInterpolateenqueues a waypoint, then:2642arms the leash.
Retail: MoveOrTeleport @0x0051638E reads arg4, finds it 0, and falls to
return 0 — no body write, no queue write, and (because ConstrainTo sits
inside if (MoveOrTeleport(...) != 0) @0x00454254) no leash arm. Retail makes
no player/NPC distinction anywhere in MoveOrTeleport. The null
classification is reachable for every remote through the whole login window
(ClassifyRemoteAcceptedPosition returns null until _playerController
exists), so this is not a corner.
Why MAJOR rather than "pre-existing". The behaviour predates the slice, but two things make it a failure of this slice:
- D2 pinned the outcome — "unifies the player and NPC arms on one leftover- airborne behaviour" — and only half of it was implemented.
- The rewritten AP-137 row now states as fact: "This deletes the legacy
player-arm fallback's entity-revert quirk … and unifies player and NPC
remotes on one behaviour." That sentence is false against
docs/architecture/retail-divergence-register.mdline 287's own code. A register row that describes behaviour the code does not have is worse than no row: it is the thing that stops the next reader from finding the divergence.
Correct behaviour: give the NPC arm the same early return the player arm
now has — AP-135's two bookkeeping writes, then return — gated on the same
predicate (classification is null/Rejected* and !update.IsGrounded).
Or, if the intent is to keep the NPC snap deliberately, rewrite the AP-137
sentence to say so and give the asymmetry its own row.
Test gap that let it through: contract test-plan item 10 ("D2's leftover-
airborne shape: wire-airborne null-classified packet writes exactly AP-135's two
fields and nothing else") was not written for either arm, and item 11 (AP-135
preservation on both airborne no-op paths) was not written either. The D4
partition test that was written
(RuntimeRemoteSteadyStatePositionTests.TryArmConstraintAfterOperation_MatchesTheCompletePartition)
asserts the Runtime helper's arm→bool mapping, which is correct, but the
partition table's two "no arm" rows for the wire-airborne leftover case are a
property of the caller returning early — and that property is only true on
the player arm. The test cannot see the gap.
R2 (MAJOR) — report_collision_end(this, 1) is mismapped; the faithful port exists in-tree and is not called.
Where: src/AcDream.App/Physics/LiveEntityNetworkUpdateController.cs:254 —
ReportCollisionEnd: () => _physicsEngine.ShadowObjects.Suspend(localEntityId).
Retail @0x00514F31 → @0x00514620. report_collision_end(this, arg2) walks
this->collision_table. With arg2 != 0 the staleness/still-touching tests are
bypassed (@0x005146F3, @0x005146E7 both goto label_514706), so every
record is deleted and each is passed to report_object_collision_end
@0x00510A90, which fires weenie_obj->DoCollisionEnd(partnerId) on this object
(gated on this->state & 8 = REPORT_COLLISIONS) and
DoCollisionEnd(this->id) on the partner (gated on the partner's own 0x8, and
skipped wholesale when the partner carries state & 0x200000). It is a
force-end-all with bidirectional notification.
What acdream substitutes. ShadowObjectRegistry.Suspend
(src/AcDream.Core/Physics/ShadowObjectRegistry.cs:1480-1499) removes the
entity from every cell collision list while retaining its registration. That
method's own doc comment says what it is: "the registry counterpart of retail
CPhysicsObj::remove_shadows_from_cells" — a different retail function,
which teleport_hook does not call. (Retail's shadow removal for a teleport
happens later and internally, inside SetPositionInternal.)
The faithful port already exists. RuntimeCollisionReportingState
(src/AcDream.Runtime/Physics/RuntimeCollisionReportingState.cs) is an explicit
port of CPhysicsObj::collision_table and publishes bidirectional
ObjectCollisionEnd with the same ReportCollisions gates on both sides
(PublishResolvedObjectEnd:1225-1270). It has a private ForceEnd(record, key)
(:1435-1450) that is exactly report_collision_end(this, 1) — reached today
only from the destruction / leave-world / batch-retirement edges (:547,
:881, :913, :943). The teleport hook does not reach it.
The canonical placement does not cover the gap: the SetPosition batch dispatch
runs EndExpiredObjectCollisions(..., force: false, ...) (:493-500), which is
retail's other call — handle_all_collisions → report_collision_end(this, 0)
@0x005147F0. Retail runs both: force-end-all in the hook, then the ordinary
non-forcing pass inside the placement.
Observable delta. After a remote teleports away, any object still holding a
CollisionRecord against it keeps it until the ordinary ~1 s staleness pass, and
neither side receives the immediate ObjectCollisionEnd retail fires. Retail
ends it on the spot, on both sides.
Added-divergence half. Conversely, suspending the shadow at hook time is
something retail does not do there: between the hook and the placement — and,
on any non-commit outcome, until the next SyncRemoteShadowToBody — the remote
is non-collidable. The window is bounded (the placement's
Register/RefreshPositionRows both clear _suspendedEntities, and the DR
tick's ShouldSynchronizeShadow re-syncs on the next pose delta), so this is a
frames-scale residual rather than the #184 permanent class — but it is real, and
it is not what retail does at this call site.
Contract compliance: the contract's open question 2 required the reviewer to
"confirm … or file the delta on the AP-137 successor row." The delta is real
and the row does not mention report_collision_end, ShadowObjects.Suspend, or
the notification half. No row anywhere in the register covers it.
Correct behaviour: route the hook's sixth action at a
RuntimeCollisionReportingState force-end entry point (the analogue of
ForceEnd), and drop or separately justify the shadow suspension. If the
force-end is deliberately deferred, it needs its own register row naming
@0x00514F31 and @0x00514620.
R3 (MAJOR) — a teleported NPC now plans a RunForward cycle from a teleport-distance-derived velocity.
Where: src/AcDream.App/Physics/LiveEntityNetworkUpdateController.cs:2525-2543
then :2684-2709.
Newly reachable. In the pre-slice tree the shared if (remotePlacementRequired)
block (HEAD:1958) sat above the NPC path and returned on every branch, so a
teleport packet never reached the NPC synth-velocity code. This slice deletes
that block, and a teleport-classified NPC packet now flows straight through it.
What it does. serverVelocity is update.Velocity; when ACE omits velocity
(the ordinary UpdatePosition case) :2530-2532 synthesises
(worldPos − rmState.LastServerPos) / elapsed — across the whole teleport
distance, over one packet interval. :2536-2537 installs it. At :2704
RemoteServerControlledVelocityCycle.Apply then calls
ServerControlledLocomotion.PlanFromVelocity
(src/AcDream.Core/Physics/ServerControlledLocomotion.cs:40-62), which returns
MotionCommand.RunForward for anything above RunThreshold, and
ae.Sequencer.SetCycle(style, RunForward, …) fires. None of the guards block it:
rm.Airborne is false at a walkable destination; rm.MoveTo was just
invalidated by the hook's own CancelMoveTo; a standing NPC's Ready passes
CanApplyVelocityCycle. SpeedMod is clamped, the motion is not.
The cycle persists until the next packet or the 0.6 s
ServerControlledVelocityStaleSeconds pass in RuntimeRemotePhysicsUpdater.cs:198-208.
Retail. The teleport branch writes no velocity at all (contract invariant 6,
verified above), and teleport_hook's first action cancels the moveto. A retail
observer sees the creature appear at the destination and stand. acdream will
show it sprinting in place.
Impact is animation-only — ServerVelocity drives cycle selection, not
translation (RuntimeRemotePhysicsUpdater.cs:188-193) — so this is not a
body-motion defect. But "stands with correct animation" is a literal line in the
contract's own two-client gate recipe, so the gate is expected to catch it if it
is run against an NPC. It will not be caught against a player remote:
RemoteServerControlledVelocityCycle.Apply:27-49 returns early for player GUIDs.
Correct behaviour: the teleport arm must not leave a teleport-derived
ServerVelocity installed. The narrow fix is to skip the synth (and the cycle
apply) for a teleport-classified packet, which is also what invariant 6's "the
teleport arm must not add a velocity write" was reaching for; the register row
for the AP-80 velocity-cycle adaptation should name the exclusion.
R4 (MINOR) — stale comment: the prologue rebucket no longer feeds the classifier's CommittedCellId.
LiveEntityNetworkUpdateController.cs:1848-1853 still reads: "it is the only
site that moves an ordinary moving remote's draw bucket, commits its canonical
FullCellId (which feeds back as the classifier's own CommittedCellId and as the
ConstraintDistance cell key)…". After D1 the remote classifier's
CommittedCellId is the pre-merge value threaded on
AcceptedPhysicsTimestamps, never the rebucket's commit. The comment sits four
lines below the classification call the slice rewrote and directly contradicts
the fix; a reader who believes it would "simplify" D1 away. Correct it in the
same commit (process rule 6). The ConstraintDistance half is still true.
R5 (MINOR) — the architecture docs still describe the deleted classes as live.
docs/architecture/acdream-architecture.md:314, 316, 469-471 and
docs/architecture/code-structure.md:265, 267, 420-423 document
RemoteTeleportController / RemoteTeleportPlacement as the live remote
placement owners. CLAUDE.md: "When the architecture doc and reality diverge,
update one or the other — never leave them out of sync." The deletion inventory
covered every .cs wiring site but not these two docs.
R6 (MINOR) — the [remote-teleport] probe can report hookRan=True for a hook that did nothing.
RunRemoteTeleportHook (LiveEntityNetworkUpdateController.cs:234-256)
re-resolves the RemoteMotion and EntityPhysicsHost by GUID, rather than
using the rmState the arm already holds and hands to
ApplyAcceptedRemoteTeleport. Every action is remote?. / host?., so on a
null lookup all six silently no-op and RemoteTeleportHook.Execute still
returns true — and PhysicsDiagnostics.LogRemoteTeleport prints
hookRan=True. Since the probe exists specifically to satisfy process rule 5
("a clean-looking live session is not a passed gate"), a probe that cannot
distinguish "ran" from "no-opped" undercuts the gate it was added for. Passing
rmState through (it is in scope at all three call sites) removes both the
mismatch risk and the false-positive.
R7 (MINOR) — hookRan is consumed only by the probe. Correct, but say so.
ApplyRemoteContactRouting:183 captures the hook's currency result and the
routing proceeds regardless. That is retail-faithful — retail has no
currency concept and runs the hook before knowing the placement outcome — but
the code reads as if a false were being dropped on the floor. One line of
comment naming @0x005163EF's unconditional ordering would close it.
R8 (MINOR) — D1's null-widening has a second, undocumented producer.
The AP-137 rewrite names exactly one new null-producing reason: "the merge
observed no PRIOR canonical record for this entity." There is a second: the
dormant initial-residence enqueue path. TryApplyPosition returns at
RuntimeEntityObjectLifetime.cs:1705-1721 (EnqueueDormant) before the
:1767-1770 PreMergeCommittedCellId write, so those timestamps carry null —
which the diff's own changed assertion in
RuntimeInitialCreateResidenceStateTests (Assert.Null(...PreMergeCommittedCellId))
now pins. A TELEPORT_TS-advancing packet on that path previously classified
SetPosition (via TeleportAdvanced, which is unaffected by the cell input) and
now classifies null → UnroutedCatchUp. I could not determine from static
reading whether the App's OnPosition reaches ClassifyRemoteAcceptedPosition
for an enqueued packet — flagging as unverified rather than guessing. Either
way the AP-137 row should name the producer the test now pins.
R9 (MINOR) — the D2 block's "AP-135's two writes" are the packet's second copy.
LiveEntityNetworkUpdateController.cs:2206-2209 writes LastServerPos /
LastServerPosTime, but :2106-2107 already wrote both unconditionally for this
packet (and rmState.CellId at :2076), with a second DateTime.UtcNow read
producing a slightly later timestamp. Harmless, but the block's comment reads as
though these are the AP-135 writes being preserved, when the preservation
actually happened 100 lines earlier. Contract invariant 8 is satisfied on both
arms (player: :2076 + :2106-2107; NPC: :2519-2521) — this is a clarity
issue, not a correctness one.
Part 4 — what the diff gets right (verified, not assumed)
Recorded so a fix round does not disturb it:
- D1 plumbing. Pre-merge cell measured by the same
TryApplyPositioncall that merged the packet, never re-read;nullpropagated honestly through bothTryBuildandBuild; route-1 semantics untouched (the old overload still passescanonical.FullCellId,RuntimeAcceptedPositionRouteRequests.cs:38-54). The discriminating test pair (CellLessRecord_…/CompanionTest_NonzeroPreMergeCellWithNoTeleportAdvance_…) genuinely distinguishes the fix from the shipped dead predicate. - D3 hook.
RemoteTeleportHook.Executepreserves retail's six actions in retail's order with a currency re-check between each;WeenieError.ITeleportedis 0x3C;EntityPhysicsHost.NotifyTeleportedis the correct one-to-one for theClearTarget+NotifyVoyeurOfEventpair under retail's single guard. - D4 single arming site. The legacy pre-operation call is gone; the
post-operation site is the only one, matching @0x00454272. Dropping the
ConstrainAfterRoutingroute-flag check is behaviour-preserving —InterpolateandSetPositionSimpleboth carryConstrainPhase.AfterPositionOperation(RuntimeAuthoritativePositionRouteClassifier.cs:473), andNoPositionOperation'sNoneis already excluded by the arm mapping. Keying on the routing outcome rather than the raw classification is the right call: it is the only input that separates a groundedUnroutedCatchUp(arms, retail's nonzero analogue) fromAirborneSnap(does not). - D5 ordering. The teleport dispatch is the first thing
ApplyRemoteContactRoutingdoes, ahead of the!remote.Body.InContactcarve-out; on the player arm it precedes both the wire-airborne block and the landing block; on the NPC arm the sticky gate is widened (!snapSuppressedByStick || isTeleportRoute) rather than duplicated. All three match "decided beforearg4".UnStickbeing the hook's second action is the right justification for the sticky widening. - D6/D7.
TryApplyGenericRemoteRenderPosegate unchanged and now stated;TryAdoptWireCellAfterRoutingsuppression widened to the teleport arm; the re-entrancy guard covers both placement arms and sits before the arming on both, preserving the R5 invariant. - Invariant 1.
ApplyAcceptedRemoteTeleportreuses the far arm'sStoresAcceptedDestinationpartition andStoreAcceptedDestinationPoseunchanged — the partition was not re-litigated. - Invariant 2. Both teleport tails sync the render entity from the resolved
body and publish the shadow (
:2171-2184player,:2719-2732NPC). The placement writes the resolved cell back throughRuntimeSetPositionState.cs:5013(remote.CellId = result.CellId), soentity.ParentCellIdreally is the resolved cell. - Invariant 4. The classifier's teleport branch carries
StopInterpolating: falsedeliberately; the queue clear comes from the hook'sInterp.Clear(), matching @0x00514EFD. - Invariant 10. No new production caller for
TickLostCellDeadlines/TryDequeueExpiredLostCell;ArmLostFamilyDeadlineskeeps its single pre-existingParkDeferredcall. - Register bookkeeping. AP-137 rewritten in place rather than deleted (correct — the contract overrides the handoff here); AD-42's dead citation removed; AP-136 and AP-138 updated to the two surviving non-Position rebucket writers; AP-138's Risk column gains the teleport arm; AP-135 untouched. All correct except the false sentence named in R1.
Part 5 — gate evidence not present
Stated, not held against the verdict:
- The complete Release suite figure the contract's Gates section requires ("measure and record the new figure; do not inherit 11,027") is not recorded — the change is uncommitted and there is no commit message. I ran the build and a focused filter only.
- The two-client connected teleport gate is user-run and cannot be evidenced by a reviewer. Note that per R3 an NPC target is the discriminating case: a player-remote teleport will not exercise the velocity-cycle path at all.
- Contract test-plan items 10 (D2 shape) and 11 (AP-135 preservation on both airborne no-op paths) have no corresponding test in the diff. Item 10's absence is what left R1 invisible.
Recommended fix order
- R1 — give the NPC arm the same D2 return-0 shape, and make the AP-137 sentence true (or split the asymmetry into its own row). Add contract test 10 for both arms.
- R2 — route the hook's sixth action at the collision-reporting force-end, or file the delta on AP-137 with the @0x00514F31/@0x00514620 citations.
- R3 — exclude a teleport-classified packet from the NPC synth-velocity install and the cycle apply; note the exclusion on the AP-80 row.
- R4/R5 — the two stale-documentation fixes, same commit.
- R6-R9 — clarity and probe-honesty, at the implementer's discretion.