feat(runtime): execute initial placement continuations

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>
This commit is contained in:
Erik 2026-08-02 03:49:56 +02:00
parent 4a8f74dc72
commit 5db3de3c7a
9 changed files with 8002 additions and 86 deletions

View file

@ -362,6 +362,20 @@ internal enum RuntimeInitialCreateResidenceCompletionStatus : byte
RejectedAuthority,
}
/// <summary>
/// Result of <see cref="RuntimeInitialCreateResidenceState.ConsumeExecuted"/>,
/// the executor-only release that supersedes the host's
/// <see cref="RuntimeInitialCreateResidenceState.AcknowledgeAdoption"/> once
/// the initial placement has been adopted.
/// </summary>
internal enum RuntimeInitialCreateResidenceExecutorReleaseStatus : byte
{
Released,
Revised,
RejectedToken,
RejectedAuthority,
}
/// <summary>
/// Exact post-residence receipt. A local graphical or no-window host may run
/// the retail after-enter teleport suffix only when this receipt carries
@ -385,6 +399,29 @@ internal readonly record struct RuntimeInitialCreateResidenceOwnershipSnapshot(
&& PendingAdoptionCount == 0;
}
/// <summary>
/// Round 4 R4-4: field-masked precision for
/// <see cref="RuntimeInitialCreateResidenceState.AdvanceExecutorBaseline"/>.
/// The blanket four-field re-sync the executor previously called after
/// EVERY apply silently absorbed an external race on whichever field(s) a
/// given apply did NOT itself move - e.g. an ObjDesc/Movement/State/Vector
/// apply never touches PositionAuthorityVersion/CreateIntegrationVersion/
/// FullCellId/PlacementCommitVersion, so blanket-resyncing all four there
/// would mask a genuine concurrent bump to one of them instead of letting
/// the next <see cref="RuntimeInitialCreateResidenceState.IsCompletedCurrent"/>
/// check catch it. Each caller now passes exactly the field(s) its OWN
/// mutation moved.
/// </summary>
[Flags]
internal enum RuntimeExecutorBaselineFields : byte
{
None = 0,
PositionAuthorityVersion = 1 << 0,
CreateIntegrationVersion = 1 << 1,
FullCellId = 1 << 2,
PlacementCommitVersion = 1 << 3,
}
/// <summary>
/// Owns only initial CreateObject residence leases. DAT lookup, body creation,
/// and presentation stay outside this owner; their immutable preparation is
@ -403,6 +440,48 @@ internal sealed class RuntimeInitialCreateResidenceState
internal required RuntimeEntityRecord Record { get; init; }
internal required RuntimeInitialCreateResidenceLease Lease { get; set; }
internal required RuntimeInitialCreateResidenceReceipt Receipt { get; set; }
/// <summary>
/// True once the continuation executor has consumed the initial
/// placement's acknowledged completion through
/// <see cref="AdoptCompletedPlacement"/>. A retained
/// <c>_acknowledgedPlacementCompletions</c> entry on
/// <see cref="RuntimeSetPositionState"/> blocks EVERY later placement
/// begin for the same key (see
/// <see cref="RuntimeSetPositionState.BeginAcceptedPlacementCore"/>'s
/// <c>HasRetainedCompletion</c> guard) — a Position continuation could
/// never start its own authored placement while the initial one still
/// sits unconsumed. Adoption resolves that deadlock by consuming the
/// proof exactly once, while this flag keeps the completed entry
/// itself "current" for placement-tracking purposes even though the
/// placement token is no longer separately tracked.
/// </summary>
internal bool PlacementAdopted { get; set; }
/// <summary>
/// Executor-tracked baseline for the four version/cell fields
/// <see cref="IsCompletedCurrent"/> compares against the LIVE record.
/// Seeded from <see cref="Receipt"/>'s own (frozen, identity-matching)
/// <c>Token</c>/<c>FullCellId</c>/<c>PlacementCommitVersion</c> at the
/// moment <see cref="Complete"/> first produces this entry, then kept
/// in sync by <see cref="AdvanceExecutorBaseline"/> every time the
/// continuation executor legitimately advances one of them while
/// applying a retained continuation. <see cref="Receipt"/>.Token
/// itself must NEVER be rebaselined — a caller (the executor) always
/// re-presents the SAME original token instance on every retry, and
/// <see cref="Complete"/>'s own token-identity match
/// (<c>completed.Receipt.Token == token</c>) depends on that struct
/// staying byte-identical. Splitting "identity" (the frozen token)
/// from "expected current value" (these fields) is what lets the
/// executor's own sequential mutations keep the entry current
/// without the residence mistaking its own controlled progress for
/// an external race - see the 2026-08-01 admission handoff's own
/// warning about exactly this risk.
/// </summary>
internal ulong ExpectedPositionAuthorityVersion { get; set; }
internal ulong ExpectedCreateIntegrationVersion { get; set; }
internal uint ExpectedFullCellId { get; set; }
internal ulong ExpectedPlacementCommitVersion { get; set; }
}
private readonly RuntimeEntityDirectory _entities;
@ -410,6 +489,7 @@ internal sealed class RuntimeInitialCreateResidenceState
private readonly Dictionary<RuntimeEntityKey, Entry> _entries = [];
private readonly Dictionary<RuntimeEntityKey, CompletedEntry> _completed = [];
private Func<RuntimeGenerationToken>? _generation;
private Action<RuntimeEntityKey>? _retirementNotification;
private ulong _nextLeaseId;
internal RuntimeInitialCreateResidenceState(
@ -432,6 +512,31 @@ internal sealed class RuntimeInitialCreateResidenceState
_generation = generation;
}
/// <summary>
/// Round 3 B3: the ONE choke point every residence retirement path -
/// <see cref="Retire(Entry)"/>, <see cref="Retire(CompletedEntry)"/>,
/// <see cref="Forget"/>, and <see cref="Clear"/> - notifies through,
/// regardless of which caller (a host query, a staleness check inside
/// this class, or the continuation executor itself) triggered the
/// retirement. Without this, a residence retired by a path OTHER than
/// the executor's own <c>DiscardProgress</c> call (e.g. a host's
/// <see cref="TryGetTransaction"/> silently discovering staleness) would
/// leave the executor's progress AND its separately-tracked pending
/// continuation placement token orphaned - this class owns no reference
/// to the executor type, so the lifetime binds a plain delegate here
/// instead.
/// </summary>
internal void BindRetirementNotification(Action<RuntimeEntityKey> notify)
{
ArgumentNullException.ThrowIfNull(notify);
if (_retirementNotification is not null)
{
throw new InvalidOperationException(
"The initial Create residence retirement notification is already bound.");
}
_retirementNotification = notify;
}
internal bool CanAcceptCreate(WorldSession.EntitySpawn incoming)
{
bool parented = (incoming.ParentGuid
@ -788,10 +893,59 @@ internal sealed class RuntimeInitialCreateResidenceState
Record = record,
Lease = lease,
Receipt = receipt,
ExpectedPositionAuthorityVersion = token.PositionAuthorityVersion,
ExpectedCreateIntegrationVersion = token.CreateIntegrationVersion,
ExpectedFullCellId = receipt.FullCellId,
ExpectedPlacementCommitVersion = receipt.PlacementCommitVersion,
});
return RuntimeInitialCreateResidenceCompletionStatus.Completed;
}
/// <summary>
/// Executor-only: re-synchronizes the completed entry's staleness
/// baseline (see <see cref="CompletedEntry.ExpectedPositionAuthorityVersion"/>
/// remarks) to the record's CURRENT live values, but ONLY for the
/// field(s) named in <paramref name="fields"/> (Round 4 R4-4). Called
/// after the continuation executor legitimately advances one or more of
/// PositionAuthorityVersion/CreateIntegrationVersion/FullCellId/
/// PlacementCommitVersion while applying a retained continuation, so a
/// LATER <see cref="Complete"/>/<see cref="IsCompletedCurrent"/> check
/// does not mistake the executor's own controlled progress for an
/// external race. Passing a field NOT actually moved by the caller's own
/// mutation would defeat the whole point - it would silently bless an
/// external race on that field instead of letting the next currency
/// check catch it - so every call site names exactly its own field(s);
/// an apply that moves none of the four tracked fields (ObjDesc,
/// Movement, State, Vector) must not call this method at all. A no-op
/// (returns false) if the token no longer matches a live completed
/// entry - the executor's own currency checks catch that condition
/// independently and this call is purely advisory bookkeeping, never a
/// source of truth by itself.
/// </summary>
internal bool AdvanceExecutorBaseline(
RuntimeEntityRecord record,
in RuntimeInitialCreateResidenceToken token,
RuntimeExecutorBaselineFields fields)
{
ArgumentNullException.ThrowIfNull(record);
if (!token.IsValid
|| !_completed.TryGetValue(token.Entity, out CompletedEntry? entry)
|| !ReferenceEquals(entry.Record, record)
|| entry.Receipt.Token != token)
{
return false;
}
if ((fields & RuntimeExecutorBaselineFields.PositionAuthorityVersion) != 0)
entry.ExpectedPositionAuthorityVersion = record.PositionAuthorityVersion;
if ((fields & RuntimeExecutorBaselineFields.CreateIntegrationVersion) != 0)
entry.ExpectedCreateIntegrationVersion = record.CreateIntegrationVersion;
if ((fields & RuntimeExecutorBaselineFields.FullCellId) != 0)
entry.ExpectedFullCellId = record.FullCellId;
if ((fields & RuntimeExecutorBaselineFields.PlacementCommitVersion) != 0)
entry.ExpectedPlacementCommitVersion = record.PlacementCommitVersion;
return true;
}
internal bool AcknowledgeAdoption(
RuntimeEntityRecord record,
in RuntimeInitialCreateResidenceAdoptionToken token)
@ -816,7 +970,15 @@ internal sealed class RuntimeInitialCreateResidenceState
// discard accepted packets.
if (!current.Lease.Continuations.IsEmpty)
return false;
// The executor's own release path is ConsumeExecuted, not this host
// method. If the executor already adopted the placement proof
// (RuntimeInitialCreateContinuationExecutor.AdoptCompletedPlacement),
// it is gone from RuntimeSetPositionState's tracking table entirely —
// do not re-consume it a second time, just tolerate the already-
// satisfied state and fall through to the same removal every other
// caller of this host method observes.
if (current.Lease.Route.PerformsSetPosition
&& !current.PlacementAdopted
&& !_setPosition.ConsumeAcknowledgedPlacement(
current.Lease.Placement,
current.Receipt.Projection))
@ -841,6 +1003,7 @@ internal sealed class RuntimeInitialCreateResidenceState
lease = entry.Lease;
cancellation = _setPosition.ForgetExactPlacement(
lease.Placement);
_retirementNotification?.Invoke(key);
return true;
}
if (record.Key is { } completedKey
@ -853,6 +1016,7 @@ internal sealed class RuntimeInitialCreateResidenceState
lease = completed.Lease;
cancellation = _setPosition.ForgetExactPlacement(
lease.Placement);
_retirementNotification?.Invoke(completedKey);
return true;
}
lease = default;
@ -888,6 +1052,13 @@ internal sealed class RuntimeInitialCreateResidenceState
{
_setPosition.PublishCancellation(cancellations[index]);
}
if (_retirementNotification is { } notify)
{
foreach (Entry entry in active)
notify(entry.Lease.Token.Entity);
foreach (CompletedEntry entry in completed)
notify(entry.Receipt.Token.Entity);
}
}
internal RuntimeInitialCreateResidenceOwnershipSnapshot CaptureOwnership() =>
@ -917,6 +1088,24 @@ internal sealed class RuntimeInitialCreateResidenceState
return _generation?.Invoke() ?? default;
}
/// <summary>
/// The staleness check every completed-entry caller shares. Compares the
/// live record against <see cref="CompletedEntry.ExpectedPositionAuthorityVersion"/>
/// et al — an executor-tracked, continuously re-synchronized baseline —
/// rather than against <see cref="RuntimeInitialCreateResidenceReceipt.Token"/>'s
/// own FROZEN admission-time fields directly. This is what lets the
/// continuation executor's own legitimate mutations
/// (AdvancePositionAuthority, AdvanceCreateAuthority, SetFullCell,
/// AdvancePlacementCommit — all driven by applying a retained
/// continuation) keep this entry current across the many
/// <see cref="Complete"/> re-entries a multi-call drain requires, while
/// still correctly detecting a genuine EXTERNAL race (anything that
/// changes one of these fields WITHOUT going through
/// <see cref="AdvanceExecutorBaseline"/>) exactly as it always did. The
/// token itself remains the untouched identity/match key -
/// <see cref="Complete"/>'s <c>completed.Receipt.Token == token</c> check
/// depends on that.
/// </summary>
private bool IsCompletedCurrent(CompletedEntry entry)
{
RuntimeInitialCreateResidenceReceipt receipt = entry.Receipt;
@ -925,35 +1114,147 @@ internal sealed class RuntimeInitialCreateResidenceState
&& _entities.SessionLifetimeVersion
== receipt.Token.SessionLifetimeVersion
&& entry.Record.PositionAuthorityVersion
== receipt.Token.PositionAuthorityVersion
== entry.ExpectedPositionAuthorityVersion
&& entry.Record.CreateIntegrationVersion
== receipt.Token.CreateIntegrationVersion
&& entry.Record.FullCellId == receipt.FullCellId
== entry.ExpectedCreateIntegrationVersion
&& entry.Record.FullCellId == entry.ExpectedFullCellId
&& entry.Record.PlacementCommitVersion
== receipt.PlacementCommitVersion
== entry.ExpectedPlacementCommitVersion
&& entry.Lease.Route.Authority.Generation
== CurrentGeneration()
&& receipt.Token.SessionLifetimeVersion
== receipt.Adoption.SessionLifetimeVersion
&& receipt.Token.LeaseId == receipt.Adoption.LeaseId
// A completed entry whose placement proof the executor already
// adopted remains current on the placement dimension without
// re-querying RuntimeSetPositionState: AdoptCompletedPlacement
// consumed (removed) the exact tracked token, so
// IsPlacementCompletionTracked would now report false even though
// nothing here has gone stale.
&& (!entry.Lease.Route.PerformsSetPosition
|| entry.PlacementAdopted
|| _setPosition.IsPlacementCompletionTracked(
entry.Lease.Placement));
}
/// <summary>
/// Executor-only: consumes the initial placement's acknowledged
/// completion exactly once so a later retained Position continuation can
/// begin its own authored placement for the same
/// <see cref="RuntimeEntityKey"/> (see the remarks on
/// <see cref="CompletedEntry.PlacementAdopted"/> for why this is
/// necessary). Idempotent: a retry after <see cref="PlacementAdopted"/> is
/// already true is a no-op success, never a double-consume.
/// </summary>
internal bool AdoptCompletedPlacement(
RuntimeEntityRecord record,
in RuntimeInitialCreateResidenceToken token)
{
ArgumentNullException.ThrowIfNull(record);
if (!token.IsValid
|| !_completed.TryGetValue(token.Entity, out CompletedEntry? entry)
|| !ReferenceEquals(entry.Record, record)
|| entry.Receipt.Token != token)
{
return false;
}
if (entry.PlacementAdopted)
return IsCompletedCurrent(entry);
if (!IsCompletedCurrent(entry))
{
Retire(entry);
return false;
}
if (!entry.Lease.Route.PerformsSetPosition)
{
// A Parented/PickedUp lease never captured a real placement
// token; there is nothing to consume, but the tail must still be
// able to progress past this step exactly once.
entry.PlacementAdopted = true;
return true;
}
if (!_setPosition.ConsumeAcknowledgedPlacement(
entry.Lease.Placement,
entry.Receipt.Projection))
{
return false;
}
entry.PlacementAdopted = true;
return true;
}
/// <summary>
/// Executor-only release: consumes the residence entirely once the exact
/// adoption token still matches AND the caller has applied every
/// continuation through the CURRENT lease's full length. Placement
/// consumption already happened via <see cref="AdoptCompletedPlacement"/>,
/// so this does not call <see cref="RuntimeSetPositionState.ConsumeAcknowledgedPlacement"/>
/// a second time for an adopted entry — unlike the host-facing
/// <see cref="AcknowledgeAdoption"/>, which only ever runs for entries the
/// executor has not touched.
/// </summary>
internal RuntimeInitialCreateResidenceExecutorReleaseStatus ConsumeExecuted(
RuntimeEntityRecord record,
in RuntimeInitialCreateResidenceAdoptionToken token,
ulong executedThroughSequence)
{
ArgumentNullException.ThrowIfNull(record);
if (!token.IsValid
|| !_completed.TryGetValue(token.Entity, out CompletedEntry? entry)
|| !ReferenceEquals(entry.Record, record)
|| entry.Receipt.Adoption.Entity != token.Entity
|| entry.Receipt.Adoption.LeaseId != token.LeaseId)
{
return RuntimeInitialCreateResidenceExecutorReleaseStatus
.RejectedToken;
}
if (!IsCompletedCurrent(entry))
{
Retire(entry);
return RuntimeInitialCreateResidenceExecutorReleaseStatus
.RejectedAuthority;
}
if (entry.Receipt.Adoption.Revision != token.Revision)
{
// A newer continuation arrived mid-drain (Enqueue bumps Revision
// in place on the SAME completed entry). The executor must
// re-fetch via Complete and drain the tail, never replay the
// already-applied prefix.
return RuntimeInitialCreateResidenceExecutorReleaseStatus.Revised;
}
if (!entry.PlacementAdopted && entry.Lease.Route.PerformsSetPosition)
{
throw new InvalidOperationException(
"Executor release requires the initial placement to have been adopted first.");
}
if ((ulong)entry.Lease.Continuations.Length != executedThroughSequence)
{
return RuntimeInitialCreateResidenceExecutorReleaseStatus
.RejectedAuthority;
}
return _completed.Remove(token.Entity)
? RuntimeInitialCreateResidenceExecutorReleaseStatus.Released
: RuntimeInitialCreateResidenceExecutorReleaseStatus
.RejectedAuthority;
}
private void Retire(Entry entry)
{
_entries.Remove(entry.Lease.Token.Entity);
RuntimeEntityKey key = entry.Lease.Token.Entity;
_entries.Remove(key);
RuntimePlacementCancellationReceipt cancellation =
_setPosition.ForgetExactPlacement(entry.Lease.Placement);
_setPosition.PublishCancellation(cancellation);
_retirementNotification?.Invoke(key);
}
private void Retire(CompletedEntry entry)
{
_completed.Remove(entry.Receipt.Token.Entity);
RuntimeEntityKey key = entry.Receipt.Token.Entity;
_completed.Remove(key);
RuntimePlacementCancellationReceipt cancellation =
_setPosition.ForgetExactPlacement(entry.Lease.Placement);
_setPosition.PublishCancellation(cancellation);
_retirementNotification?.Invoke(key);
}
}