Ports retail's missile Position handling into the canonical Runtime
placement owner instead of the deleted ApplyAuthoritativePosition
short-circuit. The Create/residence-window halves of the projectile
pipeline (RuntimeProjectile binding, TryBind's adopted-body branch,
the collision/shadow registration) were already canonical from prior
slices; this closes the remaining gap — how an ACCEPTED Position for
an in-flight missile is classified, placed, and presented.
Byte-decode (Step 1 hard gate, before any code was written):
CPhysicsObj::MoveOrTeleport @0x00516330-0x00516438 disassembled from
the PDB-paired binary (Capstone, x86 32-bit thiscall). `ret 0x10`
establishes four stack args; [esp+0x7c] (arg5, the velocity pointer)
is never referenced in any of the three branches (teleport/near/far).
The retail reviewer independently reproduced this by searching the
whole function body for the `24 7c` mod/rm+disp8 encoding a
`[esp+0x7c]` read would require and found zero occurrences. This
retired a fabricated `?? Vector3.Zero` fallback in the deleted method
— retail's PositionPack::UnPack initializes an absent velocity to
zero and MoveOrTeleport never installs it; the projectile's Vector
channel (RuntimeProjectilePhysicsUpdater.ApplyAuthoritativeVector)
remains the sole velocity authority for a missile. D-P5 in the
contract; the Runtime seam commits no velocity from the Position
packet at all.
The unbound-missile fix: RuntimeEntityObjectLifetime's
ClassifyRemoteAcceptedPosition now derives ProjectileAuthoritative
from a CONJUNCTIVE predicate — the Missile bit AND a bound
RuntimeProjectile whose Body is the canonical PhysicsBody — never the
bit alone. Retail places every non-player CPhysicsObj unconditionally
(there is no missile-specific placement gate in MoveOrTeleport or its
callers), so an unbindable or not-yet-bound missile taking the
ordinary remote tail is retail-faithful, not a fallback: the earlier
bit-only discriminator would have silently frozen it instead.
AP-141 records this as a deliberate, recorded divergence, not
fidelity. Retail mechanically WOULD arm a missile's ConstrainTo leash
on any nonzero MoveOrTeleport return: HandleReceivedPosition
@0x00453FD0's only kind test is player-vs-not, ConstrainTo
@0x00454272 has no kind test of its own, and CPhysicsObj::ConstrainTo
@0x00510520 creates a PositionManager on demand via
MakePositionManager @0x00510523 if one doesn't exist. acdream
deliberately does not construct that EntityPhysicsHost/
PositionManager/InterpolationManager chain for a ballistic body — the
route-5b split the C4 route 5 contract rejected — so a live missile
never shows an armed leash and never catches up via the near/
UnroutedCatchUp policy. This divergence is safe specifically because
ACE never sends UpdatePosition for a missile
(references/ACE/Source/ACE.Server/WorldObjects/WorldObject_Tick.cs:
333-334, SendUpdatePosition() commented out inside the
PhysicsState.Missile branch at :265) — every half of this row is
deterministic-test-gated only, never exercised against a real server.
AP-141 also records the surviving ConstrainTo re-anchor divergence
under clause (b): for the adopted-body case (TryBind's shared-body
branch — an ordinary remote whose Missile bit is set by a later
State packet, so it still carries a live RemoteMotion), acdream now
ports retail's teleport-branch and far-branch StopInterpolating
action (Interp.Clear()), but never re-arms or re-anchors the
inherited ConstrainTo leash the way retail's HandleReceivedPosition
@0x00454254/@0x00454272 does on every nonzero return. The risk
column's earlier wording — that a stale leash "would drag the body
toward a stale anchor" — was wrong and is retracted in this same
commit: ConstraintManager.ConstraintPos is write-only in both retail
and the port (never read by AdjustOffset), and
ConstraintManager::adjust_offset @0x00556180 only tapers or zeroes an
already-composed per-tick offset while InContact — a leash brakes
motion the interp/sticky chain already produced, it cannot pull
anything toward the anchor. The real residual is one tick of un-reset
brake accumulator, contact-gated, and it cannot move an airborne
far-snapped missile at all (the clamp branch does not run while
airborne).
NO CONNECTED GATE EXISTS for this route, by design: ACE never sends a
missile UpdatePosition (see above), so retail's own server never
exercises this code path in play. Every proof obligation here is
test-gated only — Runtime and App-level fixtures constructing the
packet directly — never a live client/server capture.
Three review rounds closed 8 MAJOR findings before this landed:
round 1 (A1 App discarded the seam's status; A2/R1 silent swallow on
an unbound missile; A3/R2 the adopted-body teleport_hook never
wired; A4/A5 zero Runtime/App test coverage); round 2 (a
ParentCellId regression introduced by round 1's own R6 finding,
which the retail reviewer retracted the following round as factually
wrong — the fix here is the REVERT to record.FullCellId, not the
relocation round 1 shipped; B2 the far-branch StopInterpolating skip
never extended to the adopted-body case; residual App/Runtime store-
path coverage; a per-packet closure contradicting the file's own
#315 cached-delegate pattern). Round 3 closed on coverage alone (no
defect): the Advance() retry arm's projectile branch — added at
round 2, semantically reordered at round 2's B5 fix (skip prediction
invalidation on a re-parked Contention, since it writes nothing) —
had never been executed by any test; two new tests drive it directly
and are sabotage-verified against both the reordering and the
retry-arm's own SyncProjectilePresentation call site. The one
recorded defect this campaign produced (the ParentCellId regression)
was caused by complying with a review finding that its own author
later retracted — the standing lesson recorded for future rounds is
that review findings are evidence to re-verify against the code, not
commands to obey unconditionally.
Complete Release suite: 11,063 passed / 4 skipped / 0 failed
(baseline 11,036 at 30d3d114, +27 new tests across this campaign).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Flips the last remote classification (SetPosition: teleport-advanced and
cell-less) onto 4b-1's RuntimeRemotePlacementDriveController, runs retail's
teleport_hook before the placement, and deletes the legacy remote-teleport
machinery. Contract: docs/research/2026-08-04-c4-route-4b-3-contract.md.
Retail: MoveOrTeleport @0x00516330's branch @0x00516386 -> teleport_hook
@0x005163EF -> SetFlags(0x1012) @0x00516414 -> SetPosition @0x00516420 ->
return 1 @0x00516438. The hook @0x00514ED0 runs BEFORE the placement and
regardless of its outcome. Retail places this branch unconditionally, at any
distance and any contact state (arg4 is read only @0x0051638E, after the
branch) — which is what retires AP-137's cell-less enqueue-vs-place delta.
D1 — the classifier's cell-less input is now the PRE-merge committed cell.
Retail's predicate is `this_1->cell == 0`, the BODY's own cell at
MoveOrTeleport entry (this_1 is assigned from this @0x00516334). acdream fed
the POST-merge canonical.FullCellId, which RefreshSnapshot ->
RefreshDerivedState -> SetFullCell has already stamped with the accepted wire
cell; a zero wire cell fails validation into RejectedData first. The shipped
remote cell-less predicate was therefore dead code, not merely different from
remotePlacementRequired. Threaded via a builder overload; route 1's overload
is untouched. The graphical !IsSpatiallyVisible arm of
projectionRequiresTeleportHook is deleted — a presentation predicate with no
retail analogue that fired the teleport machinery on a routine hot path.
Deleted: RemoteTeleportController (605), RemoteTeleportPlacement (85),
RemoteShadowPlacementSynchronizer (49), their 1,709 lines of tests, the
remotePlacementRequired predicate, the TeleportHookRequired plumbing, the
legacy pre-operation ConstrainTo fallback, and the player arm's legacy
!IsGrounded fallback. Net -2,030 lines.
Structural fix (two independent Opus reviews, round 1 FAIL/FAIL): three of the
four MAJORs were one defect — OnPosition carried two parallel inline copies of
the routing tail (player-guid, NPC-guid) that had drifted. Extracted
RunRemoteArmTail (3 call sites) and ApplyWireAirborneLeftoverBookkeeping (2),
both branches now share one implementation.
A1 ToConstraintArm mapped AirborneSnap -> AirborneNoOperation, so the NPC
arm armed ConstrainTo ZERO times for an out-of-contact wire-grounded
creature — a regression this slice introduced while closing a
structurally identical hole. Now maps to NearInterpolate; switch made
total with a throwing default proven unreachable.
R1 D2's write-nothing shape existed on the player arm only; NPC packets
fell through and wrote the body. Retail makes no player/NPC distinction.
R2 report_collision_end(this,1) @0x00514F31 was bound to
ShadowObjects.Suspend, a port of a DIFFERENT retail function
(remove_shadows_from_cells) that teleport_hook never calls. Now routes
to RuntimeCollisionReportingState.LeaveWorld, which wraps the private
ForceEnd in an admission-blocking transaction so a DoCollisionEnd
callback cannot recreate the contact table.
R3/A2 A teleported NPC synthesized ServerVelocity from the teleport distance
(~1,000+ m/s) and planned a run cycle from it. Both the install and
RemoteServerControlledVelocityCycle.Apply now gate on !isTeleportRoute.
BISECT HAZARD — A1's fix is correct only BECAUSE R1 landed. AirborneSnap is
reachable wire-airborne on the NPC arm only while D2's shape is missing there.
Reverting R1 alone silently inverts A1 into the opposite divergence: arming
where retail returns 0. Revert both or neither.
Also in the velocity hunk: the NPC block's two !IsPlayerGuid(update.Guid)
guards were dropped when it was wrapped in `if (!isTeleportRoute)`. Safe — all
five exit paths of the enclosing IsPlayerGuid block return, so the predicate is
unconditionally false below it — but it was unremarked by both reviews.
Register: AP-137 REWRITTEN (not deleted) to the surviving acdream-only
divergences — null classification during the login window and Rejected*
through UnroutedCatchUp keep a row. AD-42's RemoteTeleportController citation
retired; AP-136/AP-138 writer lists corrected to the two surviving non-Position
rebucket writers; AP-138 gains the teleport arm as a second producer of the
visible-without-collision residual (retirement path remains #309). AP-135 is
untouched and its two airborne bookkeeping writes are preserved on both arms.
AP-131 does not retire; #276 does not close.
Proof obligation 1: ParkCollisionResidents' overlap throw stays unreachable —
the teleport arm adds packets to the same TryBeginExclusiveAuthoredPlacement
one-operation-per-key machinery the far arm uses, opens no new operation shape,
and every DeferredCell outcome cancels synchronously with
restoreCancelledPark: true. The guarded property remains
HasOldPrefixPlacementDebt's stall, not a throw (4b-1's B2 caveat stands).
Correction to an earlier claim: LiveEntityPresentationController's
_activePlacementOwners was NOT write-never at HEAD —
remotePlacementRequired -> BeginPlacement -> Begin -> BeginAuthoritativePlacement
was a live writer chain. It becomes write-never BECAUSE this slice deletes that
chain, which is why deleting the dead half is behaviour-preserving.
Probe: ACDREAM_PROBE_REMOTE_TELEPORT=1 emits one [remote-teleport] line per
routed arm (guid, cause, hook-ran, placement status). TEMPORARY, strip with the
probe family.
Carried, disclosed not fixed: no dedicated bidirectional collision-partner test
for R2 (the wiring, not LeaveWorld itself, is what lacks coverage); the
stress test's teleport step drives hand-written field assignments rather than
the canonical arm; the per-packet runTeleportHook closure allocation (network
path, not the resolve path Slice I's 0 B discipline governs — file before
route 5 adds a fourth call site). B2: IRuntimeCollisionReportObserver has zero
production implementations, so retail's bidirectional DoCollisionEnd half still
reaches no gameplay consumer — this fix closes the wrong-function binding, not
that nobody listens.
Complete Release suite MEASURED at 11,013 passed / 4 skipped / 0 failed
(baseline 11,027/4/0; net -14 = ~33 deleted test cases against ~19 added).
Neither known flake fired (#302 PortalProjectionTests GC-allocation, #308
NakEmissionTests wall-clock).
STILL OWED: the two-client connected gate, which MUST use an NPC/creature
teleport target. Both round-1 MAJORs lived on the NPC arm and the velocity
cycle early-returns for 0x50xxxxxx guids, so a player target structurally
cannot observe A1, A2, or R3.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Regression from 7f1c1f5a (C4 route 4b-2). A remote player who recalled in,
arrived, and stood still was permanently absent from the world render AND the
radar while remaining fully simulated — 71 healthy physics ticks with contact
and walkable, interpolation enqueues, equipment attached, chat visible.
Route 4b-2 is the first commit that lets an ordinary remote UpdatePosition open
a canonical SetPosition. A park publishes a synchronous Withdraw that tears down
presentation registrations; only TryPublishPlace restores them.
RestoreParkWithdrawal — added in the same slice — restores InWorld, the object
clock, and canonical residency, i.e. the Runtime half only. Eight Opus reviews
verified those three fields and the tests asserted exactly them, so the suite
stayed green while the entity was invisible.
Why it is intermittent: the presentation half IS restored incidentally by the
per-packet prologue rebucket for a MOVING remote. It only sticks when the
entity parks on its FINAL accepted Position and then goes idle, because ACE
stops broadcasting for a stationary entity, so no later packet arrives to
re-publish it and nothing else re-drives.
The fix publishes a RuntimePlacementProjectionKind.WithdrawalRestored receipt on
the one ordered placement stream, acknowledge-only in Runtime (the parked
operation is already retired by CancelCoreDeferred), which the App sink maps to
the exact inverse of its own TryPublishWithdrawal: the projection half (bucket,
IsSpatiallyProjected, IsSpatiallyVisible, spatial indexes, RefreshPresentation)
plus the publish half (_worldState, _worldEvents, _effectPoses,
_localPlayerShadow, visibility sinks). Applied with commitPose: false, because
the withdrawal never moved the sidecar; a test feeds a deliberately wrong
position to pin that.
Two alternatives were refuted on measurement, not preference. Routing the
restore's SetFullCell through CommitCanonicalCell cannot fire on the shipped
remote path at all — the prologue rebucket has already recommitted a non-zero
FullCellId before the merge cancels the park, so no cell edge remains — and it
never touches the publish half regardless. Extending RestoreParkWithdrawal
directly reduces to the same receipt, since Runtime must not reach behind the
host sink.
Gated on the entity ending the rollback canonically whole (FullCellId != 0 &&
InWorld) rather than on residencyRestored, which is false on the shipped remote
path and would have made the fix a no-op. AP-136's quiescing-prefix refusal arm
is preserved: no receipt, entity stays withdrawn.
Corrects my own framing of the defect: _worldState/_worldEvents/_effectPoses are
lost but are NOT what kills render and radar (_worldState is the plugin
IGameState; _effectPoses is the pose registry, not entity.MeshRefs). The
load-bearing casualties are the visibility sinks and the
IsSpatiallyProjected/IsSpatiallyVisible + bucket removal that gates the radar.
Register: AD-63 filed (selection deliberately not restored — user intent),
AP-136 amended (its "restored visible" claim covered only the canonical half;
the gap was a defect, not a divergence). ShadowObjectRegistry.Suspend stays
out of scope per AP-136.
Seven-revert discrimination table including one that proves the test is not
merely re-checking the bucket. Suite 11,023 passed / 4 skipped / 0 failed.
Live gate is user-run and folds into #309: two clients, ACDREAM_PROBE_PARK=1,
recall a remote in and let it stand still; acceptance is
[park-restore] ... presentation=True for that guid plus a visible model and a
radar blip.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A remote observed in acdream landed on a sloped roof and froze; the server slid
on, the gap passed AP-87's 4 m threshold, and the body snapped — the visible
blip. Live probe capture, two adjacent ticks 63 ms apart:
t=88420671 rsInContact=True rsOnWalkable=False rsIsOnGround=True
bodyCpNz=0.6097 floorZ=0.6642 steep=True gravity=True
vel=(2.146,2.264,-3.549)
t=88420734 contact=True onWalkable=True <- forced against the sweep
gravity=False <- cleared
velBeforeZero=(2.146,2.264,0.000)
moved=0.0000 <- and every tick after
The roof is 52.4 degrees against a 48.4 degree limit, so acdream's classifier
was CORRECT and was then overruled. Four independent links each froze the body
on their own: a per-tick force of Contact|OnWalkable, a per-tick velocity zero,
a Gravity clear at landing, and a landing edge testing IsOnGround
(= inContact || ...) instead of OnWalkable. The tick called
HandleAllCollisions alone — the tail of SetPositionInternal without its prefix.
Retail simulates remotes locally and derives these bits rather than asserting
them: CPhysics::UseTime @0x00509950 iterates the whole object table;
update_object @0x00515D10 gates only on parent/cell/FROZEN with no
is_player fork; SetPositionInternal @0x00515330 sets CONTACT from
contact_plane_valid @0x00515430 and ON_WALKABLE from contact_plane.N.z vs
floor_z @0x00515465-@0x0051548E before handle_all_collisions @0x005154FE;
set_on_walkable @0x00511310 fires HitGround @0x00511364 / LeaveGround
@0x00511346 edge-triggered with no ownership gate; calc_acceleration
@0x00510950 zeroes only when CONTACT && ON_WALKABLE && !Sledding @0x0051096B;
calc_friction @0x0050EE70 returns at its first line when ON_WALKABLE is clear.
acdream had copied retail's airborne no-op WITHOUT retail's local simulation.
The fix is mostly deletion: stop forging the transients, stop discarding the
authoritative velocity, stop clearing Gravity, and route the remote tick
through the same SetPositionInternal commit TickHidden and the local player
already use, with the landing edge derived from the sweep's own OnWalkable.
AP-87's threshold and conditions and InterpolationManager's node_fail_counter
snap-to-tail are deliberately untouched — this removes the CAUSE of the
divergence rather than weakening the backstop.
Cross-checked against ACE: its only creature-side VectorUpdate emitters are the
jump broadcast and spell projectiles, so integrating the wire velocity cannot
double-move a walking remote; and PhysicsGlobals.DefaultState already carries
Gravity, so deleting the manufactured State |= Gravity is safe.
Register: AP-81 narrowed (its GRAVITY half retired outright), AP-87 annotated,
AP-139 filed (the interpolation-queue clear on the landing edge), AP-140 filed
(the two routing gates select snap-vs-interpolate on walkability where retail
uses CONTACT — adjust_offset @0x00555D30 gates on transient_state & 1
@0x00555D52). AP-140's follow-up is deliberately shaped as "point the two gates
at Body.InContact", NOT "re-derive Airborne", which would perturb five writers
and collide with a pinned RemoteTeleportPlacementTests assertion.
Three gaps recorded in #32 rather than papered over: the new LeaveGround
dispatch is untested for chatter; a persistently !Ok transition can latch a
remote airborne; and — the visual-gate watch item — the deleted forge was a
blanket guarantee of Contact|OnWalkable, and contact_allows_move @0x00528dd0
silently refuses action animations without both, which is the literal root
cause of closed#270. Retail-correct on a steep face, a regression anywhere
else.
10 discriminating tests over a real PhysicsEngine landblock whose contact
normal Z is 0.61 against FloorZ 0.6642 — the live roof's exact relationship.
Suite 11,019 passed / 4 skipped / 0 failed. Includes the temporary
ACDREAM_PROBE_REMOTE_LANDING / ACDREAM_PROBE_REMOTE_SLIDE probe family that
produced the capture above; strip with the family.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Flips the SetPositionSimple classification (contact, PlayerDistance >= 96 m) for
remotes onto 4b-1's drive controller and deletes both legacy far blocks, both
duplicated 96f/4f constant pairs, and both `?? Vector3.Zero` fabrications. The
4 m constant now exists exactly once. Teleport and cell-less stay legacy for
4b-3.
Retail: MoveOrTeleport @0x00516330's far branch runs StopInterpolating
@0x005163CB before SetPositionSimple @0x005163D9 and returns 1 @0x005163E8
regardless — the SetPositionError is discarded — so HandleReceivedPosition arms
ConstrainTo @0x00454272 post-move on commit AND on failure. The x87 parity
decode at @0x00516393-@0x0051639E puts exactly 96.0 on the far branch.
SetPositionSimple @0x005162B0 builds flags 0x1012 at @0x005162C4.
Non-commit outcomes still advance the body, because retail's SetPositionInternal
@0x00515BD0 commits the destination via store_position @0x00515CE2 when no cell
resolves. The partition is by STAGE, not heuristic, enforced by an exhaustive
switch: Refused/Contention/NotApplicable/RejectedPreparation store (the placement
never executed); Committed/Deferred/RejectedByPlacement do not (the engine ran
and refused, matching retail's non-storing returns @0x00515CB2 and @0x00515CD5).
Without this a refused far snap froze the remote with an emptied queue.
Also fixes a shipped defect this route made live: ParkDeferred's quiescence parks
withdrew the entity (InWorld=false, clock suspended, residency removed) and were
never restorable, while Forget(restoreCancelledPark: true) runs for every
accepted Position on every entity. The restorable decision now lives inside
ParkDeferred AFTER SnapToCell, reading body.CellPosition.ObjCellId — the value
RestoreParkWithdrawal actually restores at — against every live quiescence
rather than one minimum-OperationId token. The three pre-snap fields are hoisted
into locals because SnapToCell ends with InWorld = true. ParkCollisionResidents
passes restorableOnCancel: false explicitly; the plain unplaceable park is
provably unchanged. RestoreParkWithdrawal re-tests the prefix at restore time so
a retained route-2 park cannot re-admit into a prefix that began quiescing
during the park.
CanAttemptDestination is retained as an OPTIMISATION only, with the two Core
predicates it cannot reproduce written down at the pre-flight, plus the two
properties that depend on it staying there.
Four fix rounds and eight Opus reviews. The slice was fully green at 10,990,
10,997 and 11,004 while containing real defects — a frozen remote pinned as
correct by its own test, a fallback that over-wrote on the exact retail paths
that decline to store, and a park guard incomplete on two independent axes.
Register: AP-137 (leftover classifications take AP-87's catch-up; states the
cell-less enqueue-vs-place delta deferred to 4b-3, that RejectedData is applied
anyway, and the headless divergence), AP-138 (the refusable far placement),
AP-136 narrowed to match the relocation. #309's acceptance steps rewritten —
step 5 previously asserted a recovery the code does not perform — and gated on a
new ACDREAM_PROBE_PARK=1 signal so the check cannot pass while broken.
Suite 11,009 passed / 4 skipped / 0 failed against a measured 10,968 baseline.
The 10,973 figure recorded earlier was wrong and is corrected here.
Connected gate outstanding: the two-client far-snap walk and #309.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Builds the machinery route 4b-2 and 4b-3 will flip on, and changes no remote
behaviour: it has no production caller, so RemotePlacementDrivePendingCount is
provably 0 and IsConverged is unchanged.
Five pieces: a per-entity remote placement owner (RuntimeRemotePlacementDriveController),
a Position-time service-window guard with a Runtime interface plus BOTH host
implementations, N3's headless RetryPending pump, parked-count observability in
the ownership ledger, and the service-window optimisation that avoids parks we
can cheaply predict.
Landed alone because it is where the park-withdraws-the-entity failure was
decided; that decision is fixed at the source in the preceding commit and must
not share a review signal with a behaviour flip.
Two parts of route 2's controller are deliberately NOT ported, both verified
against retail rather than assumed. There is no ack: SendPositionEvent is called
only inside HandleReceivedPosition's local-player FORCE_POSITION gate
@0x0045400C-@0x00454091, and the remote arm @0x0045414D has no equivalent. There
is no re-issue funnel: retail never re-attempts a position it could not apply —
stale timestamps merely bump error_count @0x004542AC — and re-issuing packet N
after N+1 has merged would apply a pose the newer packet already superseded,
which is correct for a one-shot ForcePosition and wrong for a 5-10 Hz stream.
The service-window guard is an OPTIMISATION, not the correctness mechanism. The
original contract had it the other way round, justified by a claim that retail
cannot represent "arrived but not placeable" — false, and corrected in the
review findings: retail's GotoLostCell/reenter_visibility path represents it
exactly. A pre-flight guard also cannot be complete, because Core defers on the
entity's CURRENT cell, on the swept QueriedCellIds footprint spanning
neighbouring landblocks, and on residency evaluated after AdjustToOutside —
conditions only Core can see.
Review found and this commit fixes: DetachRoute cleared two maps of LIVE Core
operations without cancelling them (route 2's AbandonPending is the correct
mirror, not the first-entry controller) and its test asserted that blindness as
convergence; the headless predicate answered "can ever publish" rather than "is
published", and after the first fix still matched only 1 of the 9 landblocks
this host publishes; OwnsPlacement admitted remote top-level Creates until
gated on the Teleport flag as well as the disposition; Advance re-submitted
without re-checking the window; and four comments cited a report that did not
exist.
Contract item 6 is met by the structural proof, not the earlier test:
HasOldPrefixPlacementDebt refuses collision-prefix mutation permission before
ParkCollisionResidents is ever entered, so its overlap throw is unreachable.
That same mechanism is the unbounded stall filed as #310, which 4b-1 does not
bound — it only avoids widening it.
#311 files the remaining per-tick allocation in RetryPendingProjections; the
early-out for the empty-FIFO case landed via a new HasPendingReceipts accessor
so hosts still never touch .Placements. directly.
Gates: complete Release solution 10,973 passed / 4 skipped / 0 failed (baseline
10,938). Four review rounds; every fix discrimination-verified by revert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Shipped-code defect affecting committed route-2 code, found while reviewing
route 4b-1.
RuntimeSetPositionState.ParkDeferred withdraws an entity from the world:
body.InWorld = false, TransientStateFlags.Active cleared, WithdrawCanonical,
SuspendObjectClock. CancelCoreDeferred then removed the operation and rewrote
the pending Withdraw into a Discard while restoring NONE of it. So cancelling a
wakeable park was strictly worse than keeping one — the park is wakeable, the
cancel destroys the only object that could ever wake it, and the entity is left
invisible AND intangible with nothing to bring it back.
Route 2's re-issue funnel masked this: re-issuing is correct for a one-shot
ForcePosition ACE never repeats, and wrong for a repeated remote stream, so the
hole was hidden rather than fixed.
Retail's own answer is a working park, verified in the decomp rather than
assumed: CPhysicsObj::SetPositionInternal @0x00515BD0, when AdjustPosition
yields no cell @0x00515C1D, calls prepare_to_leave_visibility @0x00515CDA,
store_position @0x00515CE2 (the DESTINATION pose is committed), GotoLostCell
@0x00515CF2 registering at m_position.objcell_id read AFTER store_position (so
the destination cell), clears transient 0x80 @0x00515CF7, and returns OK
@0x00515D07. InitObjCell @0x00508260 drains the lost list on cell load and calls
reenter_visibility @0x00516250, which re-places from the object's OWN
m_position with flags 0x11.
Two corrections to the direction I gave, both forced by evidence and both right:
The pose must NOT be rolled back — only the withdrawal. Three shipped route-2
tests capture positionAtPark AFTER the park and assert it survives the cancel,
and retail agrees: store_position commits the destination and nothing
un-commits it. Restoring residency at the body's committed cell is therefore
retail's own cell choice, not merely self-consistent.
The gate defaults to FALSE with four explicit opt-ins, rather than defaulting
true with opt-outs at the withdrawal callers. That keeps every one of the ~20
shipped Forget/ForgetExactPlacement sites at exactly its current behaviour
instead of depending on having correctly enumerated the withdrawal transactions.
Review had already found the broad version corrupting five of them
(TryApplyPickup, CommitAcceptedParent, CommitAcceptedParentCellless,
CommitWithdrawal, CommitPositionChannelUpdate): they hand-roll a partial
re-withdrawal that undoes the clock and FullCellId but not InWorld or the
_spatialRoots re-registration, leaving a picked-up item both in inventory and an
InWorld cellless spatial root in the physics workset.
ParkDeferred's restorableOnCancel is opt-in for exactly one of its four callers
— the plain unplaceable-destination park. Every quiescence and retirement park
is excluded deliberately: those entities are withdrawn because their world is
going away, and restoring residency inside a quiescing prefix blocks its
retirement.
VerifyPositionChannelCancellation now asserts InWorld and IsSpatialRoot per
channel — Position is a cancellation and must restore; Pickup and Parent are
withdrawals and must not. It previously asserted only !IsDeferred and counts,
which is why five green states hid this.
Register row AP-136 measured against GotoLostCell/reenter_visibility rather than
labelled "retail-shaped". Files #309 (the restore-on-cancel residual, with
park-survives recorded as the retail-faithful target and its two blockers named:
the NewerPositionPickupAndParentEachCancelExactLostOperation invariant and
teardown convergence) and #310 (an unbounded retirement stall — a retained
preparation retry pins its prefix through HasOldPrefixPlacementDebt forever, and
TickLostCellDeadlines has no production caller so the 25 s timer never fires).
This is a user-observable change to shipped paths: restorableOnCancel: true sits
in SubmitPreparedPlacementCore, the shared core behind every production
placement. AP-136 and #309 carry the proposed two-client check.
Gates: complete Release solution 10,973 passed / 4 skipped / 0 failed (baseline
10,938). Every new test discrimination-verified by reverting the fix.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Routes the classifier's two NO-PLACEMENT remote branches — Interpolate
(contact, PlayerDistance < 96 m) and NoPositionOperation (no contact) — through
a Runtime-owned seam, and fixes the two divergences they carried. Teleport,
far-snap and cell-less stay on the legacy App path; 4b owns them.
Route 4 was split into 4a/4b after scoping put the whole route at 1,500-2,500
lines against a ~400 budget. 4a's branches perform no SetPosition, so this slice
carries no deferred-cell park, no service-window guard and no allocation
exposure — which is what made the split worth doing.
Divergences fixed, both previously unfiled:
* D1 — the NPC airborne branch hard-snapped Body.Position/Orientation and
branched on the client-tracked rmState.Airborne, never consulting the wire
IsGrounded bit. Retail's MoveOrTeleport @0x00516330 returns 0 at 0x0051636D
and writes nothing. Player remotes were already correct; NPCs were not.
* D2 — ConstrainTo was armed before the operation, unconditionally, so it fired
on the airborne no-op retail skips and anchored to the PRE-move position.
Retail arms it at 0x00454272, only when MoveOrTeleport returns nonzero,
anchored to &arg2->m_position read live, i.e. post-move.
AP-87 and TS-44 were carried deliberately, not delegated away. AP-87's three
conditions — including firstUp, which one round silently dropped — are preserved
as an explicit acdream policy layer applied AFTER the classifier commits to
Interpolate; the two previously separate player/NPC copies are now one. TS-44
stays an NPC-only caller gate; extending sticky suppression to player remotes has
no retail basis and no live evidence, so it was declined rather than absorbed.
Landing is explicitly carved out of 4a's ownership on both arms. A landing packet
classifies Interpolate, so an ordering slip would ENQUEUE a body that must PLANT
and a creature knocked off a ledge would glide down over a packet interval. The
carve-out is a named entry point returning AirborneSnap/SteadyStateInterpolate/
Legacy precisely so the PRECEDENCE is observable and testable rather than implied
by statement order — that is how the slip happened once and was caught.
The player/NPC asymmetry on landing is real and NOT resolved here: retail draws
no such distinction, but converging them is a behaviour decision needing its own
evidence. Filed into the 4b plan.
Register: AP-135 filed for the two bookkeeping writes the airborne branch
deliberately retains (rmState.CellId, LastServerPos/Time) — not retail's model,
but load-bearing for our catch-up sweep and staleness timer, and verified not to
be a canonical cell commit for ordinary remotes. AP-87 and TS-44 rewritten to
describe the code.
Honest remainder: App still owns branch selection, the airborne return, the cell
write, the entity write and the shadow publish, and headless satisfies "both
hosts drive the identical entry point" only vacuously since it returns early for
remotes. That is written into the 4b bullet rather than left implicit.
Cost: 364 non-comment production lines, 91% of the ~400 budget — the split did
isolate the cheap half, but not by much. Do not carry "well under" into 4b's
scoping.
Gates: complete Release solution 10,938 passed / 4 skipped / 0 failed (pre-4a
baseline 10,909). Four review rounds; the first three each introduced a new
behavioural defect while fixing another, and each left a comment asserting
behaviour that no longer matched — the final round's precedence matrix was
traced cell-by-cell against HEAD with only the D1-intended difference. App tests
call production entry points against a real WorldEntity and real classifier
output, closing route 2's #292 gap rather than repeating it.
Connected acceptance NOT run — needs a live second character.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Shipped defect in route 2 (9966b531), found while reviewing route 4a.
`InboundPhysicsStateController.TryApplyPosition` built its AcceptedPhysicsTimestamps
via `Current(gate, teleportAdvanced: ...)`, omitting `previousTeleport`, which
defaulted to a literal 0. The only site that populated it was the deferred
initial-create path — which is why the continuation executor was correct and
every newer consumer was not.
Consequence in shipped code: route 2 feeds this into
`ValidAcceptedAuthority`, which requires Previous == Accepted for a
ForcePosition. Any local player whose TELEPORT_TS is nonzero — anyone who has
portalled or recalled this session — had the authority rejected and the force
correction SILENTLY DROPPED. The user's @pklite acceptance was genuine but
narrow: that character had not teleported, so the stamp was still 0.
Second latent consequence: with an accepted stamp >= 0x8000, wrap-safe
TeleportRegressed also fires against the 0 and rejects ordinary Apply positions,
not just ForcePosition.
The fix captures `previousTeleport = gate.TeleportTimestamp` BEFORE
`TryAcceptPositionEvent` mutates it, matching the shape the deferred path
already used. Ordering is the whole point: capturing after would make
Previous == Accepted unconditionally, so ValidAcceptedAuthority's check would
pass vacuously — the symptom would disappear while the semantics broke.
Also removes the footgun that allowed it. `Current`'s parameter is now
`ushort? previousTeleport = null` resolving to `gate.TeleportTimestamp`, so the
eleven non-Position channels — none of which can move TELEPORT_TS — get
"previous == current" by omission rather than a literal 0 that is
indistinguishable from a genuine "never teleported".
Consumer audit: only TryApplyPosition was defective. The two route-2 call sites
trace back to it; the RuntimeEntityObjectLifetime sites source from
TryAcceptDeferredPosition and were already correct.
Tests discrimination-verified by reverting the argument to 0: the stamp test
fails Expected 10 / Actual 0, and the classifier test fails Expected
SetPositionSimple / Actual RejectedAuthority — the shipped defect reproduced
exactly.
Gates: complete Release solution 10,935 passed / 4 skipped / 0 failed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The user typed @pklite and then walked straight through other PKLite players.
Root cause: ClientObject.PublicWeenieBitfield was written exactly once, from the
0xF745 CreateObject parse, and never refreshed. ACE's only PK-change message is
PropertyInt.PlayerKillerStatus (134) over 0x02CE/0x02CD, which we parsed and
stored into Properties.Ints[134] but never translated back into the bitfield —
and ACE never re-sends a PublicWeenieDesc at all (EnqueueBroadcastUpdateObject
has zero live callers), so that property is the ONLY signal a client can learn
from. Both sides of the collision test read the frozen value, so
CollisionExemption's "4c. both PKLite -> collide" rule could never fire.
Retail's missing port: PublicWeenieDesc::SetPlayerKillerStatus @0x005AC7C0
rewrites _bitfield in place — PK(4) -> (b & 0xfddfffff) | 0x20; PKLite(0x40) ->
(b & 0xffdfffdf) | 0x2000000; Free(0x20) -> (b & 0xfdffffdf) | 0x200000; else
b &= 0xfddfffdf. Mutually exclusive, verified byte-for-byte, with input values
confirmed against retail's own PKStatusEnum (acclient.h:6412-6427), not just
ACE's. Driven from ACCWeenieObject::OnStatUpdated @0x0058DF20 case 0x86.
The fix rewrites the value at its source rather than patching consumers. Two
review rounds were needed because the first pass missed that there are TWO
snapshot stores: InboundPhysicsStateController keeps its own private _snapshots
dictionary, and every untimestamped-field merge (ApplyAcceptedObjDesc and
friends) reads `old` from THAT store, not from RuntimeEntityRecord.Snapshot.
Refreshing only the active record left the target-side shadow flags correct
until the remote's next equip or unequip — ACE broadcasts an ObjDesc on every
one — at which point the appearance path rebuilt the registration from the
frozen spawn and dropped the bit permanently. The regression test demanded by
review is what surfaced that; it is verified discriminating (reverting gives
Actual: 8 instead of 33554440).
Five stores now hold this value, kept coherent from one source by two
ObjectUpdated subscribers plus the appearance-rebuild path. The two shadow-flag
writers are the same invalidation applied at the two edges that can invalidate
it, not competing authorities — review enumerated every drift path and closed
each. That coherence invariant is new as of this commit and is recorded as
register row AP-134, with AP-133 as the precedent for filing a row when the
danger is a future writer rather than current behaviour.
Also corrects TS-23's retirement narrative, which claimed every mover-flags call
site read the mover's "real" PK bits from 2026-07-30. The bits existed but their
source was frozen, so that only became true here; the site enumeration also
missed RuntimeSetPositionMoverPreparation, a seventh site that decodes the
snapshot directly.
Unblocks #298 (melee/missile admission needs the local player's own PKLite bit).
Follow-ups filed: #300 (Properties.Ints[134] vs bitfield mirror gap), #301 (same
defect class for radar blip colour and radar behaviour), #302 (a pre-existing
PortalProjection allocation-assertion flake, 1 in 6, found while verifying this
gate), #303 (LiveEntityPvpBitfieldSync is App-resident but Runtime-owned-state).
Gates: complete Release solution 10,895 passed / 4 skipped / 0 failed (baseline
10,887 including #299). Adversarial + retail-conformance review PASS after one
FAIL round. Every new test discrimination-verified by reverting the fix.
Connected acceptance NOT run — needs a live two-client PKLite session.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A local-player ForcePosition had TWO independent writers for one accepted
packet: LocalForcePositionTransaction snapped the physics body
(PlayerMovementController.BlipPosition, a raw SnapToCell with no collision
resolve), while LiveEntityNetworkUpdateController's generic tail separately
wrote position/cell/rotation to the render WorldEntity from the raw wire and
rebucketed it. Two stores, one packet — the divergence class 670f307c fixed on
the remote path. The outbound AutonomousPosition ack also fired BEFORE any
canonical commit existed: we told ACE "got it, I'm here" before deciding where
"here" was, and the trailing isCurrent() could only suppress the continuation,
never recall the packet.
RuntimeAcceptedPositionDriveController is now the one Runtime-owned seam. Both
hosts call the identical TryExecuteAcceptedLocalPosition; App and headless
project the committed result through the existing placement projection sink
(LiveEntityRuntime.TryApplyRuntimePlacementPlace already performed the same
four writes, from committed state rather than a wire guess).
Retail: SmartBox::HandleReceivedPosition @0x00453FD0's FORCE_POSITION branch is
get_heading -> Frame::set_heading -> SmartBox::BlipPlayer @0x00453940 -> stamp
POSITION_TS -> SendPositionEvent @0x00454091 -> return @0x0045409D. BlipPlayer
is CPhysicsObj::SetPositionSimple @0x005162B0 with flags 0x1012
(Teleport|Slide|SendPositionEvent) — a real collision-resolving SetPosition,
not a snap. The pinned classifier already encoded this exactly.
Named behaviour changes:
* The ack is now an OUTPUT of the committed route, fired strictly after the
canonical commit and exactly once per accepted force packet.
* The ForcePosition route no longer re-arms the constraint leash. The force
branch returns at 0x0045409D, ahead of all three ConstrainTo sites
(0x00454272, 0x0045418A, 0x004541EC); the old re-arm cited retail's "Player,
normal" branch, which BlipPlayer is not on. The teleport, CommitPreparedPosition
and first-entry callers legitimately still constrain and are untouched.
* A force correction that terminates WITHOUT committing still sends its
position event and is not retried — retail's BlipPlayer discards
SetPositionSimple's SetPositionError return and acks unconditionally.
A single _pending funnel owns the in-flight placement, deciding on the token's
PositionAuthorityVersion against the record's: equal -> clear; advanced with the
newest accepted event still a force -> re-issue, re-classified; advanced to an
ordinary Apply -> clear, since newer server truth owns that pose. This closes a
double-apply/double-ack and a silently-dropped correction that two earlier
iterations of this slice each introduced.
AD-62 records the residual: a ForcePosition our async collision publication
cannot carry to a committed placement is not re-applied. Retail has no park —
its world is fully resident and its placement synchronous — so the state is
unreachable there. AP-131 is NOT retired; its legacy Position caller is route 4.
Deleted: LocalForcePositionTransaction, PlayerMovementController.BlipPosition,
HeadlessSessionWorldProjection.BlipLocalPlayer.
Gates: complete Release solution 10,858 passed / 4 skipped / 0 failed (baseline
10,844/4/0). Two independent Opus reviews (retail-conformance and
architecture/adversarial) PASS on the final diff after three FAIL rounds; every
intermediate state was fully green, so the suite caught none of the four real
defects. Connected acceptance is NOT run: nothing a user can do makes ACE emit
a ForcePosition without retail's @pklite, which acdream does not implement — see
docs/research/2026-08-03-c4-route-2-visual-gate.md.
Known gap, recorded not claimed: the plan's acceptance item 2 is unmet. The App
double-write check is a source pin, and "the committed projection moves the
render entity" is uncovered at any layer (#292). Filed alongside: #286-#291,
#293-#296.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixes#284 (plan S1).
A first-entry placement that could not be prepared returned
RetrySetupUnavailable and was re-Advanced every pump forever. Nothing counted
it, nothing named its cause, and nothing distinguished "waiting for something
that will arrive" from "waiting for something that never can". That is why
#281's 43 test failures presented as four unrelated symptoms across App and
Runtime instead of one cause, and why a stuck entity in the live client simply
never appears with no log line to follow.
Worse, the two causes were conflated: 670f307c's missing-world-frame park
reported itself as RetrySetupUnavailable, sending anyone diagnosing it to the
prepared-asset pipeline rather than to the absent local-player Create that
actually publishes the frame.
- RetryWorldFrameUnavailable splits the two causes. Call sites now ask
IsRetryable() instead of comparing against one reason, so a future retry
reason cannot be silently reclassified as a hard rejection - the exact way
this class of bug hides.
- The operation retains its RuntimeSetPositionParkReason, and
RuntimeSetPositionOwnershipSnapshot reports parked work by cause
(ParkedAwaitingSetupCollisionCount / ParkedAwaitingWorldFrameCount /
ParkedPlacementCount), so parked placements appear wherever ledgers are
already asserted.
- ObserveLocalPlayerCreate records the accepted local-player Create even when
it carries no landblock - precisely the case where no frame is ever
published - and ThrowIfWorldFrameUnreachable makes that contradiction
terminal. Waiting is legitimate only while that Create is outstanding; after
it, no later pump can supply the frame. Same shape as 01f4791e, which made a
violated receipt-ledger invariant terminal rather than resumable.
This is observability plus fail-fast. There is no timeout, no retry cap, and
no grace period anywhere in it; retryable work still retries exactly as before
and no placement behaviour changed.
The parked counts are deliberately NOT folded into IsConverged: #277 documents
a far Create legitimately parking for a whole session, so a parked entry at
teardown is not automatically a defect. Wiring them into the connected gates
is carried with #277's service-window conversion, where "legitimately parked"
becomes definable.
Runtime 1,012/1,012. Complete Release solution: 10,834 passed / 4 skipped /
0 failed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
670f307c made remote first-entry placement resolve its landblock-local
CreateObject origin through Runtime's world frame
(RuntimeSetPositionState.PrepareMover:1526-1544) and return
RetrySetupUnavailable until that frame exists. Only the accepted local-player
Create publishes it (RuntimeEntityObjectLifetime.RegisterEntityCore:558-570 ->
RuntimePhysicsState.ObserveLocalWorldFrame).
Fixtures that drive remote conductors in a world with no local player - a
state production never occupies, since the player's own Create always precedes
broadcast Creates - therefore parked forever on RetrySetupUnavailable. Their
initial-create residences never retired, which cascaded into rejected
appearance updates, missing canonical bodies, unconverged ownership ledgers,
and a GameRuntime teardown that could not complete stage 10.
The measured blast radius was far larger than the handoff recorded. It claimed
"six selected fixture failures"; a baseline run found 43. The App suite was
fully green at 01f4791e and 670f307c broke 28 tests at once; the Runtime suite
lost 13, twelve of them in RuntimeRemoteFirstEntryStateTests - the exact
conductor that commit gated. Both commits were verified on focused runs only.
The production gate is correct, so nothing here weakens it. It matches App's
own coordinate owner: LiveWorldOriginState is initialized once from the local
player's spawn (LiveEntityHydrationPorts.cs:226) and rebased only by
StreamingOriginRecenterCoordinator.Advance at a teleport boundary - exactly
ObserveLocalWorldFrame's semantics. Every fixture is repaired by supplying the
missing precondition beside the resident landblock it already models, and not
one expected value or assertion was changed.
The mechanism shipped with zero tests. RuntimeWorldFrameTests now pins its
contract: the local player publishes the frame, remotes never do, neighbouring
landblocks convert at 192 m per step, ordinary movement across a landblock
boundary must NOT rebase it, an accepted teleport must, and a zero cell id
neither publishes nor resolves. That "no rebase on ordinary movement" rule is
load-bearing - if it and LiveWorldOriginState ever disagree, remote objects
are placed a multiple of 192 m from where the world is streamed.
Runtime 1,009/1,009; App 4,048 passed / 3 skipped.
Refs #281.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ACE intentionally creates the local player Hidden and releases that materialization state on LoginComplete. Sending LoginComplete from raw F746 receipt raced canonical placement and left the login haze visible. Route one one-shot completion callback from Runtime's local first-entry terminal edge to graphical and prepared headless hosts; retain a guarded accepted-Create edge only for content-less headless sessions. Focused Runtime login tests, all 79 Headless tests, the connected user gate, and the Release build pass.
Publication-throughput rework per the D2 design (docs/research/
2026-08-02-collision-throughput-handoff/design-note.md): O1 per-prefix
installed-key ledgers replacing the seal's full-map scans; O2 per-
landblock delta commit (LandblockReplacementApplyCursor against the
active root) replacing whole-world TransferTo; O3 empty staging root,
commit-time reflood (CObjCell::init_objects 0x0052B420 ->
recalc_cross_cells 0x00515A30), journal/peer-rebase machinery deleted
(~1,900 lines net).
Automated gates green: Runtime 999, Core physics 2,135, App 4,039/3,
Headless 79, complete solution 10,812/0/4; lifecycle gate PASS
(connected-world-gate-20260802-193029). Soak 194423: publication-side
acceptance fully met (37 -> 4 failures, all convergence dims zero,
loadedLandblocks baseline-identical, waitCue 6/9 -> 1/9).
COMMITTED AS WIP ON USER DIRECTION - NOT ACCEPTED. The user feel-test
FAILED on this tree: monsters still pop into existence at close range,
monsters spawned mid-air far ahead, static placements visibly wrong,
plus 243x "Landblock already has a full retirement receipt"
InvalidOperationException catch-retry loop during origin recenter
(launch-feeltest-oclone.log). The 4 remaining soak failures
(pendingLandblockRetirements 131/122 at the Caul->Sawato stops) and the
implementer's "exposed pre-existing" classification are under
re-judgment against that loop. Dual reviews were dispatched and then
stopped mid-flight on user direction; NO review has passed this commit.
Full problem inventory + next-agent instructions:
docs/research/2026-08-02-collision-throughput-handoff/.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Campaign P remaining-physics-divergence, placement cutover slice C3c
(docs/plans/2026-08-02-placement-cutover.md). Both production hosts now
register every initial Create through the residence + continuation-
executor + first-entry-conductor machinery (C0-C3b):
- Graphical (route 1): RegisterEntityWithInitialResidence at Create; the
shared RuntimeFirstEntryDriveController pumps both conductors from the
placement-receipt flow; MaterializeProjection and RebucketLiveEntity
are presentation-only while a residence is ACTIVE (ExecutorCompleted is
the presentation-binding receipt); post-residence entities take the
full legacy path including the prepare_to_enter_world clock edges.
PlayerModeController attaches presentation to the Runtime-published
controller; its legacy resolve/step-heights/host-construction path is
deleted; presentation-only rollback (retail has no entry-flow rollback).
- Headless (route 8): OnSpawned registers with residence when a drive
exists; content-less sessions keep the pre-flip direct registration;
SynchronizeLocalPlayer/CreateController/ApplySetupStepHeights deleted;
prepared-collision read failure is a typed AwaitingCollisionSource
retry; far remotes outside the service window complete celless.
- RuntimeLocalPlayerMovementState.Controller setter sealed internal; all
controller mutation flows through the publication lifecycle.
Fix slices landed within this cutover, each dual-gated:
- F1: live movement-stat/server-physics application routed through the
Runtime ownership seam (post-logout ingest crash on the retired
controller eliminated; RuntimeMovementSkillProjection deleted).
- F2: login activation wedge - collision-admission prefix gate factored
out of the seal (reentrant-commit RejectedAuthority), rearm generation
identity corrected, PlayerModeAutoEntry requires the Runtime-published
controller (world reveal can no longer seal unmaterialized).
- F3: landblock-prefix 0-sentinel replaced by explicit absent-id guards;
map-corner landblocks (grid row/col 0) fully legal through admission,
park/rearm/retire, quiescence, and outdoor shadow seeds.
- F5: local-player first-entry ground contact seeded by the shared
SpawnPlacementSettler (moved App->Core) at FinalizeActivation - the
retail first-gravity-frame touch (enter_world 0x00516170 carries no
seed); the legacy unconditional force-seed is overwritten by a real
floor-found contact; airborne spawns stay airborne; outbound contact
bit verified end-to-end. Fixes the standing-cast 'You can't do that
while in the air!' rejections.
- R1 (dual-review round): login constraint leash armed at the committed
placement (HandleReceivedPosition 0x00453FD0 analog); register rows
AD-61 (settle-timing compression now covering the local player) and
AD-42 (repointed off the deleted resolve split) in this commit;
residence-conversion owner API; wire-landblock guards; drive-pending
ledger in IsConverged; route attach/detach latch; executor-drain drift
model documented + source-pinned.
Gates: Runtime 1,003, App 4,039/3 skips, Headless 79, complete solution
10,816/0 failed/4 skips (Release, -m:1); connected lifecycle/reconnect
gate PASS (logs/connected-world-gate-20260802-175401; graceful exits,
world-visible, zero airborne rejections). The nine-stop soak remains red
for the pre-existing 6b28ff99 whole-world collision-clone throughput
regression (attributed with evidence; scheduled as its own slice before
C5). Dual Opus reviews (retail-conformance + adversarial): delta PASS.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cutover slice C3b: residence-route remote/creature/projectile Creates now
get their canonical PhysicsBody at Create time — retail's order, closing
the C3 flip's Finding C (production builds bodies at first motion; retail
builds them in ACCObjectMaint::CreateObject). RuntimeRemoteBodyDescription
walks set_description 0x00514F40 exactly: the motion-table gate (zero id
PASSES — verified at 0051871f/005127ca), the frame-vs-movement branch
keyed on retail's movement_buffer != 0 (an empty-buffer movement payload
takes the PLACEMENT branch and writes no autonomy — the wire-shape defect
the retail review caught), set_state, the byte-certain friction gate
(inclusive [0,1]; NaN deliberately skipped per the gates doc's sanctioned
deviation), the set_elasticity clamp with retail's unordered-to-zero NaN
routing (ACE diverges to 0.1 on that edge), the translucency gate
(!= 0.0f, original always recorded), velocity via setter, omega raw, and
ctor-defaults for absent wire fields (0.95f/0.05f/0 — the fresh-desc-per-
message flow verified at both UnPack call sites). InWorld stays false
until submission, the enter_world analog.
RuntimeRemoteFirstEntryState sequences mover-prep -> body construction ->
placement -> acknowledgement -> Execute with every C3a hardening
inherited: exactly-once stages, the shared acknowledge-stage
discriminator (extracted to RuntimeFirstEntryAcknowledgement, one body
for both conductors), typed Contention against in-flight remote-motion
binds, never-clobber body binding through the canonical writer (foreign
body fails closed — provably safe coexistence with today's
build-at-first-motion path in both directions), automatic convergence
through the retirement fan-out, and the construction receipt riding the
terminal Advance. Dormant: no production caller; C3c wires both hosts.
Reviewed: retail-conformance PASS (the construction order, both gate
boundary/NaN semantics, the motion-table and autonomy verdicts all
re-derived from the pseudo-C) + architecture/adversarial PASS after one
fix round. Runtime 982/982; complete Release solution 10,777 passed / 4
intentional skips.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cutover slice C3a: the resumable transaction that dissolves the C3
flip's circularity finding. RuntimeLocalPlayerFirstEntryState drives the
local player's complete entry in retail's own order — authored-mover
preparation (the makeObject/set_description shape analog, via a pure
no-submit extraction TryPrepareAuthoredMover), the publication chain's
off-canonical Prepare + atomic body Commit against the residence's exact
placement token, the Evaluate/CommitActivation enter-world analog, the
Place-receipt acknowledgement as that act's virtualized completion, and
only then the executor's FIFO drain (retail: enter_world at 93824
strictly precedes ProcessObjectNetBlobs at 93831). Five stages, eight
typed statuses, exactly-once per stage under retry, no second token
copies, and an acknowledge-stage discriminator that separates
not-yet-FIFO-head (retryable) from authority-moved (typed abandonment) —
a mid-flight delete can no longer strand a retry-forever entry.
The residence retirement notification becomes an ordered multicast
(snapshot-iterated per the event-stream precedent), the lifetime
constructs the conductor with a late-bind Publication seam (transactional
unbound failure — no mutation before the throw), deletion/reset converge
the conductor automatically through the same choke points as the
executor, and its active count is in the ownership snapshot and
IsConverged. Dormant: no production Advance caller; GameRuntime binding
is C3c's first act.
Reviewed: retail-conformance PASS (the stage order verified
step-for-step against retail's entry sequence; the live-controller-on-
abandonment invariant proven structurally enforced and retail-correct —
retail has no entry-flow rollback) + architecture/adversarial PASS after
one fix round (acknowledge-stage authority discrimination; the wiring
fold; two prescribed pre-C3c hardenings). Runtime 948/948; complete
Release solution green across all nine projects.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cutover slice C3-1 (the C3 flip's Runtime prerequisite, landed separately
after the flip itself was halted with structural findings — see the plan's
C3a/b/c decomposition). Hosts can now read the executor-completion facts
they must bind at cutover through one public, generation-gated channel
accessor: RuntimePlacementProjectionChannel.TryGetInitialCreateCompletion
returns RuntimeInitialCreatePlacementCompletion — the teleport-hook phase,
resident cell, replay outcomes, and per-Position route facts (disposition,
constrain phase, hook phase, stop-interpolation/zero-velocity/preserve-
heading/send-position flags) via public 1:1 mirror enums of the internal
classifier vocabulary. The projection is built once at completion, cached
in the same reaped entry as the internal receipt (identical acknowledge/
discard/clear lifecycle, ledger-covered), and read allocation-free.
Mirror maps enumerate every value explicitly with throwing catch-alls,
guarded by a sabotage-verified arity/round-trip reflection test. Doc
comments pin the two consumption rules: unparent/placement-frame are
already applied to the canonical snapshot (hosts must not re-apply), and
array order — not Sequence — is the authoritative Position-fact ordering.
Reviewed: architecture PASS + retail-conformance PASS (mirrors verified
member-for-member against the retail phase semantics; the route-fact
selection confirmed to cover exactly the host-bindable deferrals).
Runtime 932/932; complete Release solution 10,727 passed / 4 skips.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cutover slice C2: the dormant placement path's per-operation cost was the
recorded activation blocker for routing frame-frequency traffic through
the canonical SetPosition owner (1,880 B/op measured at 4B2, cap 2,048).
Root-cause removal, not a raised cap: the per-operation envelope is now
pooled (bounded 64, reset-at-rent, InPool double-retire guard, cleared on
session reset/dispose and surfaced as a diagnostic ownership count), the
two engine-callback closures became one cached delegate over an explicit
context stack, and the pending-projection head read no longer boxes the
sorted enumerator. Measured 2,032 -> 944 B/op; the regression gate
tightens to 1,536. The residual floor is documented at the gate: ~520 B
inside Core's PhysicsEngine.SetPosition (outside this slice's scope) and
~208 B of sorted-tree node per pending receipt.
Pooling demanded — and received — the full staleness-discipline rework:
every frame holding an operation across a reentrancy point now captures
its never-reissued token and revalidates via fresh lookup
(IsCurrentByToken / token-shaped CancelCore), because a recycled
instance reinstalled at the same key makes every reference-identity
check a tautology. All ~26 sites audited (15 remain reference-based with
per-site no-reentrancy proofs); CommitCanonical's post-callback reads
are hoisted stack locals mirroring retail's savedTransientState pattern
(handle_all_collisions bits, pseudo-C 283952), its bookkeeping writes
are token-gated, and the settle path stays deliberately identity-
agnostic because retail's SetPositionInternal runs its physical settle
unconditionally even for displaced operations.
Reviewed: retail-conformance PASS + architecture/adversarial PASS after
two fix rounds (the ground-edge recycle window, the pool's cross-reset
retention, the class-wide tautology, a self-found snapshot-reference
iteration hazard). Runtime 927/927; complete Release solution 10,722
passed / 4 intentional skips; budget test green at the tightened gate.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cutover slice C0 (docs/plans/2026-08-02-placement-cutover.md): the seam
work that lets C3 flip hosts onto a complete receipt stream instead of
growing one mid-cutover. The executor's Released exit now publishes an
acknowledge-only ExecutorCompleted receipt through the one placement
projection stream — registered before observer dispatch, correlated to
the full execution receipt, reaped exactly once on acknowledgement/
discard/session-clear, and counted in the convergence ledger. All three
production placement sinks acknowledge-and-ignore the new kind via early
returns proven behavior-preserving for every existing kind; without them
the first such receipt at cutover would permanently wedge the exact-head
FIFO behind sinks that return false. Provably inert today: the publisher
has no production caller.
Execute's live inputs now derive from Runtime's own owners bound at
GameRuntime construction: UsePositionFromServer is retail's exact
autonomy_level != 2 (CommandInterpreter::UsePositionFromServer
0x006B3B40, startup-only knob), and PlayerDistance uses the live movement
controller's position with a null-safe fallback to the caller struct —
never a fabricated origin. TryPrepareAndSubmitAuthoredPlacement chains
the prepared-collision Setup read through PrepareMover to submission with
zero validation-semantics changes. TryCommitParent and CommitWithdrawal
gain the sibling cancellation flow (residence + ordinary placement
family); TryCommitParent deliberately omits LeaveWorld — retail's
set_parent performs its single gated leave_world (0x00515A90) and a
second would have no counterpart.
Not fully dormant: the two cancellation fixes change Runtime paths
production already calls (today as no-op-adjacent hardening, since
nothing upstream begins a residence yet); everything else is reachable
only by tests. Reviewed: retail-conformance PASS + architecture/
adversarial PASS after one fix round (sink wedge, completion-receipt
lifecycle, null-controller distance). Runtime 921/921; complete Release
solution 10,716 passed / 4 intentional skips.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The admission checkpoint (30012361) sealed accepted updates behind a
pending initial placement; nothing could apply them, so AcknowledgeAdoption
refused any non-empty FIFO and the residence system had no path to
completion. RuntimeInitialCreateContinuationExecutor is that missing
mechanism: a synchronous, retry-idempotent Execute transaction that adopts
the acknowledged initial placement exactly once (consuming the retained
completion so later authored placements for the key can begin), emits the
AfterEnterWorld hook request for the local player, replays deferred
missing-parent raw Creates and queued parent relations by parent GUID
(retail ProcessObjectNetBlobs order: whole-bucket detach, FIFO dispatch,
cancellation-aware restore), and drains the mixed continuation FIFO
strictly by sequence with retail route decisions taken at execution time
via ClassifyAcceptedPosition on live inputs (server-asserted wire contact,
data-driven animation proxy, live distance/options).
Apply bodies are shared with the legacy fused paths through new gate-less
instance seams on InboundPhysicsStateController that keep the one snapshot
store in lockstep; SameIncarnationCreate envelopes apply atomically with
per-stage idempotency and buffered publication after the final stage;
every abandonment path retires the residence through the lifetime choke
point and converges the ownership ledger (executor progress, deferred
buckets, replay windows, placement watches all folded into IsConverged).
Position/placement side effects are exactly-once under retry, external
mutations are detected via a field-masked executor baseline, and
AwaitingContinuationPlacement yields keep the FIFO head retryable.
Production routes are deliberately untouched: graphical and headless
Create still use legacy RegisterEntity, and no host calls Execute. The
cutover is the next checkpoint; AP-1/AD-1 remain open until it lands.
Register rows AD-59/AD-60/AP-130/AP-131/AP-132/TS-62/TS-63 document the
slice's deviations in this commit.
Reviewed: retail-conformance PASS + architecture/adversarial PASS after
five implementation rounds (wire-contact source, snapshot lockstep,
WeenieDescription merge, abandonment convergence, reentrant retirement
windows, acknowledged-completion leak, baseline precision, replay
containment/restore, queue-by-parent-GUID relation deferral all fixed at
root cause). Runtime tests 903/903; complete Release solution 10,696
passed / 4 intentional skips; focused executor gate 161/161.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Retail jump landings BOUNCE: the floor touch records both a contact plane
(grounding) AND a collision normal (collided_with_environment), and
handle_all_collisions reflects the unmodified impact velocity off it at
5% elasticity (v += -(v.n)(elasticity+1).n, DEFAULT_ELASTICITY 0.05
@0x007c6a7c). Our transition already recorded both facts; the bounce was
suppressed by the AD-25 adaptation stack in the per-tick commit: a
Velocity.Z<=0 landing gate (needed because the resolver glued ascending
movers to the ground) plus a landing Velocity.Z=0 hand-zero whose stated
purpose was making the reflect a no-op. Downhill glided instead of
bouncing, flat-ground landings had no pop, and uphill jumps flapped
between grounded/airborne against the animation machine.
Three retail mechanisms replace the stack:
- check_contact (0x0050f5b0) seeding in ResolveWithTransition: a body in
CONTACT seeds the transition's contact only while v.contactPlane.N <=
0.0002; moving away seeds the last-known plane alone (get_object_info
0x00511cc0). Ascending jumps therefore run contact-free (ballistic, no
glue) - the gate's reason-for-being is gone. The plane requirement is
strict: Contact-without-plane is unrepresentable in retail.
- SetPositionInternal-shaped commit (0x00515330, byte-read end-to-end,
velocity-sign-FREE): contact purely from the transition's contact
plane, HitGround on the airborne->walkable edge, HandleAllCollisions
with unmodified impact velocity. Whole commit gated on Ok &&
candidateMoved (retail pc:283657 skips SetPositionInternal entirely
when the candidate did not move) - a standing body's contact state is
never re-derived, which is what keeps rest bit-stable (AD-41 updated).
- Byte decodes: gate override state&0x800000=Sledding, zero branch
state&0x20000=Inelastic, reflect strictly dot<0 - our port already had
all three correct.
Settle: real landings (>=0.25 m/s) bounce and decay geometrically;
smaller impacts are consumed by retail's unconditional small-velocity
zero, so standing never micro-bounces. Re-baselines documented in place:
landing-survival pin measures decay post-settle; LiveCompare_Tick0/376
pin the new IsOnGround=false on zero-move ticks (captured true was the
retired seed echo; tick 376's captured body carries an 11.8 m/s grounded
velocity from the deleted get_state_velocity-overwrite era); de-overlap
fixture now carries the plane real grounded bodies always have. New
pins: LandingBounceSeedingTests (ascent no-seed, rest keeps contact,
strict plane, slope 5% reversal + tangential preservation, Sledding
override).
Investigation + implementation record:
docs/research/2026-07-30-landing-bounce-family.md. Complete Release
suite: 10,031 passed / 5 skips / 0 failures.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two new PlayerMovementController-level tests, exercising the real
production controller (not just the Core-level composed model in the
prior commit):
- Update_AnimationRootMotion_WalkSpeedUnaffectedByResidualVelocityFix:
ordinary root-motion walking (no fall/collision in flight) advances by
exactly the authored per-tick delta for 30 ticks with BodyVelocity
staying exactly zero throughout - the fix is a complete no-op for the
common case, pinning the L.3c hazard
(claude-memory/project_physics_collision_digest.md's DO-NOT-RETRY
table) at the Runtime level alongside the existing Core-level
GroundedRootMotion_FrictionThreshold_DoesNotHammerLocomotionTests pin
(unmodified, still green).
- Update_RunningJumpLandsOnFlatGround_ResidualVelocitySurvivesAndDecays_NotFrozen:
a real charged running jump lands on flat ground and its residual
horizontal speed survives the first post-landing tick, then measurably
decays (dot(velocity, (0,0,1)) ~ 0 < 0.25, so friction engages here,
unlike the sloped roof capture where it doesn't).
Building the second test surfaced two genuinely separate, already-
correctly-scoped mechanisms unrelated to #265/#166, requiring no
production change: MotionInterpreter.LeaveGround (CMotionInterp::
LeaveGround 0x00528b00, R3-W4/J7/J8) recomputes velocity from the
CURRENT interpreted command on the grounded->airborne edge tick, so the
test holds Forward for one extra tick before releasing it; and
MotionInterpreter.ApplyCurrentMovementInterpreted's AP-77 "animation-less
/headless movement fallback" (already correctly scoped in the
divergence register) independently rewrites grounded velocity when no
DefaultSink is wired, so the test wires a minimal
FakeAnimationDispatchSink to match production's always-wired sink.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Wire ConstraintManager.ConstrainTo at every current acdream inbound-position
acceptance seam, matching retail SmartBox::HandleReceivedPosition
(0x00453fd0):
- Remote (player + NPC): LiveEntityNetworkUpdateController arms right after
the hard-teleport branch (remotePlacementRequired) returns - reaching that
point already means MoveOrTeleport did NOT hard-place - anchored to the
object's own live IPhysicsObjHost.Position.
- Local player teleport: PlayerMovementController.SetPositionCore now runs
UnConstrain (retail teleport_hook 0x00514ed0, previously a no-op because
nothing armed the leash) then re-arms anchored to the just-snapped
position, composing with the existing StopCompletelyAtPhysicsObjectBoundary
velocity zero rather than duplicating it. CommitPreparedPosition mirrors
the same pair for the deferred player-mode-entry commit path.
- Local player ForcePosition: PlayerMovementController.BlipPosition arms
with NO preceding UnConstrain (retail BlipPlayer/SetPositionSimple
survives motion/velocity/stick, and the leash is no different).
Push PhysicsBody.IsFullyConstrained from PositionManager.IsFullyConstrained
at the SAME per-tick chokepoint each pump already runs AdjustOffset
(PlayerMovementController.Update, RuntimeRemotePhysicsUpdater.Tick/TickHidden)
so TS-35's read gate in jump_is_allowed sees live state instead of a stub
that is never written.
Tests: local-player arm/teardown/rearm/taper/jump-refusal (Runtime.Tests,
PlayerMovementControllerTests), remote-tick IsFullyConstrained push
(Runtime.Tests, RuntimePhysicsStateTests). Full Core/Runtime/App suites
green with no regressions.
The render-alpha clamp test compared physics vs render position with
xunit precision:4 (Math.Round semantics); the AP-7 friction port shifts
the velocity-fallback trajectory by 7.6 um, landing two essentially
equal values on opposite sides of a 5e-5 rounding boundary. Assert with
a 1 mm tolerance instead. Merged-tree full Release suite: 9,887 passed /
0 failed / 5 skips including Headless (the exposed velocity-fallback
path holds).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ports the retail CACQualities/EncumbranceSystem/MovementSystem chain
(named-retail decomp pc 256393/412901-414050/416169-416320/695958+) so
PlayerWeenie's run rate, jump height, jump permission, and jump stamina
cost are real functions of burden, current stamina, and vitae/skill
enchantments instead of stubs.
Core:
- New EncumbranceSystem.cs (delegates to the already-verified
BurdenMath formulas — one source of truth for the burden HUD and
movement physics) and MovementSystem.cs (GetRunRate/GetJumpHeight/
JumpStaminaCost/GetJumpPower, decomp-cited; ACE cross-referenced
where BN dropped the general-case arithmetic entirely).
- PlayerWeenie rewritten as the CACQualities-shaped composition:
CanJump gates on burden (<2.0 load, UN-8 — x87 polarity resolved by
plausibility, Ghidra MCP unavailable this slice), JumpStaminaCost
returns the real ceil((load+0.5)*power*8+2) cost and always affords
it (matches decomp — retail's own function never refuses; "weak"
jump comes entirely from the stamina==0 skill-zeroing gate inside
InqRunRate/InqJumpVelocity, not a hard refusal), SetStamina wires a
null="unknown, don't gate" sentinel preserving every pre-P1 test.
- EnchantmentMath.GetMod gained an optional StatModType flag filter
(GetSkillMod convenience wrapper) so the SAME vitae/family-stacking
machinery already used for vital-max buffs now also answers "what's
the vitae+skill-enchantment-adjusted Run/Jump skill" — reusing the
M3 active-enchantment state, not a new engine.
Runtime:
- RuntimeCharacterState now stores the pre-EnchantSkill base run/jump
skill and recomputes the adjusted value (vitae first, then matching
Skill-flagged buffs, floor 0.5, truncate) on every base push AND on
every Spellbook.EnchantmentsChanged notification — a vitae change
alone moves the produced rate without a fresh PlayerDescription.
- RuntimeMovementSkillState extended with Burden/CurrentStamina
(RuntimeMovementSkillProjection.ApplyTo pushes both through the
existing seam); LiveSessionEventRouter recomputes burden from the
same Strength+aug-property+EncumbranceVal inputs the burden HUD
already assembles (reacting to the same ClientObjectTable events)
and pushes current stamina from LocalPlayerState vital updates.
- Wires the previously dead-lettered ReportExhaustion() R3-W4 seam:
LiveSessionRuntimeFactory's OnMovementStatsUpdated callback re-
applies the current snapshot to the live controller and forces an
immediate movement re-evaluation on any skill/burden/stamina change.
Register: retires TS-5 (CanJump/JumpStaminaCost stubs) and AP-25 (no
vitae in pushed skill). Adds AP-127 (two minor unmodeled retail bonus
properties + the stamina-buff-adjusts-local-copy nuance, deliberately
out of the bounded "run/jump query path only" scope) and UN-8 (the
CanJump x87 polarity call, flagged for a future Ghidra MCP
confirmation pass). Extends TS-23 (PlayerKillerStatus not parsed) to
cover JumpStaminaCost's new pk parameter, hardcoded false pending P3.
Full pseudocode + retail citations + the vitae/skill-level finding in
docs/research/2026-07-30-stat-coupled-movement-pseudocode.md.
Release suite: Core.Tests 3977/2 skips, Runtime.Tests 425/0 skips,
App.Tests 3968/3 skips — all green. (One pre-existing, unrelated Debug-
only flake in LandblockBuildOriginTests reproduces on the pre-P1
baseline and passes in Release; not touched here.)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>