diff --git a/docs/plans/2026-08-02-placement-cutover.md b/docs/plans/2026-08-02-placement-cutover.md new file mode 100644 index 00000000..6ececdbe --- /dev/null +++ b/docs/plans/2026-08-02-placement-cutover.md @@ -0,0 +1,89 @@ +# Placement production cutover — campaign plan (2026-08-02) + +The final leg of the remaining physics-divergence campaign before AP-22 and +AD-10: route graphical AND headless production placement through the +residence + continuation-executor owner (`38fd4b8d` / `30012361` / +`5db3de3c`), delete the legacy duplicate authorities, and retire AP-1/AD-1 +behind connected + user-visual gates. + +**Inputs (read in order):** +1. [`2026-08-02-runtime-continuation-executor-handoff.md`](../research/2026-08-02-runtime-continuation-executor-handoff.md) + — the completed dormant mechanism and its cutover notes. +2. [`2026-08-02-cutover-route-inventory.md`](../research/2026-08-02-cutover-route-inventory.md) + — the full 8-route, both-host call-chain inventory with exact file:line + for every duplicate authority to remove. THE map for all slices below. +3. [`2026-07-31-remaining-physics-campaign-handoff.md`](../research/2026-07-31-remaining-physics-campaign-handoff.md) + — the original per-route requirements and prerequisite definitions. + +**Standing discipline per slice:** pinned contract → single implementer → +independent retail-conformance + architecture/adversarial reviews (both must +PASS on the final diff) → focused + complete Runtime + Release build + +complete solution gates → bisectable behavior commit (register rows in the +same commit) → docs/handoff commit. No workarounds; no fused slices. + +## Confirmed pre-cutover gaps (from the inventory) + +- The executor publishes only generic entity deltas; nothing bridges its + completion to `RuntimePlacementProjectionChannel`, so no host can learn + "my initial placement committed" through the built observer seam. +- No atomic controller/body publication owner exists (prerequisite C); + App and headless hand-write divergent `PlayerMovementController` + construction, and `SubmitPreparedPlacement` requires a canonical + `PhysicsBody` that nothing currently publishes atomically. +- The dormant placement path's 1,880 B/operation (2,048 cap) allocation + remains the activation blocker for frame-frequency routes. +- `Execute`'s live inputs (`UsePositionFromServer`, `PlayerDistance`) are + computed by no host; they must derive from Runtime's own character-option + and local-player owners. +- `RuntimePortalPlacementAuthority` has zero producing call sites; the + adapter from `RuntimeWorldTransitState` does not exist. +- The exact-Setup mover chain (`PrepareMover` / + `RuntimeSetPositionMoverPreparer.TryBuild` / + `IPreparedCollisionSource.ReadSetupCollision`) exists piecewise, unwired. +- Route-6 split-recovery creates need an effect-replay suppression signal; + route-7 needs `TryCommitParent`/`CommitWithdrawal` cancellation-symmetry + fixes and host-visible cancellation receipts; headless lacks any + parent-realize sequence (pre-existing, adjacent). + +## Slices + +- **C0 — Runtime bridge + live inputs (dormant).** Publish executor + adoption/completion through `Placements` as the receipt stream hosts + consume; derive `UsePositionFromServer`/`PlayerDistance` inside Runtime + from its own owners (no host-supplied gameplay inputs); wire the + exact-Setup mover chain end-to-end for the initial-Create placement; fix + the route-7 cancellation asymmetries. Still no production caller. +- **C1 — atomic controller/body publication (prerequisite C).** One + Runtime-owned transaction publishing the exact same canonical body to + graphical and no-window controllers, prepared off-canonical, committed + atomically, respected by every body writer/binding/clock-epoch/teardown + path. The rejected snapshot-lease design stays rejected. Adversarial-gate + heavy (the campaign handoff's full list). +- **C2 — placement allocation budget.** Pool or eliminate the operation/ + projection envelope allocations on the accepted placement path (root + cause, not a raised cap), or obtain explicit user approval for a measured + budget. Re-measure; the regression gate keeps the ceiling. +- **C3 — spawn-frequency cutover: routes 1 + 8.** Flip graphical AND + headless initial Create/login registration to + `RegisterEntityWithInitialResidence` + executor + placement receipts + together; `MaterializeProjection`/`BuildControllerAndCamera`/ + `SynchronizeLocalPlayer` become projection + acknowledgement only; + presentation-only rebucketing (`RebucketLiveEntity` loses `CommitRebucket`). + Gates add the exact lifecycle/reconnect connected route. +- **C4 — remaining routes: 2 (ForcePosition), 3 (portal, with the + `RuntimeWorldTransitState` → `RuntimePortalPlacementAuthority` adapter), + 4 (remote Create/Position; delete `RemoteTeleportController`/`Placement` + and the inline MoveOrTeleport duplicate), 5 (projectile authoritative), + 6 (drops + split-recovery marking), 7 (residual pickup/parent/delete + polish).** May land as more than one commit if a route proves large; + each sub-landing keeps the full review discipline. +- **C5 — legacy deletion + closeout gates.** Delete every superseded legacy + path; parity tests; exact lifecycle/reconnect + canonical nine-stop + connected routes; two-client observation; **user visual matrix** (the + campaign's stopping point for user acceptance). Retire AP-1, AD-1, + AP-131, AD-60's legacy half, and close #275. Update register/roadmap/ + milestones/architecture/memory + successor handoff. + +After C5: AP-22 (authored collision shapes), then AD-10 (remote +contact-plane projection), then the campaign's final matrix and ledger +closeout; vendor Slice 5 resumes. diff --git a/docs/research/2026-08-02-cutover-route-inventory.md b/docs/research/2026-08-02-cutover-route-inventory.md new file mode 100644 index 00000000..4b0c5885 --- /dev/null +++ b/docs/research/2026-08-02-cutover-route-inventory.md @@ -0,0 +1,1042 @@ +# 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=`, + `ACDREAM_AUTOMATION_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)