acdream/docs/research/2026-08-02-cutover-route-inventory.md
Erik 27e05b99e4 docs(physics): plan the placement production cutover
The continuation executor (5db3de3c) completed the dormant residence
mechanism; the cutover is the campaign leg that makes it production truth.
The committed 8-route inventory maps every duplicate placement authority in
both hosts with exact call chains, confirms the placement-receipt observer
seam is fully built but unattached, and surfaces five pre-cutover gaps the
shipped mechanism cannot yet express (executor-to-channel bridge, atomic
controller/body publication, the 1,880 B/op activation budget, Runtime-side
live-input derivation, the portal-authority adapter). The plan decomposes
the cutover into C0-C5 bisectable slices under the campaign's standing
contract/dual-review/gate discipline, ending at the connected routes and
the user visual matrix that retire AP-1/AD-1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 04:10:38 +02:00

1042 lines
64 KiB
Markdown

# Production cutover — 8-route inventory (2026-08-02)
Repo: `C:\Users\erikn\.codex\worktrees\af5e\acdream`, branch `codex/port-claude-agents`,
HEAD `9ad590dc`. READ-ONLY research; this file is the only write target.
Sources read first:
- `docs/research/2026-08-02-runtime-continuation-executor-handoff.md` (executor mechanism, complete/dormant)
- `docs/research/2026-07-31-remaining-physics-campaign-handoff.md` sections "What remains" (prereqs A-E) and "Production route cutover" (routes 1-8)
- `docs/research/2026-07-31-canonical-set-position.md` (1,880 B/op allocation finding, line 327-333)
- scratchpad `runtime-surface.md` sections 8-9 (GameRuntime construction, the two legacy `RegisterEntity` call sites)
Key confirmed facts carried in from the map:
- `RuntimeInitialCreateResidenceState`/executor is a COMPLETE, TESTED, but 100% UNWIRED
mechanism. Grep confirms (map file, line 545-549) the ONLY two production Create call
sites are:
- `src/AcDream.Runtime/Session/RuntimeLiveEntitySessionController.cs:76``Entities.RegisterEntity(spawn)` (headless/direct-host)
- `src/AcDream.App/World/LiveEntityRuntime.cs:537``_entityObjects.RegisterEntity(...)` (graphical/App)
Neither calls `RegisterEntityWithInitialResidence`. `CompleteInitialCreateResidence`/
`AcknowledgeInitialCreateResidenceAdoption` have NO production caller anywhere.
- `GameRuntime.cs:180-183` constructs `RuntimeEntityObjectLifetime` once as `context.EntityObjects`.
`BindEventContext` at `GameRuntime.cs:266-269` supplies the generation function
`() => generationReset.ActiveRetiringGeneration ?? context.Session.Generation`.
- 1,880 B/op measured on the warmed dormant placement commit/ack route vs a 2,048 B cap
(`docs/research/2026-07-31-canonical-set-position.md:327-333`) — explicit 4B2 activation
blocker, not yet resolved (pool/remove envelopes or record an approved budget).
---
## Cross-cutting: observer-seam status (grep results)
`IRuntimePlacementObserver` **already exists** as a Runtime-side interface —
it is NOT vaporware, contrary to a naive reading of "prerequisite D" as
all-still-to-build:
- Defined `src/AcDream.Runtime/Entities/RuntimeEntityObjectEventStream.cs:18-21`
(`interface IRuntimePlacementObserver { void OnPlacement(in RuntimePlacementDelta delta); }`).
- `RuntimeEntityObjectEventStream` (same file) already has the full pub/sub
plumbing: `_placementObservers` array (line 40), `SubscribePlacement`
(119-133, copy-on-write add), `UnsubscribePlacement` (356-369), dispatch
loop (302-310ish inside the drain routine), `PublishPlacement` (164-171,
builds a `RuntimePlacementDelta(NextStamp(), placement)` and enqueues it
through the same ordered synchronous drain as entity/inventory deltas).
- Public host seam: `src/AcDream.Runtime/Physics/RuntimePlacementProjectionChannel.cs`
(88 lines, read in full) — `Subscribe(IRuntimePlacementObserver)` (31-32,
thin passthrough to `_events.SubscribePlacement`), `Acknowledge(expectedGeneration, token)`
(51-55, generation-gated call into `_setPosition.AcknowledgeProjection`),
`RetryPending` (61-67), `TryPeek` (72-80, non-consuming), `PendingCount` (82).
This channel is constructed once inside `RuntimeEntityObjectLifetime`'s ctor
(`Placements = new RuntimePlacementProjectionChannel(Events, Physics.SetPosition)`,
per the surface map's file-1 section) and exposed on `GameRuntime.Placements`.
- Receipt shape: `RuntimePlacementProjectionSnapshot` (`RuntimeSetPositionState.cs:210-217`):
`Token, Kind (Withdraw|Place|Discard — enum at line 57-61), WorldPosition,
Orientation, CellLocalPosition, InContact, OnWalkable`.
- **What does NOT exist**: grep across `src/AcDream.App/**/*.cs` and
`src/AcDream.Headless/**/*.cs` for `IRuntimePlacementObserver`,
`SubscribePlacement`, or `Placements.Subscribe` returns ZERO matches
(confirmed via `grep -rln` across both trees; the only hits anywhere in the
repo outside `src/AcDream.Runtime/` are 5 Runtime-test fake-observer classes
in `tests/AcDream.Runtime.Tests/**`, e.g.
`RuntimeInitialCreateResidenceStateTests.cs:2651`,
`RuntimeSetPositionStateTests.cs:2610`/`2641` (`ThrowingPlacementObserver`),
`RuntimeCollisionPrefixQuiescenceTests.cs:877`,
`RuntimeLocalPlayerPhysicsPublicationStateTests.cs:2326`). **Neither host
has ever constructed a production `IRuntimePlacementObserver` implementation.**
Prerequisite D ("Add one graphical and one headless `IRuntimePlacementObserver`
using `GameRuntime.Placements`") is 100% unbuilt on the host side even though
the Runtime-side plumbing it will attach to is complete and tested.
## Cross-cutting: connected-gate harness (exact scripts/env)
- **Exact lifecycle/reconnect gate**: `tools/run-connected-world-lifecycle-gate.ps1`
(477 lines, read in full). Drives two sequential `AcDream.App.exe` sessions
against the always-up local ACE (127.0.0.1:9000):
1. `capped` session using route script `tools/connected-world-lifecycle.route.txt`,
expects exactly 6 named checkpoints (`capped_login, aerlinthe_first, rynthid,
facility_hub, holtburg_after_dungeon, aerlinthe_revisit`) and 5 screenshots.
2. `uncapped-reconnect` session (starts as soon as ACE's own log records the
first session's transport Disconnect — no artificial settle delay) using
`tools/connected-world-reconnect.route.txt`, expects one checkpoint
`uncapped_reconnect`.
Per-session env (lines 250-281): `ACDREAM_DAT_DIR`, `ACDREAM_LIVE=1`,
`ACDREAM_TEST_HOST=127.0.0.1`, `ACDREAM_TEST_PORT=9000`,
`ACDREAM_TEST_USER`/`ACDREAM_TEST_PASS`, `ACDREAM_RETAIL_UI=1`,
`ACDREAM_FRAME_PROF=1`, `ACDREAM_UNCAPPED_RENDER` (1 for the reconnect leg
only), `ACDREAM_UI_PROBE_SCRIPT=<route.txt path>`,
`ACDREAM_AUTOMATION_ARTIFACT_DIR=<per-session artifact dir>`; several other
vars explicitly cleared (`ACDREAM_RENDER_BACKEND`, `ACDREAM_NET_DROP_*`,
`ACDREAM_DUMP_MOVE_TRUTH`, `ACDREAM_WB_DIAG`) to keep the gate a clean
baseline. `Validate-Checkpoint` (140-227) asserts, per checkpoint: reveal
readiness/no invariant failures, Runtime environment ownership initialized
with exactly one active day group, **every `transitOwnership` counter
(`bufferedTeleportDestinationCount, pendingTeleportStartCount,
activeTeleportCount, acceptedTeleportDestinationCount, activeRevealCount,
pendingDestinationReadinessCount, hostProjectionCount,
pendingHostAcknowledgementCount`) is exactly zero at a stable checkpoint**,
zero pending live teardowns/landblock retirements/staged mesh
uploads/composite warmups, and (optionally) zero collision-shadow
mismatches/faults. Client must exit only via graceful `WM_CLOSE`
(`Close-ClientGracefully`, 85-92) and ACE's own log must record
`graceful logout confirmed` + a `PacketHeader Disconnect` drop
(347-354) — **this cutover MUST add placement-owner counters
(pending placement receipts, active leases, pending FIFO continuations) to
this SAME zero-at-stable-checkpoint discipline**, matching the existing
`transitOwnership`/`resources` pattern, or the gate will not actually prove
placement convergence.
- **Canonical nine-stop route**: `tools/run-connected-r6-soak.ps1` (only first
60 lines read) driving `tools/connected-r6-soak.route.txt`, 9 named
checkpoints in order: `caul-baseline, sawato-baseline, rynthid, aerlinthe,
sawato-return, holtburg, caul-return, sawato-plateau, caul-plateau` (Caul/
Sawato/Holtburg legs additionally "Exercise"d — likely movement stress, not
just a static stop). Supports `-Uncapped`/`-DenseTown` switches (DenseTown
swaps in `connected-dense-town.route.txt` against a single `arwic-dense`
checkpoint instead). Same env-var family as the lifecycle gate
(`-SkipBuild`, `-LoginTimeoutSeconds`, `-CollisionShadowEvery`). Historical
evidence of both capped and uncapped passing runs:
`docs/research/2026-07-25-slice-g5-production-profile.md:38-39`
(`logs/connected-r6-soak-20260725-105133.report.json` /
`...-121035.report.json`).
- Both scripts require a pre-existing, already-listening local ACE on UDP
9000 and a live `AceLogPath` (default `C:\ACE\Server\ACE_Log.txt`) and will
themselves invoke `dotnet build ... -c Release` unless `-SkipBuild`.
## Cross-cutting: 1,880 B/op allocation budget (4B2 activation blocker)
`docs/research/2026-07-31-canonical-set-position.md:327-333`: "The warmed
immediate commit/ack route currently measures exactly **1,880 managed bytes
per operation** in the Release Runtime test host (1,000 iterations after 64
warmups); the regression gate caps it at 2,048 bytes. This dormant-path
result is an explicit 4B2 activation blocker rather than a claim of
allocation-free production readiness: 4B2 must either pool/remove the
operation and projection envelopes or record an approved measured budget
before routing frame-frequency placement through this owner." Restated
verbatim in the executor handoff
(`docs/research/2026-08-02-runtime-continuation-executor-handoff.md:181-184`).
**Not yet resolved as of this research pass** — no commit after
`2026-07-31-canonical-set-position.md` claims pooling/removal of the
operation/projection envelopes, and the executor handoff still calls it "the
standing 4B2 activation blocker." This matters most for routes 2 (Local
ForcePosition, frame-frequency-ish under repeated corrections), 4 (remote
Position, genuinely per-network-tick high frequency), and 5 (projectile
per-quantum integration, explicitly excluded from SetPosition routing for
exactly this reason per route 5's "Do not route ordinary per-quantum
projectile integration through SetPosition").
## Cross-cutting: `Execute`'s caller-supplied live inputs are unwired in BOTH hosts
`RuntimeInitialCreateContinuationExecutor.Execute` takes an
`in RuntimeInitialCreateExecutionInputs inputs` parameter
(`RuntimeInitialCreateContinuationExecutor.cs:47-49`):
`bool UsePositionFromServer, float PlayerDistance` — doc comment (38-46):
"Executor-time inputs sampled at the retail decision point. These cannot be
retained at admission time because they describe LIVE state (the local
player, the current physics simulation)... contact is NOT one of these — it
comes solely from the retained wire packet's own `IsGrounded` bit, never from
a live body query." Both fields feed directly into
`RuntimeAuthoritativePositionRouteClassifier.ClassifyAcceptedPosition`'s
`RuntimeAcceptedPositionRouteRequest.UsePositionFromServer`/`PlayerDistance`
fields (`RuntimeAuthoritativePositionRouteClassifier.cs:137`, consumed at
line 338 for the LocalPlayer interpolate-vs-not branch and later for the
Remote/Projectile near/far 96 m threshold). **Grep across
`src/AcDream.App/` and `src/AcDream.Headless/` for `UsePositionFromServer`
returns ZERO source hits (only compiled `bin`/`obj` binary matches).**
Neither host currently computes or tracks a `UsePositionFromServer` concept
anywhere in production code — this is a genuinely new per-call input a
cutover caller must supply on every `Execute` call, not something that can be
threaded through unchanged from an existing option/flag. `PlayerDistance` is
likely derivable today from existing local-player/WorldEntity state (it is
NOT a new concept — some form of distance-to-local-player computation
already exists for other purposes, e.g. streaming radius), but it has never
been wired specifically into this call contract either.
---
## Prerequisite C context (atomic local controller/body publication)
Campaign handoff (`docs/research/2026-07-31-remaining-physics-campaign-handoff.md:203-221`)
requires ONE Runtime-owned exclusive/versioned controller/body publication
transaction "respected by **every** canonical body writer" — rejects the
prior prototype (a snapshot/rollback lease) as failure-atomic-only, not
reentrant-safe (line 143-168: a nested SetPosition/remote-bind/GUID-reuse/
clock-epoch-change/disposal racing the open lease could let the outer
rollback erase newer authority or detach a body another owner already uses).
Today's per-host body construction (both confirmed by direct read, both are
literally the two things prerequisite C must unify):
- **Graphical**: `PlayerModeController.BuildControllerAndCamera`
(`src/AcDream.App/Input/PlayerModeController.cs:244-...`, only the first
~140 lines read directly by me) constructs `new PlayerMovementController(...)`
(line 259-262) directly against `_physics`/`playerRecord.ObjectClock`, wires
a `MoveToManager` facade and `EntityPhysicsHost` closures over captured
locals (267-345+), and stores the result in the App-local `_controllerSlot.Controller`
(not `_runtime.MovementOwner.Controller` directly at this point in the file —
Agent 1's detailed route-1 report is authoritative for exactly where/whether
this crosses into `RuntimeLocalPlayerMovementState`/`GameRuntime.MovementOwner`).
- **Headless**: `HeadlessSessionWorldProjection.CreateController`
(`src/AcDream.Headless/Hosting/HeadlessSessionWorldProjection.cs:639-655`)
constructs `new PlayerMovementController(_runtime.EntityObjects.Physics.Engine,
record.ObjectClock, PlayerMovementConstructionOptions.From(...))` (642-646),
applies physics state/step heights/movement skills (647-652), and is the
ONLY production writer of `_runtime.MovementOwner.Controller = controller`
in the entire repo (confirmed by grep — the graphical host does NOT write
`_runtime.MovementOwner.Controller` anywhere findable by that exact token;
it may go through a different Runtime seam — flag as an open question for
whoever designs prerequisite C: **confirm whether the graphical host's
built controller is ever actually published into `RuntimeLocalPlayerMovementState`
the same way headless's is, or whether this is itself an existing
graphical/headless asymmetry prerequisite C must resolve**).
- `HeadlessSessionWorldProjection.SynchronizeLocalPlayer`
(566-615, read in full) is the duplicate placement authority for headless
route 1/8: calls `_runtime.EntityObjects.Physics.Engine.Resolve(...)` (589-594)
then `.ResolvePlacement(...)` (595-605) directly, then
`controller.SetPosition(resolved.Position, resolved.CellId, wirePosition)`
(610-613) and `controller.SetBodyOrientation(orientation)` (614) — entirely
independent of `RuntimeSetPositionState`/`RuntimeInitialCreateResidenceState`.
`HeadlessSessionWorldProjection.BlipLocalPlayer` (617-637) similarly calls
`controller.BlipPosition(...)` (633-636) directly with no Runtime placement
commit/ack in between — this is the headless twin of route 2's "direct
BlipPosition/pre-commit acknowledgement" duplicate authority.
---
## Cross-cutting: accessibility is NOT a blocker (correction of an initial hypothesis)
I initially suspected the residence/executor surface being entirely `internal`
(`RegisterEntityWithInitialResidence`, `InitialCreateResidences`,
`InitialCreateExecution`, `Execute`, `CompleteInitialCreateResidence`,
`AcknowledgeInitialCreateResidenceAdoption` — confirmed all `internal` via
direct grep of `RuntimeEntityObjectLifetime.cs` and
`RuntimeInitialCreateContinuationExecutor.cs:426`) might block the graphical
host (a different assembly) from calling them at all. **This is WRONG —
verify before citing it as a gap.** `src/AcDream.Runtime/AcDream.Runtime.csproj:11-16`
declares `InternalsVisibleTo` for `AcDream.Runtime.Tests`, **`AcDream.App`**,
`AcDream.App.Tests`, `AcDream.Core.Tests`, **`acdream-headless`**, and
`AcDream.Headless.Tests` — both hosts already have full internal-member
access to `AcDream.Runtime`. Route 1's research agent independently confirmed
this at the exact same csproj lines. **There is no accessibility barrier to
either host calling `RegisterEntityWithInitialResidence`/driving the executor
today** — the reason nobody does is purely that the call sites haven't been
switched yet, not an assembly-boundary problem. Do not resurrect this as a
"gap" in the final report.
---
## Route 1 — Initial login and CreateObject (from parallel research agent, spot-checked)
### Graphical host chain (wire -> presentation)
1. `src/AcDream.App/Net/LiveEntitySessionController.cs:51-53``OnSpawned` wire
entry, routes `CreateObject` via `RetailInboundEventDispatcher.Run` to
`LiveEntityHydrationController.OnCreate`.
2. `src/AcDream.App/World/LiveEntityHydrationController.cs:226-250``OnCreate`
(dormant/stale-generation classification) -> `OnCreateCore`.
3. `LiveEntityHydrationController.cs:259-262``OnCreateCore` (under `_datLock`)
calls `_runtime.RegisterLiveEntity(spawn)`.
4. `src/AcDream.App/World/LiveEntityRuntime.cs:526-539`, **line 537**:
`_entityObjects.RegisterEntity(incoming, RetirePriorProjection)` — the
LEGACY non-residence call, confirmed exact line from the handoff doc.
5. `src/AcDream.Runtime/Entities/RuntimeEntityObjectLifetime.cs:315-322`
`RegisterEntity` -> `RegisterEntityCore(incoming, beginInitialResidence:false, ...)`
— never touches `InitialCreateResidences`/`InitialCreateExecution`.
6. `LiveEntityHydrationController.cs:288-294``ApplyAcceptedSpawn(...)` commits
the accepted snapshot (still cellless authority, no position/rebucket yet).
7. `LiveEntityHydrationController.cs:938-1040` (`ProjectExactOnce`), line 1036:
`_materializer.TryMaterialize(...)`.
8. `src/AcDream.App/Rendering/DatLiveEntityProjectionMaterializer.cs:407-427` ->
`MaterializeProjection` (680-857).
9. **Duplicate authority**`DatLiveEntityProjectionMaterializer.cs:712-737`:
`_runtime.MaterializeLiveEntity(..., out expectedRecord)` with the
`residence` parameter OMITTED, defaulting to
`LiveEntityMaterializationResidence.LegacyImmediate`
(`LiveEntityRuntime.cs:596-597`).
10. `LiveEntityRuntime.cs:589-789` (`MaterializeLiveEntity`) — constructs the
`WorldEntity` (sets Position/Rotation/ParentCellId directly from
wire-decoded world position, ~718-729); because residence is
`LegacyImmediate` not `AwaitRuntimePlacement`, falls through at **line 777**
to `RebucketLiveEntity(serverGuid, fullCellId)`.
11. **Duplicate authority**`LiveEntityRuntime.cs:792-931` (`RebucketLiveEntity`):
immediately calls `_spatial.RebucketLiveEntity(...)` (spatial bucket
mutation), sets `IsSpatiallyProjected/Visible`, calls
`_entityObjects.CommitRebucket(...)` (canonical `FullCellId`/
`CanonicalLandblockId` write), resets/suspends the object clock — all
synchronous, no Runtime placement receipt involved anywhere in this path.
12. `DatLiveEntityProjectionMaterializer.cs:791-820` — builds collision, binds
projectile runtime; for static-animating physics objects,
`RegisterAnimation` (859-1021) at **1003-1016** calls
`_runtime.GetOrCreatePhysicsBody(spawn.Guid, incarnation => new
PhysicsBody{...}.SnapToCell(...))` — a SECOND, narrower body-construction
duplicate authority (non-player static-animation bodies).
13. `LiveEntityHydrationController.cs:739-759`/`1070-1098` — publish-ready via
`ILiveEntityReadyPublisher.Publish`.
14. `src/AcDream.App/Input/PlayerModeAutoEntry.cs:214-230` — per-frame guard;
once `IsPlayerEntityPresent` + `IsWorldReady` (world-reveal, unrelated to
any Runtime placement receipt) -> `EnterPlayerMode()` ->
`PlayerModeController.EnterFromAutoEntry`.
15. `src/AcDream.App/Input/PlayerModeController.cs:126-133,217-242` ->
`TryEnter` -> **`BuildControllerAndCamera`** (244-525).
16. **Duplicate authority**`PlayerModeController.cs:409-413`:
`_physics.Resolve(playerEntity.Position, initialCellId, Vector3.Zero, 100f)`.
17. **Duplicate authority**`PlayerModeController.cs:422-430`:
`_physics.ResolvePlacement(...)` using a locally computed Setup cylinder
(`_motionBindings.GetSetupCylinder`) — no shared "exact Setup mover" step.
18. **Duplicate authority**`PlayerModeController.cs:449-480`: builds
`EntityPhysicsHost`/`MoveToManager`, installs via
`EntityPhysicsHostComposition.SelectStableHostWithoutRebind`/`InstallOrRebind`
(`src/AcDream.App/Physics/EntityPhysicsHostComposition.cs:16,35`) — its own
body/controller construction, independent of headless's copy.
19. Camera — `PlayerModeController.cs:440-447`: constructs
`ChaseCamera`/`RetailChaseCamera`, `_camera.EnterChaseMode(...)`
(host-only concern, stays, but must be gated by the Runtime ack not
ad-hoc resolve success).
20. **Duplicate authority — final commit**`PlayerModeController.cs:482-484`:
`playerEntity.SetPosition(initial.Position); playerEntity.ParentCellId =
initial.CellId; controller.CommitPreparedPosition();` — direct write,
bypasses any Runtime `Place` receipt.
### Headless host chain
Same shape as the graphical route through `RegisterEntity`/`ApplyAcceptedSpawn`
(`RuntimeLiveEntitySessionController.cs:73-95`, see Route 8 section above for
the full per-line breakdown), then `IRuntimeDirectWorldProjection.ProjectSpawn`
-> `HeadlessSessionWorldProjection.SynchronizeLocalPlayer`
(566-615)/`CreateController` (639-655) — same duplicate-authority shape as
graphical hops 16-20, but a SEPARATELY HAND-WRITTEN copy, not shared code
(confirmed by the codebase's own comment at
`HeadlessSessionWorldProjection.cs:685-689` contrasting itself with "the
graphical `PlayerModeController.ApplyStepHeights`"). No headless equivalent of
graphical hops 12-14/19 exists (no `WorldEntity`/render sidecar/camera).
### What each hop must become / capability gaps (route 1)
- Switch `RegisterLiveEntity`/`OnSpawned` from `RegisterEntity` to
`RegisterEntityWithInitialResidence`**no accessibility blocker** (see
correction above); this is a pure call-site + follow-up-loop change.
- DELETE `MaterializeProjection`'s direct `RebucketLiveEntity` fall-through;
pass `residence: LiveEntityMaterializationResidence.AwaitRuntimePlacement`
(the enum value ALREADY EXISTS and is used by other routes via
`LiveEntityRuntime.cs:765-776`/`938-1127`'s
`TryApplyRuntimePlacementPlace`/`Withdrawal` — this route just isn't
plumbed into it yet).
- DELETE `PlayerModeController.cs:409-430` (own Resolve/ResolvePlacement) and
`482-484` (direct SetPosition/CommitPreparedPosition); body/controller
construction becomes acknowledge-only from the shared Runtime-resolved
position.
- DELETE headless `SynchronizeLocalPlayer:589-607` and `610-614` the same way.
- DELETE the legacy `RegisterEntity` overload entirely once both hosts move
(`RegisterEntityWithInitialResidence` is already a strict superset).
- **Gap 1 (real)**: no shared "exact Setup mover" / DAT-preparation API.
`RuntimeInitialCreateResidenceState.Own` only OPENS the placement slot
(`TryBeginExclusiveAuthoredPlacement`); nothing in Runtime resolves a
Setup-derived cylinder/sphere-list and feeds it into
`RuntimeSetPositionState.SubmitPreparedPlacement`. Today that DAT read +
Resolve/ResolvePlacement call is hand-duplicated with DIFFERENT defaults in
App (`_motionBindings.GetSetupCylinder`) vs. headless (hardcoded
`DefaultRadius=0.48f`/`DefaultHeight=1.835f` unless `_preparedCollision`
supplies a Setup) — this is exactly prerequisite B, confirmed still open.
- **Gap 2 (real)**: no shared "atomic Runtime controller/body" owner exists
anywhere (`RuntimeEntityRecord` has storage slots — `PhysicsBody`,
`PhysicsHost` — but no construction logic); `BuildControllerAndCamera` and
`HeadlessSessionWorldProjection.CreateController` are two independently
written, divergent implementations of the same "build a
`PlayerMovementController`/host from a `RuntimeEntityRecord`" job. This IS
prerequisite C, confirmed still unbuilt in production code.
- **Gap 1 REFINEMENT (verified directly, not from an agent)**: "no shared Setup
mover API" is not quite right — `RuntimeSetPositionState.PrepareMover`
(`RuntimeSetPositionState.cs:1079-1133`) + `RuntimeSetPositionMoverPreparer.TryBuild`
(`RuntimeSetPositionMoverPreparation.cs:69+`) **already exist** as the shared
authored-mover-preparation API prerequisite B calls for, and
`SubmitPreparedPlacement` (`RuntimeSetPositionState.cs:2041`) already
requires `operation.Record.PhysicsBody is not { } body` to be non-null
BEFORE a placement can even be submitted — i.e. prerequisite C's body must
exist first, structurally enforced. What's genuinely missing is the HOST
input: `PrepareMover` takes a `RuntimeSetPositionMoverPreparation` whose
`Setup` field is a host-supplied `RuntimeSetPositionMoverSetup` (Setup
table ID + `FlatSetupCollision?`, `RuntimeSetPositionMoverPreparation.cs:23-42`)
— Runtime does NOT read DAT itself; the host must call
`IPreparedCollisionSource.ReadSetupCollision(setupId)` (already used by
headless at `HeadlessSessionWorldProjection.cs:668-679`) and feed the
result in. **No production call site anywhere chains
`ReadSetupCollision` -> `PrepareMover` -> `SubmitPreparedPlacement`** — both
hosts instead read Setup DAT/prepared-collision data ad hoc and call
`PhysicsEngine.Resolve`/`ResolvePlacement` directly, entirely bypassing this
already-built pipeline. This is a wiring gap, not a missing-mechanism gap.
- **Gap 3 (real, previously unstated)**: the executor publishes only generic
`RuntimeEntityChange` via `_events.PublishEntity`
(`RuntimeInitialCreateContinuationExecutor.cs:2053-2061`), NOT through
`RuntimePlacementProjectionChannel`/`Placements` — the channel the existing
App `TryApplyRuntimePlacementProjection`/headless placement sinks already
consume for OTHER routes. Whether a residence-driven initial commit
actually surfaces a `Place` token on the SAME channel other routes use is
unconfirmed/unbuilt — this bridge must be built, not assumed.
- Confirms the executor handoff's own claim: zero production callers of
`Execute`/`RegisterEntityWithInitialResidence` today.
---
## Route 2 — Local ForcePosition (from parallel research agent)
Graphical-only (headless equivalent is folded into Route 8's ForcePosition
branch, already covered above).
### Call chain
`src/AcDream.Core.Net/WorldSession.cs:1767` (wire opcode `0xF748`
UpdatePosition — same opcode for ordinary and force; "force" is purely a
`ForcePositionSequence` freshness fact) -> `WorldSession.cs:1774-1786` raises
`PositionUpdated` -> `src/AcDream.App/Net/LiveEntitySessionController.cs:67-69`
-> `src/AcDream.App/Physics/LiveEntityNetworkUpdateController.cs:1024` `OnPosition`
-> `:1035-1048` `_authorityGate.TryAcceptPosition` ->
`LiveEntityInboundAuthorityGate.cs:148-206` -> `_liveEntities.TryApplyPosition` ->
`PhysicsTimestampGate.cs:181-218` `TryAcceptPositionEvent` (retail
`SmartBox::HandleReceivedPosition` 0x00453FD0 FORCE_POSITION branch) returns
`ForcePosition` when newer AND `teleport == current teleport stamp` ->
`LiveEntityNetworkUpdateController.cs:1101-1117` `forceLocal` computed ->
**`LocalForcePositionTransaction.Apply`** (`src/AcDream.App/Physics/LocalForcePositionTransaction.cs:10-28`,
**duplicate authority**): `if (!isCurrent()) return false; blip(); acknowledge();
return isCurrent();` where `blip` = `PlayerMovementController.BlipPosition`
(`src/AcDream.Runtime/Gameplay/PlayerMovementController.cs:1759-1773`: `_body.SnapToCell`,
`UpdateCellId` at `:1577-1599` which ALSO publishes the render root via
`_physics.UpdatePlayerCurrCell`) and `acknowledge` = `LocalPlayerOutboundController.SendImmediatePosition`
(`src/AcDream.Runtime/Gameplay/LocalPlayerOutboundController.cs:152-185`,
**sends the outbound `AutonomousPosition` ack BEFORE any Runtime canonical
commit exists — the exact pre-commit-ack bug the campaign handoff names**).
Falls through unconditionally into the **generic tail**
(`LiveEntityNetworkUpdateController.cs:1252-1270`, **second duplicate
authority**): `entity.SetPosition`/`ParentCellId`/`Rotation` +
`_liveEntities.RebucketLiveEntity(...)` on the render-facing `WorldEntity`
a SECOND independent mutation of the SAME accepted Position, parallel to the
`PlayerMovementController` body mutated by the first authority (two-store
divergence, the exact bug class the executor handoff says it already fixed
for Create).
### What becomes what / gaps
- DELETE `LocalForcePositionTransaction` outright; its job (validate
ownership, blip, ack-once) becomes `RuntimeSetPositionState.Apply`/
`BeginAcceptedPlacement` with `RuntimeSetPositionOperationKind.LocalAuthoritative`.
- `OnPosition`'s force branch reduces to: classify via
`RuntimeAuthoritativePositionRouteClassifier.ClassifyAcceptedPosition` (its
`ForcePosition` branch — lines 291-313 — **already produces exactly**
`SetPositionSimple`, `PreserveHeading: true`, `SendPositionImmediately: true`
— this route kind is FULLY MODELED already, just unused) -> submit through
`RuntimeSetPositionState` -> only call `SendImmediatePosition` AFTER commit
(fixes the ordering bug).
- `BlipPosition` becomes acknowledge-only (applies the already-committed
Runtime projection instead of being an independent authority).
- DELETE the generic tail's second `entity.SetPosition`/`RebucketLiveEntity`
for the local player (1252-1270) — dead code once the host projects only
the one canonical result.
- **Gap (confirmed)**: zero production references to
`RuntimeAuthoritativePositionRouteClassifier`/`RuntimeSetPositionState`
anywhere in `src/AcDream.App` for Position handling — tracked by issue
**#275** ("post-cutover unification of the legacy Position path onto the
classifier"), cited in both the executor handoff and the divergence
register's AP-131 row.
- **Gap (confirmed)**: "reject a stale host ack" already exists structurally
(`RuntimeSetPositionState.IsPlacementCurrent`/`ConsumeAcknowledgedPlacement`,
exact-token requirement) but is unwired for Route 2.
- **Scope note**: the continuation EXECUTOR (`Execute`/FIFO drain) is scoped
to initial-Create admission, not steady-state Position on an
already-resident entity — Route 2's correct integration point is
`RuntimeSetPositionState`/`InboundPhysicsStateController.ApplyAcceptedPositionSnapshot`
directly, NOT `Execute`. The `AwaitingContinuationPlacement` retry flavors
are documented generically but demonstrated only for Create — their
applicability to a live ForcePosition is unproven, not just unwired.
- The 1,880/2,048-byte allocation budget applies directly here: ForcePosition
is genuinely frame-frequency-ish traffic.
---
## Route 3 — Portal transit and materialization (from parallel research agent)
`LocalPlayerTeleportPlacement` exists verbatim (not renamed) inside
`src/AcDream.App/Streaming/LocalPlayerTeleportController.cs:183-290`.
### Call chain
Two wire inputs join in `RuntimeWorldTransitState`: **F751 start** (`WorldSession.cs:1972-1987`
opcode `0xF751`) -> `LiveEntitySessionController.cs:83-85` `OnTeleportStarted`
-> `LocalPlayerTeleportController.cs:436-459` gates on
`LiveLocalPlayerTeleportAuthority.IsFreshStart` ->
`PhysicsTimestampGate.IsFreshTeleportStart` and
`RuntimeWorldTransitState.TryQueueTeleportStart` (`:426-442`). **Destination
Position** rides the SAME wire path as Route 2, then at
`LiveEntityNetworkUpdateController.cs:1906-1913` (guarded on `disposition==Apply
&& guid==playerServerGuid`) calls `_localPlayerTeleport.OfferDestination(...)`
-> `RuntimeTeleportDestinationAdapter.cs:15-36` (pure mapping) ->
`RuntimeWorldTransitState.OfferTeleportDestination` (`:480-520`). Every frame,
`LocalPlayerTeleportController.Tick` (`:472-570`) drives:
`TryAimAcceptedDestination`/`AimDestination` (`:626-734`, classifies via
`TeleportLandblockTransition.Classify` — streaming-only, no world mutation) ->
`WorldRevealCoordinator.TryBeginPortal` -> `RuntimeWorldTransitState.TryBeginPortalReveal`
(mints reveal generation + host projection token). On `TeleportAnimEvent.Place`:
preflight `RuntimeWorldTransitState.CanPlacePortalDestination` (read-only gate)
-> **`LocalPlayerTeleportPlacement.Place`** (`LocalPlayerTeleportController.cs:214-278`,
**the duplicate authority**) -> THEN `WorldRevealCoordinator.ObserveMaterialized`
-> `RuntimeWorldTransitState.AcknowledgePortalMaterialized` runs AFTER `Place`
has already fully mutated world state — **a rubber-stamp, not a gate**. On
`TeleportAnimEvent.FireLoginComplete`: `_session.SendLoginComplete()` (outbound
ack) -> `RuntimeWorldTransitState.Complete`.
`LocalPlayerTeleportPlacement.Place` (full method, 214-278) mutates in order:
`_physics.Resolve(...)` (own collision placement) -> `controller.SetPosition(...)`
(body+cell) -> `entity.SetPosition`/`ParentCellId`/`Rotation` on `WorldEntity`
-> `_liveEntities.RebucketLiveEntity(...)` (throws on failure) ->
`_host.Host?.NotifyTeleported()` (retail `teleport_hook` tail) ->
`controller.SetBodyOrientation(rotation)` -> camera reset
(`_cameras.Legacy?.Update`/`_cameras.Retail?.ResetViewerToPlayer`, **unique to
this route vs. Route 2**) -> `_spatial.Reconcile()`.
`TeleportViewPlaneController` (view-plane/FOV easing) is presentation-only,
NOT in scope for deletion.
### What becomes what / gaps
- DELETE `Place`'s resolve/controller/world-entity/rebucket/host-notify/
orientation mutations outright; replace with
`RuntimeSetPositionState.BeginAuthoredPlacement`/`Apply` carrying a
populated `RuntimePortalPlacementAuthority`, then project only the
committed result.
- Re-sequence `WorldRevealCoordinator.CanPlacePortalDestination`/
`ObserveMaterialized` to be the ACTUAL gate/confirmation around the
canonical commit (not rubber-stamps around a host-owned mutation) — i.e.
materialization fires from the Runtime commit callback, not immediately
after `Place()`'s direct mutations.
- Camera reset and `_spatial.Reconcile()` become acknowledge-only reactions.
- Headless has the analogous duplicate authority in
`HeadlessSessionWorldProjection.PrepareDestination`/`SynchronizeLocalPlayer`
(already documented in the Route 8 section above) — must cut over in
lockstep, same target (`RuntimeSetPositionState`).
- **Gap (confirmed, important)**: `RuntimeWorldTransitState` (1012 lines,
Slice J6) already fully owns reveal generation/sequence/destination-cell,
readiness, materialization/completion/cancellation, and the 4-stage host
acknowledgement protocol (`ProjectionRegistered -> SimulationReleaseProjected
-> DestinationReservationReleased -> TerminalProjected`) — but ONLY for
streaming/render-resource bookkeeping, NEVER for gating an actual placement
mutation. `RuntimePortalPlacementAuthority` (`RuntimeSetPositionState.cs:64-78`,
carrying `RevealGeneration`/`TeleportSequence`/`RuntimeWorldHostProjectionToken`)
is a REAL, VALIDATED constructor parameter on `RuntimeSetPositionCommand.Portal`
(validated in `BeginAcceptedPlacementCore`, `:978-984`) and
`LiveEntityRuntime.cs:1162-1171` even has an `IsValidPortalPlacementAuthority`
presentation-side validator — **but grep across App and Headless finds ZERO
call sites that ever construct one with `Present: true`.** The binding
machinery is real but 100% dormant end to end.
- **Missing for Route 3 specifically**: (a) the adapter that reads
`WorldRevealCoordinator`'s current generation/sequence/destination-cell/host
projection token into a `RuntimePortalPlacementAuthority`; (b) the actual
`RuntimeSetPositionState.BeginAuthoredPlacement` call site using
`LocalAuthoritative` + that authority (does not exist anywhere today); (c)
re-sequencing `ObserveMaterialized` to run from the commit callback.
- **Discard/cancellation semantics** (a stale generation/sequence/cell/token
must never reveal) look structurally sufficient
(`RuntimePlacementProjectionKind.Discard`, `IsPlacementCurrent`,
`RuntimeWorldTransitState.Cancel`/`BeginHostProjectionSupersession`) but are
UNEXERCISED together for a real placement — treat as an explicit cutover
test, not an assumed-correct combination.
- Executor scope note: same as Route 2 — the FIFO/`Execute` mechanism is
Create-admission-scoped; Route 3's target is `RuntimeSetPositionState`
directly.
---
## Route 4 — Remote CreateObject and Position (from parallel research agent)
### Foundational fact (independently confirmed by this agent AND by direct
grep during this research pass)
`InboundPhysicsStateController.cs:596-608`'s own doc comment states verbatim:
"this file's `TryApplyPosition` is today's **only PRODUCTION Position wire
caller**; the classifier-based path is **test-only** until a host wires the
executor." `RuntimeEntityObjectLifetime.TryApplyPosition` only enters the
classifier/executor path when `TryGetPendingInitialResidence` finds an
in-flight CreateObject continuation (a narrow transient window) — steady-state
remote UpdatePosition always falls to the legacy unconditional-merge path
with **no route-classification concept at all**.
### Call chain
`LiveEntitySessionController.cs:67-69` -> `LiveEntityNetworkUpdateController.OnPosition`
(`:1035`) -> `LiveEntityInboundAuthorityGate.cs:161` `TryApplyPosition` ->
`LiveEntityRuntime.cs:2116,2135` -> `RuntimeEntityObjectLifetime.cs:1229,1299`
steady-state branch -> `RuntimeEntityDirectory.cs:478` ->
`InboundPhysicsStateController.cs:610` (legacy unconditional timestamp-gated
merge). Then `LiveEntityNetworkUpdateController.cs:1197-1211` computes
`remoteHardTeleport`/`remotePlacementRequired` from `timestamps.TeleportHookRequired`
and `_remoteTeleportController.HasPending`; `RunRemoteTeleportHook` (`:1210`)
-> `RemoteTeleportHook.Execute` (`RemoteTeleportHook.cs:17`, CancelMoveTo/
UnStick/StopInterpolating/UnConstrain/NotifyTeleported/ReportCollisionEnd).
**Then, UNCONDITIONALLY** (`LiveEntityNetworkUpdateController.cs:1252-1270`,
**duplicate authority**, BEFORE any placement/teleport branch runs):
`entity.SetPosition`/`ParentCellId`/`Rotation` + `RebucketLiveEntity`. If
`remotePlacementRequired`: `RemoteTeleportController.BeginPlacement` (`:269`)
-> `TryApply` (`:117/151`) -> `Resolve` (`:278`) -> `_physics.ResolvePlacement`
(`:303`) -> `CommitResolved` (`:320`) -> `RemoteTeleportPlacement.Apply`
(`RemoteTeleportPlacement.cs:15`) -> `PhysicsObjUpdate.CommitSetPositionTransition`.
Else (ordinary MoveOrTeleport): **inline classification hand-duplicated in
App**, `LiveEntityNetworkUpdateController.cs:1594-1900` (local constants
`MaxPhysicsDistance=96f`/`BodySnapThreshold=4f` duplicated for player vs NPC)
decides far-snap/near-interpolate/airborne-noop/landing, then
`entity.SetPosition`/`ParentCellId`/`Rotation` + `LiveEntityShadowPublisher.TryPublishRemote`.
Ongoing per-tick DR: `RemotePhysicsUpdater.cs:220` ->
`RuntimeRemotePhysicsUpdater.Tick` (`:61`) — interp catch-up + `ResolveWithTransition`
sweep + shadow sync, independent of SetPosition (retail's continuous
`UpdatePositionInternal` — NOT itself a wire route, out of cutover scope).
### Duplicate authorities
- `LiveEntityNetworkUpdateController.cs:1252-1270` — position/cell/rotation
AND full rebucket, unconditionally, ahead of any classification.
- `RemoteTeleportController.cs:37,77-109``_pending` dictionary, rollback
capture/restore (`:94-109,477-569`), lost-cell tracking
(`:529-532`) — exactly the bookkeeping the residence+executor's
25-second lost-cell lifetime already owns generically.
- `RemoteTeleportPlacement.cs:43-77` — direct `body.SnapToCell`,
contact-plane fields, `PhysicsObjUpdate.CommitSetPositionTransition` — a
hand-rolled SetPosition commit parallel to `RuntimeSetPositionState`.
- `LiveEntityNetworkUpdateController.cs:1594-1900` — the "ordinary"
MoveOrTeleport classification hand-duplicated in App, when
`RuntimeAuthoritativePositionRouteClassifier.ClassifyAcceptedPosition`
ALREADY implements identical near(<96m,Interpolate)/far(SetPositionSimple)/
teleport-advanced(SetPosition)/cellless rules (classifier lines 360-439).
- `RemoteTeleportController.cs:382-475` `ParkPending`/`OnProjectionVisibilityChanged`
re-implements deferred-until-visible placement replay, another thing the
executor's FIFO/replay machinery already generalizes.
### What becomes what
- DELETE outright: `RemoteTeleportController.cs`, `RemoteTeleportPlacement.cs`,
their pending dictionary/rollback/`ParkPending`, and the pre-placement
`entity.SetPosition`/`RebucketLiveEntity` at `:1252-1270`.
- DELETE + replace with classifier delegation: the inline near/far/airborne/
landing block (`:1594-1900`) -> route through
`ClassifyAcceptedPosition` + `RuntimeSetPositionState` Begin/Watch/resume.
- Reduce to acknowledge-only: `RemoteTeleportHook.cs` stays as the App-side
execution seam, but its trigger condition should come from the classified
route's `RuntimeTeleportHookPhase`, not the App's own `remoteHardTeleport`
flag.
- `RuntimeRemotePhysicsUpdater` per-tick DR is OUT of cutover scope (retail's
continuous simulation, not a wire route).
### Gap
The classifier's `Remote` branch is FULLY implemented for both `ClassifyCreate`
and `ClassifyAcceptedPosition` — the only gap is wiring (no production caller
ever constructs a `RuntimeAcceptedPositionRouteRequest` for a Remote entity
outside the narrow pending-residence window). The 1,880 B/op budget applies
directly here too (remote Position is genuinely per-network-tick frequency).
---
## Route 5 — Projectile authoritative create/corrections (from parallel research agent)
### Call chain
**Create**: `DatLiveEntityProjectionMaterializer.cs:807` `_projectiles.TryBind` ->
`ProjectileController.TryBind` (`ProjectileController.cs:133`) — constructs/
adopts the shared `PhysicsBody` (`:176-265`), calls `body.SnapToCell` directly
(`:214/256`), then `entity.SetPosition`/`ParentCellId` + `RebucketLiveEntity`
(`:268-271`) and `ShadowPositionSynchronizer.Sync` (`:306`) — all ad hoc, no
classifier. **Vector** (launch velocity): `LiveEntityNetworkUpdateController.cs:840`
`OnVector` -> `ProjectileController.ApplyAuthoritativeVector` (`:345`) ->
`RuntimeProjectilePhysicsUpdater.ApplyAuthoritativeVector` (`:207`). **State**
(Missile bit): `:992` `OnState` -> `ApplyAuthoritativeState` (`:410`) ->
`RuntimeProjectilePhysicsUpdater.ApplyAuthoritativeState` (`:252`, direct
`SnapToCell` at `:288`). **Authoritative Position correction**:
`LiveEntityNetworkUpdateController.cs:1217-1233` (inside `OnPosition`, AHEAD of
the generic remote path — returns early if handled) ->
`ProjectileController.ApplyAuthoritativePosition` (`:508`) ->
`RuntimeProjectilePhysicsUpdater.ApplyAuthoritativePosition` (`:301`, direct
`body.SnapToCell` at `:346`, `CommitProjectileCell` at `:370`, direct
`ShadowPositionSynchronizer.Sync`/`Suspend` `:402-421`). **Ordinary per-quantum
integration**: `ProjectileController.Tick` (`:613`) -> `AdvanceQuantum`/
`TryBeginQuantum`/`CompleteQuantum` (`:761-836`) ->
`RuntimeProjectilePhysicsUpdater.TryBegin`/`Complete` (`:37,94`) — `Complete`
does direct `body.SnapToCell` at line 145 + direct shadow sync — correctly
NOT routed through SetPosition today, but ALSO not through any shared
cell-commit authority; its own ad hoc `CommitProjectileCell` + shadow write.
### Duplicate authorities
- `ProjectileController.cs:214-271` — body construction, `SnapToCell`,
`entity.SetPosition`/`ParentCellId`, `RebucketLiveEntity`, shadow sync — full
ad hoc placement authority for missile creation.
- `RuntimeProjectilePhysicsUpdater.cs:145-198` (per-quantum `Complete`) and
`:346-421` (`ApplyAuthoritativePosition`) — BOTH do direct `SnapToCell` +
`CommitProjectileCell` + shadow sync, bypassing `RuntimeSetPositionState`
entirely; **neither distinguishes "ordinary integration" from "authoritative
correction" at the cell/shadow-commit layer** — only the caller's semantic
label differs.
### What becomes what
- DELETE outright: the authoritative-placement code in `TryBind`'s create
branch (`:214-271`) and `ApplyAuthoritativePosition` (`:508-578`); and the
direct `SnapToCell`/shadow commit inside
`RuntimeProjectilePhysicsUpdater.ApplyAuthoritativePosition` (`:301-424`).
- REPLACE with `ProjectileAuthoritative`-tagged routes via the classifier
driving `RuntimeSetPositionState`, using "the same Runtime body and exact
projectile Setup sphere" — i.e. `RuntimeProjectile.Body`/`CollisionSphere`
(`RuntimeProjectile.cs:10-40`) plugged into the shared SetPosition
transaction instead of `CommitProjectileCell`.
- KEEP UNCHANGED (must NOT route through SetPosition): per-quantum
integration (`TryBegin`/`Complete`, `:37-205`) — correctly outside
SetPosition today; only its cell/shadow-commit CALL is a candidate to unify
onto a shared (non-SetPosition) cell-commit primitive.
- Reduce to acknowledge-only: `ApplyAuthoritativeVector`/`ApplyAuthoritativeState`
(`:332-478`) — smaller scope of change since they don't do placement/rebucket
themselves today.
### Gap
`ProjectileAuthoritative` exists TODAY ONLY as an `OperationKind` label
(`RuntimeSetPositionState.cs:16`), produced generically by the classifier's
`OperationKind(...)` helper for both entry points — **it is not a distinct
routing rule**: `ClassifyCreate` gives Projectile the exact same
`SetPosition`/`InitialCreateFlags` route as any non-local entity, and
`ClassifyAcceptedPosition` gives Projectile the IDENTICAL near/far/teleport/
cellless branches as Remote (proven by the test name
`ProjectilePosition_UsesRemoteMoveOrTeleportClassification`,
`RuntimeAuthoritativePositionRouteClassifierTests.cs:418-436`). So: PARTIAL —
operation-kind plumbing + generic MoveOrTeleport-shape routing exist; missing
is (a) any production wiring at all (same dormancy as route 4), and (b)
projectile-specific POLICY (should a "near" wire correction ever return
`Interpolate` for a ballistic body, or always hard-correct?) — today's
"never route per-quantum integration through SetPosition" constraint holds
only because per-quantum integration never calls the classifier at all (a
separate simulation-tick path), not because of an explicit guard.
---
## Route 6 — Drops and unparent-to-world (from parallel research agent)
Graphical-only; no headless drop UI exists.
### Call chain
**Whole-item/normal drop**: `ItemInteractionController.ExecutePlacementActions`,
`DropToWorld` case (`src/AcDream.App/UI/ItemInteractionController.cs:976-993`) —
optimistic move + send drop; ordinary server CreateObject flows through the
normal wire pump: `LiveEntityHydrationController.OnCreate`/`OnCreateCore`
(`:226-299`) -> `_runtime.RegisterLiveEntity(spawn)` -> `LiveEntityRuntime.cs:526-556`
-> `_entityObjects.RegisterEntity(...)` (line 537) -> `RuntimeEntityObjectLifetime.RegisterEntity`
(315-322, legacy, `beginInitialResidence:false`). **Split-to-world** (ACE omits
CreateObject, sends only the new GUID's Position first):
`ItemInteractionController.cs:994-1015` (`SplitToWorld` case, raises
`WorldDropDispatched`) -> `InventoryWorldDropProjectionController.OnWorldDropDispatched`
(`:81-98`, records `PendingSplitToWorldProjection`) -> wire Position (F748) for
the new GUID arrives at `LiveEntityNetworkUpdateController.OnPosition:1024-1029`,
FIRST calls `_worldDropProjection.TryRecoverUnknownPosition(update)` (1026) ->
`InventoryWorldDropProjectionController.cs:54-68`: builds a synthetic
`EntitySpawn` via `PendingSplitToWorldProjection.BuildSpawn` (171-209, clones
the SOURCE item's full spawn/PhysicsSpawnData, overriding
Guid/Position/StackSize/Container/Wielder/*Sequence/Parent*) -> calls
`_hydration.OnCreate(spawn)` (66) — **the IDENTICAL entry point as the
ordinary path**, converging on the same `RegisterEntity` call.
`ShortcutDropPlanner` is unrelated to this route (toolbar-slot reshuffling
only, never touches position/physics).
### Duplicate authority / gap
Neither drop flavor calls `Physics.SetPosition.Begin*` or
`RuntimeInitialCreateResidenceState.Begin` today — **there is no App-vs-Runtime
competing mutation for drops specifically**; the gap is identical to routes
1-5: `RegisterEntityWithInitialResidence` is never called from any production
path (confirmed by full-repo grep — only self-referential inside
`RuntimeEntityObjectLifetime`'s own constructors, as the executor's
child-replay delegate, plus tests). Both drop flavors equally bypass the
canonical create-placement transaction — this is a wiring gap shared with
routes 1-5, not a route-6-specific duplicate.
- **Real, previously-unstated Route-6-specific gap**: `PendingSplitToWorldProjection.BuildSpawn`
clones the source's DTO wholesale (correct for Position, which IS
explicitly overridden) but presentation-effect replay (create-pop VFX/sound
driven off `LiveEntityReadyPublisher.Publish`/`EntityEffectController.ReplayPendingForLiveEntity`)
has NO SIGNAL today distinguishing "real spawn" from "split continuation" —
and the executor's receipt/trace has no field for "this create is a
split-recovery continuation, suppress create-time effect replay." This must
be ADDED, not just wired — a genuine new capability, not merely dormant.
### What becomes what
`LiveEntityHydrationController.OnCreate`/`OnCreateCore` switches to
`RegisterEntityWithInitialResidence` for EVERY CreateObject (both flavors) so
both acquire a lease and drain through `Execute`.
`TryRecoverUnknownPosition`'s call shape doesn't need to change, but the
residence/executor layer needs the new "suppress effect replay for split
continuation" capability above. `WorldDropDispatch`/`ShortcutDropPlanner`/
`ItemInteractionController` dispatch (pure UI/wire trigger) is unaffected.
---
## Route 7 — Pickup, Parent, and Delete (from parallel research agent)
### Confirms/refutes "Runtime hooks already exist"
**Largely CONFIRMED at the App/headless-caller level**: none of
`LiveEntityHydrationController`, `EquippedChildRenderController`,
`LiveEntityDeletionController`, `LiveEntityRuntimeTeardownController` do their
own `SetFullCell`/`SuspendObjectClock`/rebucket math — all of it lives inside
`RuntimeEntityObjectLifetime` (`TryApplyPickup:805-868`,
`TryApplyParent/TryApplyCreateParent` -> shared helper
`CommitPositionChannelUpdate:1705-1737`, `TryCommitParent:944-974`,
`CommitAcceptedParentCellless:976-1007`, `CommitWithdrawal:1401-1422`,
`TryAcceptDelete:1466-1527`). App/headless work is exclusively presentation
(render subtree, shadows/effects/animation/selection teardown) — legitimate,
not duplicated physics authority.
**BUT the claim needs an important qualification (previously-unstated
finding)**: every `ForgetInitialCreateResidence(...)` call in this family
(849, 1720-1721, 990, 1411, 1501) cancels a residence lease that is **NEVER
ACQUIRED** anywhere in production (same Route-1/6 finding —
`RegisterEntityWithInitialResidence` unreached), and every paired
`Physics.SetPosition.Forget(...)` cancels a placement transaction that is
likewise **NEVER BEGUN** anywhere in production
(`BeginAcceptedPlacement`/`BeginAuthoredPlacement`,
`RuntimeSetPositionState.cs:803,815`, zero external callers repo-wide). **So
today these cancellation calls are structurally present but functionally
no-ops against always-empty state** — `InitialCreateResidences.Forget`
returns false; `PreferCancellation` always falls through to the equally-empty
"ordinary" receipt. Route 7 is correctly "already cut over" for the parts
that currently do anything (SetFullCell/SuspendObjectClock/RefreshSnapshot/
AdvanceXAuthority/CollisionReports/EndChildProjection are genuinely
Runtime-owned) — but the specific behavior the campaign doc worries about
("cancel the exact active placement/lost-cell family first") is INERT
because there is nothing upstream yet to cancel. Once routes 1-5 wire the
create/position path live, these existing calls become live with NO code
change required — genuinely pre-built and correctly sequenced already.
### Two internal asymmetries flagged for cutover review (new findings)
- `TryCommitParent` (944-974) is the ONLY method in the family with NO
`ForgetInitialCreateResidence`/`PreferCancellation` call at all.
- `CommitWithdrawal` (1401-1422) calls `ForgetInitialCreateResidence` but NOT
`Physics.SetPosition.Forget`/`PreferCancellation`, unlike Pickup/
`CommitAcceptedParentCellless`/`TryAcceptDelete` which cancel both families.
Once ordinary placement transactions go live,
`WithdrawLiveEntityProjectionToCellless` (`LiveEntityRuntime.cs:1315-1331`,
calls `CommitWithdrawal`) could leave an active mover/lost-cell watch
dangling. **Flag for explicit cutover test, do not assume symmetric with
the other four methods.**
### Headless-specific gap (real, pre-existing, adjacent to but distinct from the cutover)
`RuntimeLiveEntitySessionController.OnParentUpdated` (216-220) calls ONLY
`Entities.TryApplyParent` directly — **headless NEVER calls
`TryCommitParent`/`CommitAcceptedParentCellless`** — there is no headless
equivalent of the App parent-realize sequence
(`EquippedChildRenderController.ResolveAndTryRealize`/`PrepareAndTryRealize`,
`LiveEntityHydrationController.cs:470` chain) AT ALL. This looks like a live
gap independent of the residence/executor cutover — a headless parented
child's `FullCellId` may never clear. Needs new code, not just wiring.
`HeadlessSessionWorldProjection`/`IRuntimeDirectWorldProjection` has no
pickup/parent/delete hook at all (only `ProjectSpawn`/`ProjectPosition`/
`BeginTeleport`/`PrepareDestination`) — headless has ZERO duplicate
placement work for route 7 (consistent with "already cut over" for the parts
that do anything).
### Gap vs. the executor
"What the executor owns" already anticipates Route 7's "pickup during
preparation/deferred residence" and "parent during pending withdrawal" test
scenarios via the `TryGetPendingInitialResidence`/`EnqueueDormant` branches
already present in `TryApplyPickup`/`TryApplyParent` — no NEW executor
capability is needed for route 7's steady-state (post-create) behavior; the
gap is entirely "wire up creates to use residence" (routes 1-3's job). One
real gap: the executor's receipt/trace has no concept of "a pickup/delete
cancelled my pending residence" — `RuntimePlacementCancellationReceipt`
contents route only to `Physics.SetPosition.PublishCancellation`, never
surfaced to the calling host for observability/testing.
---
## Route 8 — Headless parity (researched directly, not delegated)
Files read in full: `src/AcDream.Headless/Hosting/HeadlessSessionWorldProjection.cs`
(693 lines), `src/AcDream.Runtime/Session/RuntimeLiveEntitySessionController.cs`
(349 lines).
### Current production call chain — headless host
Entry: `RuntimeLiveEntitySessionController.CreateSink()` (56-68) wires a
`LiveEntitySessionSink` whose delegates are the ONLY headless wire-dispatch
entry points for entity/position/motion/vector/state/parent/teleport traffic.
Each handler below calls straight into `RuntimeEntityObjectLifetime` (the
LEGACY, non-residence overloads — every call passes `acknowledgeProjection: null`)
and then, only for specific cases, into `IRuntimeDirectWorldProjection`
(implemented by `HeadlessSessionWorldProjection`):
1. `OnSpawned` (73-95) — `Entities.RegisterEntity(spawn)` (75-76, **the
legacy Create entry point — confirmed by the surface map's grep as one of
only two production Create call sites in the repo**) → if `registration.Canonical`
present, `Entities.ApplyAcceptedSpawn(canonical, integrationVersion,
canonical.Snapshot, replaceGeneration: ...)` (81-87) → if applied,
`_worldProjection?.ProjectSpawn(canonical, isLocalPlayer)` (90-93) →
`HeadlessSessionWorldProjection.ProjectSpawn` (507-513): **no-op for any
non-local entity** (`if (isLocalPlayer) SynchronizeLocalPlayer(record);`
line 511-512 IS the entire method body); for the local player, calls
`SynchronizeLocalPlayer` (566-615) which is the duplicate placement
authority (see below).
2. `OnPositionUpdated` (137-201) — `Entities.TryApplyPosition(update, isLocal,
forcePositionRotation: localController?.BodyOrientation,
currentLocalVelocity: localController?.BodyVelocity,
projectionRequiresTeleportHook: false, acknowledgeProjection: null, ...)`
(144-153, legacy path) → if `!isLocal` or `Rejected`, return (154-159) →
if `disposition is Apply`, builds a `RuntimeTeleportDestination` from the
wire position and calls `_runtime.TransitOwner.OfferTeleportDestination(destination,
timestamps.TeleportAdvanced)` (161-184, **every accepted local Position is
offered to the transit owner as a POTENTIAL portal destination — the
transit owner itself decides whether it's actually a portal**) → looks up
the active record and calls `_worldProjection?.ProjectPosition(record,
isLocalPlayer: true, disposition)` (185-193) → `HeadlessSessionWorldProjection.ProjectPosition`
(515-531): no-op for non-local; if no controller yet, `SynchronizeLocalPlayer`
(525); **else only for `disposition is ForcePosition` does it call
`BlipLocalPlayer`** (529-530) — an ordinary `Apply` disposition with an
existing controller does NOTHING here (ordinary interpolation must happen
elsewhere, at the physics tick) → back in the session controller, if
`disposition is ForcePosition`, `_localPlayerOutbound.SendImmediatePosition(_session,
_runtime.MovementOwner.Controller)` (194-199, **sends the outbound wire ack
BEFORE any Runtime placement commit/host-acknowledgement — the headless
twin of route 2's "direct BlipPosition/pre-commit acknowledgement"**) →
`TryCompletePortal()` (200, 243-332) unconditionally at the end.
3. `OnPickedUp`/`OnMotionUpdated`/`OnVectorUpdated`/`OnStateUpdated`/`OnParentUpdated`/`OnAppearanceUpdated`
(118-122, 124-135, 203-207, 209-214, 216-220, 237-241) — thin 1-line
pass-throughs to `Entities.TryApply*(..., acknowledgeProjection: null, out _)`,
no independent headless placement work.
4. `OnDeleted` (97-116) — `Entities.TryAcceptDelete(...)` →
`Entities.CompleteAcceptedDelete(acceptance)` → `Entities.RetireCanonicalOnly(retired)`.
No independent headless placement work (matches route 7's claim that
pickup/parent/delete are already mostly Runtime-owned — see route 7's
section for the App-side confirmation).
5. `OnTeleportStarted` (222-235) → `transit.TryQueueTeleportStart` →
`_worldProjection?.BeginTeleport()` → `HeadlessSessionWorldProjection.BeginTeleport`
(533-537): sets `controller.State = PlayerState.PortalSpace` directly (no
Runtime commit gate) → `transit.ActivateQueuedTeleport()` → `TryCompletePortal()`.
6. `TryCompletePortal` (243-332) — a **fully Runtime-driven, already-cutover
state machine** for the portal handshake itself: `TryGetAcceptedTeleportDestination`
→ `TryBeginPortalReveal` → `TryRegisterHostProjection` → `Acknowledge(...ProjectionRegistered)`
→ `_worldProjection?.PrepareDestination(generation, destination)` (271-274,
**the ONE point where the headless host does its own destination-prep
work** — see below) → `transit.AcknowledgeDestinationReadiness(readiness)`
→ `transit.AcknowledgePortalMaterialized(...)` →
`Acknowledge(...SimulationReleaseProjected)` →
`transit.RequireDestinationReservationRelease(projection)` →
`Acknowledge(...DestinationReservationReleased)` →
`transit.AcknowledgeWorldViewportVisible` + `transit.Complete(generation)` →
`Acknowledge(...TerminalProjected)` → `_session.SendGameAction(GameActionLoginComplete.Build())`
→ `transit.EndTeleport()`. **This entire sequence is already Runtime-owned
generation/token bookkeeping (J6.2-J6.4 landed) — it is NOT a route-8
duplicate authority; it's the shared portal-lifecycle plumbing routes 3
and 8 both sit on top of.**
### Duplicate authorities — headless (what the residence+executor owner must take over)
- **`HeadlessSessionWorldProjection.SynchronizeLocalPlayer`** (566-615, read
in full): `_collision.CenterOn(position.LandblockId)` (575) →
`CreateController(record)` if none exists (577-578, 639-655 — constructs
`PlayerMovementController` directly, see prerequisite-C section above) →
`_runtime.EntityObjects.Physics.Engine.Resolve(wirePosition, position.LandblockId,
Vector3.Zero, 100f)` (589-594) → `.ResolvePlacement(resolved.Position,
resolved.CellId, DefaultRadius, DefaultHeight, controller.StepUpHeight,
controller.StepDownHeight, ObjectInfoState.IsPlayer | EdgeSlide,
record.LocalEntityId ?? 0u)` (595-605) → `controller.SetPosition(resolved.Position,
resolved.CellId, wirePosition)` + `controller.SetBodyOrientation(orientation)`
(610-614). **This is a COMPLETE independent physics resolve + placement
commit, entirely bypassing `RuntimeSetPositionState`.** It is the headless
mirror of route-1's "Headless performs its own initial resolve/placement/body
construction" callout in the campaign handoff.
- **`HeadlessSessionWorldProjection.BlipLocalPlayer`** (617-637): direct
`controller.BlipPosition(wirePosition, position.LandblockId, wirePosition)`
(633-636) with zero Runtime commit/ack gating — this is exactly the
"direct Blip" duplicate the campaign handoff names for route 8 ("Delete the
independent resolve/placement/direct SetPosition and Blip logic in
`HeadlessSessionWorldProjection`").
session controller's `_localPlayerOutbound.SendImmediatePosition(...)` at
`RuntimeLiveEntitySessionController.cs:196-198` fires on the SAME
`ForcePosition` branch, before any placement receipt exists — pre-commit ack.
- **`HeadlessSessionWorldProjection.PrepareDestination`** (539-564): calls
`_collision.CenterOn(destination.CellId)` (543) then, if the destination
entity is active, `SynchronizeLocalPlayer(record)` again (544-549) — i.e.
portal arrival re-runs the SAME duplicate resolve/placement authority.
Also directly sets `controller.State = PlayerState.InWorld` (550-551)
outside any Runtime placement gate.
- **`HeadlessCollisionNeighborhood`** (168-469): NOT a placement-authority
duplicate per se (it manages per-landblock collision generation
publication/retirement, a different concern from entity placement) but it
IS the thing `RuntimeSetPositionState`/`RuntimeInitialCreateResidenceState`
must be able to wait on (its `IsReady`/`CenterOn` gate the destination
readiness the residence lease's placement depends on) — this file already
looks like a reasonable candidate for the "exact authored mover preparation"
(prerequisite B) collision-readiness signal, not something to delete.
### What each headless hop must become post-cutover
- `OnSpawned` → keep the legacy timestamp-gate call semantics but swap
`Entities.RegisterEntity(spawn)` for `Entities.RegisterEntityWithInitialResidence(spawn, ...)`
(the wrapper already exists per the surface map, just unused) so headless
enters the SAME residence lease as graphical; `ProjectSpawn` becomes
acknowledge-only: subscribe an `IRuntimePlacementObserver` (does not exist
yet — see observer-seam section) and, on a `Place` receipt for this
entity's token, set `controller.SetPosition`/`SetBodyOrientation` from the
IMMUTABLE `RuntimePlacementProjectionSnapshot` fields only. DELETE
`SynchronizeLocalPlayer`'s direct `Physics.Engine.Resolve`/`ResolvePlacement`
calls (589-605) outright — the sweep must happen exactly once, inside
`RuntimeSetPositionState`, driven by the residence lease.
- `OnPositionUpdated`'s ForcePosition branch → DELETE the direct
`BlipLocalPlayer`/`controller.BlipPosition` call (633-636) and the
immediate `SendImmediatePosition` pre-commit ack (196-198 in the session
controller); replace with: accept timestamp only (still call
`TryApplyPosition`/`TryAcceptDeferredPosition` for the timestamp-gate
side-effects already established) → begin the Runtime placement (route 2's
"LocalAuthoritative" route kind) → wait for the `Place` receipt via the new
headless `IRuntimePlacementObserver` → apply position from the receipt →
THEN send the single outbound ack.
- `PrepareDestination` → keep the collision-neighborhood prep (`_collision.CenterOn`/`IsReady`)
as the readiness signal Runtime's portal-placement authority polls, but
DELETE the second `SynchronizeLocalPlayer` call (548) — arrival placement
must come from the SAME Runtime portal-placement commit route 3 defines,
not a second independent resolve.
- `CreateController` → becomes the headless half of prerequisite C's atomic
controller/body publication transaction; must stop being callable
ad hoc from three different places (`SynchronizeLocalPlayer` twice via
`OnSpawned`+`PrepareDestination`, indirectly via `OnPositionUpdated`) and
instead run exactly once, gated by the same atomic commit graphical
route 1 uses.
### Route-8-specific capability gap
The residence+executor mechanism (per
`docs/research/2026-08-02-runtime-continuation-executor-handoff.md`,
"What the executor owns") is host-agnostic by design — nothing in its
API is graphical-only. The concrete gap for route 8 is therefore NOT in the
executor; it is that **no headless `IRuntimePlacementObserver` implementation
exists to consume `GameRuntime.Placements` receipts at all** (see
observer-seam section above — zero hits in `src/AcDream.Headless/`), so
today headless has no mechanism to learn "the Runtime commit landed, project
it" even in principle. Headless also has no analog to a "camera" but DOES
have the same controller/body atomicity requirement as graphical
(prerequisite C applies equally to both hosts — confirmed by the very
existence of `HeadlessSessionWorldProjection.CreateController` as a second,
independent constructor of `PlayerMovementController` next to
`PlayerModeController.BuildControllerAndCamera`).
---
(routes 1-7 sections below filled in as each parallel research agent reports)