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>
118 lines
4.7 KiB
C#
118 lines
4.7 KiB
C#
using AcDream.Runtime.Entities;
|
|
|
|
namespace AcDream.Runtime.Physics;
|
|
|
|
/// <summary>
|
|
/// Public host boundary for Runtime-owned SetPosition projection receipts.
|
|
/// The channel owns no placement state: observation, retry, and exact-token
|
|
/// acknowledgement delegate to the canonical entity lifetime's event stream
|
|
/// and SetPosition owner.
|
|
/// </summary>
|
|
public sealed class RuntimePlacementProjectionChannel
|
|
{
|
|
private readonly RuntimeEntityObjectEventStream _events;
|
|
private readonly RuntimeSetPositionState _setPosition;
|
|
private readonly RuntimeInitialCreateContinuationExecutor _initialCreateExecution;
|
|
private Func<RuntimeGenerationToken> _generation = static () => default;
|
|
private bool _generationBound;
|
|
|
|
internal RuntimePlacementProjectionChannel(
|
|
RuntimeEntityObjectEventStream events,
|
|
RuntimeSetPositionState setPosition,
|
|
RuntimeInitialCreateContinuationExecutor initialCreateExecution)
|
|
{
|
|
_events = events ?? throw new ArgumentNullException(nameof(events));
|
|
_setPosition = setPosition
|
|
?? throw new ArgumentNullException(nameof(setPosition));
|
|
_initialCreateExecution = initialCreateExecution
|
|
?? throw new ArgumentNullException(nameof(initialCreateExecution));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Observes ordered immutable projection receipts on the Runtime commit
|
|
/// thread. A host must acknowledge only after its projection succeeds.
|
|
/// </summary>
|
|
public IDisposable Subscribe(IRuntimePlacementObserver observer) =>
|
|
_events.SubscribePlacement(observer);
|
|
|
|
internal void BindGeneration(Func<RuntimeGenerationToken> generation)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(generation);
|
|
if (_generationBound)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"The Runtime placement generation source is already bound.");
|
|
}
|
|
|
|
_generation = generation;
|
|
_generationBound = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Acknowledges only the exact oldest outstanding receipt. Stale,
|
|
/// reordered, superseded, or already acknowledged tokens are rejected.
|
|
/// </summary>
|
|
public bool Acknowledge(
|
|
RuntimeGenerationToken expectedGeneration,
|
|
in RuntimePlacementProjectionToken token) =>
|
|
IsCurrent(expectedGeneration)
|
|
&& _setPosition.AcknowledgeProjection(token);
|
|
|
|
/// <summary>
|
|
/// Republishes every still-pending immutable receipt in canonical order.
|
|
/// Runtime authority is never replayed or recommitted by a retry.
|
|
/// </summary>
|
|
public bool RetryPending(RuntimeGenerationToken expectedGeneration)
|
|
{
|
|
if (!IsCurrent(expectedGeneration))
|
|
return false;
|
|
_setPosition.RetryPendingProjections();
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the exact oldest outstanding receipt without consuming it.
|
|
/// </summary>
|
|
public bool TryPeek(
|
|
RuntimeGenerationToken expectedGeneration,
|
|
out RuntimePlacementProjectionSnapshot projection)
|
|
{
|
|
if (IsCurrent(expectedGeneration))
|
|
return _setPosition.TryPeekProjection(out projection);
|
|
projection = default;
|
|
return false;
|
|
}
|
|
|
|
public int PendingCount => _setPosition.PendingProjectionCount;
|
|
|
|
/// <summary>
|
|
/// C3-1: the public host consumption shape for an initial-Create
|
|
/// continuation-executor drain's completion. Reached with the exact
|
|
/// <see cref="RuntimePlacementProjectionToken"/> carried by a
|
|
/// <see cref="RuntimePlacementProjectionKind.ExecutorCompleted"/> receipt
|
|
/// observed through <see cref="Subscribe"/> - the same correlation
|
|
/// identity (Entity/Sequence) every other placement Kind uses. Exposes
|
|
/// exactly the facts a cutover host needs to bind presentation off an
|
|
/// initial placement (the teleport-hook phase, the drained Position
|
|
/// continuations' route facts for constrain/interpolation binding, and
|
|
/// the replayed-deferred-child count) without widening any internal
|
|
/// Runtime type's accessibility. Returns false for a generation
|
|
/// mismatch or a stale/superseded/unknown token, mirroring every other
|
|
/// generation-gated method on this channel.
|
|
/// </summary>
|
|
public bool TryGetInitialCreateCompletion(
|
|
RuntimeGenerationToken expectedGeneration,
|
|
in RuntimePlacementProjectionToken token,
|
|
out RuntimeInitialCreatePlacementCompletion completion)
|
|
{
|
|
if (IsCurrent(expectedGeneration))
|
|
return _initialCreateExecution.TryGetCompletion(token, out completion);
|
|
completion = default;
|
|
return false;
|
|
}
|
|
|
|
private bool IsCurrent(RuntimeGenerationToken expectedGeneration) =>
|
|
_generationBound
|
|
&& expectedGeneration.Value != 0UL
|
|
&& expectedGeneration == _generation();
|
|
}
|