feat(runtime): C3c - production placement cutover: both hosts on the residence conductors (routes 1+8)

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>
This commit is contained in:
Erik 2026-08-02 18:10:33 +02:00
parent 78f1eb1896
commit 529e0e9d88
68 changed files with 5977 additions and 831 deletions

View file

@ -70,7 +70,16 @@ public readonly record struct RuntimeEntityObjectOwnershipSnapshot(
/// keys - the remote/projectile Create-time body-construction conductor.
/// Dormant like its C3a sibling; converges to zero the same way.
/// </summary>
int RemoteFirstEntryActiveCount = 0)
int RemoteFirstEntryActiveCount = 0,
/// <summary>
/// C3c-R1 review F5: outstanding host first-entry drive entries
/// (<c>RuntimeFirstEntryDriveController</c> pending keys, summed over
/// every drive registered against this lifetime via
/// <see cref="RuntimeEntityObjectLifetime.RegisterFirstEntryDriveOwnership"/>).
/// Previously outside every ledger; gated by <see cref="IsConverged"/>
/// like the conductor counts it pumps.
/// </summary>
int FirstEntryDrivePendingCount = 0)
{
public bool IsConverged =>
IsDisposed
@ -94,6 +103,7 @@ public readonly record struct RuntimeEntityObjectOwnershipSnapshot(
&& PendingCompletionReceiptCount == 0
&& LocalPlayerFirstEntryActiveCount == 0
&& RemoteFirstEntryActiveCount == 0
&& FirstEntryDrivePendingCount == 0
&& StreamSubscriberCount == 0
&& PlacementStreamSubscriberCount == 0
&& PendingDispatchCount == 0
@ -137,6 +147,10 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
{
private bool _sessionClearInProgress;
private bool _disposed;
/// <summary>C3c: see <see cref="BindInitialResidenceBeginNotification"/>.</summary>
private Action<RuntimeEntityRecord>? _initialResidenceBegan;
/// <summary>C3c-R1 review F5: see <see cref="RegisterFirstEntryDriveOwnership"/>.</summary>
private readonly List<Func<int>> _firstEntryDriveOwnership = [];
public RuntimeEntityObjectLifetime(
uint firstLocalEntityId = RuntimeEntityDirectory.FirstLocalEntityId,
@ -437,7 +451,31 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
InitialCreateExecution.LastReplayFailure is not null,
InitialCreateExecution.PendingCompletionReceiptCount,
LocalPlayerFirstEntry.CaptureOwnership().ActiveCount,
RemoteFirstEntry.CaptureOwnership().ActiveCount);
RemoteFirstEntry.CaptureOwnership().ActiveCount,
CaptureFirstEntryDrivePendingCount());
}
private int CaptureFirstEntryDrivePendingCount()
{
int total = 0;
for (int i = 0; i < _firstEntryDriveOwnership.Count; i++)
total = checked(total + _firstEntryDriveOwnership[i]());
return total;
}
/// <summary>
/// C3c-R1 review F5: registers one host first-entry drive controller's
/// pending-count provider into this lifetime's ownership snapshot, so
/// tracked-but-undriven entries can never sit outside every ledger. The
/// drive controller registers itself at construction (it already binds
/// <see cref="BindInitialResidenceBeginNotification"/> there); multiple
/// registrations sum, mirroring the multicast notification shape.
/// </summary>
public void RegisterFirstEntryDriveOwnership(Func<int> pendingCount)
{
ArgumentNullException.ThrowIfNull(pendingCount);
EnsureNotDisposed();
_firstEntryDriveOwnership.Add(pendingCount);
}
public void BindEventContext(
@ -451,6 +489,22 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
InitialCreateExecution.BindGeneration(generation);
}
/// <summary>
/// C3c: registers one host callback fired for every FRESH initial-create
/// residence begin (never for a same-generation FIFO append). Multicast,
/// mirroring <see cref="RuntimeInitialCreateResidenceState.BindRetirementNotification"/>.
/// The callback runs synchronously inside the registration transaction —
/// subscribers must only record the entity for a later drive pump, never
/// call a conductor's Advance re-entrantly from it.
/// </summary>
public void BindInitialResidenceBeginNotification(
Action<RuntimeEntityRecord> began)
{
ArgumentNullException.ThrowIfNull(began);
EnsureNotDisposed();
_initialResidenceBegan += began;
}
/// <summary>
/// C0-2: forwards to <see cref="RuntimeInitialCreateContinuationExecutor.BindLiveInputs"/>,
/// the same fan-out shape <see cref="BindEventContext"/> already uses for
@ -2270,6 +2324,20 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
return InitialCreateResidences.TryGetCurrent(canonical, out lease);
}
/// <summary>
/// C3c-R1 review F7: host seam for a bounded-collision-neighborhood
/// host to convert a remote/projectile Create's active residence to the
/// celless completion route when its destination landblock will never
/// be collision-published (a headless far remote). See
/// <see cref="RuntimeInitialCreateResidenceState.TryConvertToCellessRoute"/>.
/// </summary>
public bool TryConvertInitialResidenceToCellessRoute(
RuntimeEntityRecord canonical)
{
EnsureNotDisposed();
return InitialCreateResidences.TryConvertToCellessRoute(canonical);
}
internal RuntimeInitialCreateResidenceCompletionStatus
CompleteInitialCreateResidence(
RuntimeEntityRecord canonical,
@ -2339,7 +2407,19 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
canonical,
accepted,
isLocalPlayer);
return lease.IsValid;
if (!lease.IsValid)
return false;
// C3c: host drive notification. Fires for EVERY fresh residence
// begin through this single choke point — wire-dispatch Creates AND
// the executor's deferred-child replays (which register through this
// class's own bound delegate, never through a host runtime). The
// subscriber must only RECORD the key for a later drive pump — this
// fires mid-registration, before Registered publishes, and a
// synchronous Advance here would interleave with the enclosing
// transaction (and, for a replayed child, with the parent's own
// in-flight Execute).
_initialResidenceBegan?.Invoke(canonical);
return true;
}
private Exception FailInitialResidenceRegistration(