diff --git a/docs/research/2026-08-04-c4-route-3-contract.md b/docs/research/2026-08-04-c4-route-3-contract.md new file mode 100644 index 00000000..b3f73970 --- /dev/null +++ b/docs/research/2026-08-04-c4-route-3-contract.md @@ -0,0 +1,840 @@ +# C4 route 3 — portal / local-player placement: pinned contract (2026-08-04) + +**Scope:** make the local player's portal arrival canonical — construct the +first production `RuntimePortalPlacementAuthority` producer, execute the +deferred portal Place through a portal arm on route 2's +`RuntimeAcceptedPositionDriveController` over the one Runtime SetPosition +owner, re-home the controller-local teleport suffix, and delete the two +surviving duplicate placement authorities (`LocalPlayerTeleportPlacement.Place` +in App, `ResynchronizeLocalPlayerForPortalArrival` in Headless). + +**Route 3 is NOT a bug fix.** Portalling works and is user-accepted; this +route changes WHO commits the arrival, never when the player sees it. This is +the LAST C4 route. + +Pinned at HEAD **`ca96ea5e`**, branch `claude/acdream-physics-divergence-5aa784`. +**Line numbers are as-of `ca96ea5e` and WILL go stale; every citation also +names the symbol — trust the symbol** (process rule 6). + +**SEQUENCING BLOCKER — route 7 is concurrently in flight in this same +worktree.** Its uncommitted diff modifies `RuntimeSetPositionState.cs`, +`RuntimeAuthoritativePositionRouteClassifier.cs`, +`RuntimeLiveEntitySessionController.cs`, `RuntimeEntityObjectLifetime.cs`, and +`LiveEntityRuntime.cs` — five of route 3's surfaces. **Route 3 implementation +must not begin until route 7's commit lands.** First implementation step: +re-verify §3's site inventory against the post-route-7 HEAD (symbols, not +lines) and re-measure the Release baseline. Nothing in route 7's design +conflicts with this contract (it adds parent-cell propagation and a headless +parent-realize drive; it does not touch the portal transit, the drive +controller, or either duplicate authority) — the collision is textual, not +semantic. + +Predecessor documents, binding where they still apply: + +- [`2026-08-04-c4-route-3-scoping.md`](2026-08-04-c4-route-3-scoping.md) — + the research base (committed at `ca96ea5e`). Its §1–§5 findings, §10 trap + list, and §12 open gaps are folded in below, re-verified at HEAD. Its + correction of the campaign plan ("only the producer is missing") stands. +- [`2026-08-03-c4-route-2-contract.md`](2026-08-03-c4-route-2-contract.md) — + route 2 owns the seam this route extends. Its pins survive untouched: + ack-after-commit, `SendPositionImmediately` as a route property, the + ForcePosition no-re-arm rule (which INVERTS here — §4), and the #283 + world-frame invariant. +- [`2026-08-04-c4-route-4b-3-contract.md`](2026-08-04-c4-route-4b-3-contract.md) + — its 13 "must REMAIN true" invariants bind wherever the shared placement + machinery is concerned; its hook-BEFORE-placement rule INVERTS here (§4). +- The route-5 contract + its three review rounds — the defect classes this + contract addresses by name: *an invariant satisfied on one arm only* + (R3/B1/B2), *a mapping written against one caller's reachable set* (A2), + *a register row asserting behaviour the code does not have* (AP-141 risk + column), *a retail action skipped with the faithful port in-tree* (R2/A3), + *tests that assert only negatives* (B1 round-2), *presentation written on + outcomes Runtime declined to publish* (A1). +- [`2026-08-04-session-handoff-c4-remaining.md`](2026-08-04-session-handoff-c4-remaining.md) + — the six process rules apply verbatim. Note: its "#280 rides with route 3" + is OVERRIDDEN by this contract (§12 item 1; the scoping's §7 argument). + +--- + +## 0. Facts settled before this contract — do not re-derive + +1. **Only the PRODUCER is missing.** The portal authority's + consumption/validation side is live production code at three layers and + runs (with an empty portal) for every placement in the game: + `RuntimeWorldTransitState.IsCurrentPlacementAuthority` (`:258-275`), + `RuntimePlacementPresentationSink.TryApply` (`:100-106`), + `HeadlessRuntimePlacementProjectionSink.TryApply` (`:100-110` + + `HasValidPortalShape:112-121`), `LiveEntityRuntime.IsValidPortalPlacementAuthority`, + and `BeginAcceptedPlacementCore`'s portal gate + (`RuntimeSetPositionState.cs:1528-1534`). The authority is threaded through + command, mover preparation, operation, and projection token already. Grep + confirms zero `Present: true` constructions in `src/`. +2. **The surviving duplicates are exactly two**: `LocalPlayerTeleportPlacement.Place` + (`src/AcDream.App/Streaming/LocalPlayerTeleportController.cs:214-278`) and + `HeadlessSessionWorldProjection.ResynchronizeLocalPlayerForPortalArrival` + (`src/AcDream.Headless/Hosting/HeadlessSessionWorldProjection.cs:788-836`, + self-marked "TODO-C4 (route 3)"). +3. **Retail's local portal arrival is the GENERIC path** — the third route + in a row: `SmartBox::TeleportPlayer` @0x00453910 is + `CPhysicsObj::SetPositionSimple(player, dest, 1)` (flags 0x1012, route 2's + exact primitive) + `SmartBox::PlayerPositionUpdated(this, 1, FLT_MAX)`. + There is no dedicated portal placement path to port; the work is wiring, + ordering, and the post-placement suffix. +4. **The classifier's LocalPlayer+TeleportAdvanced route is fully modeled and + steady-state dead** (`RuntimeAuthoritativePositionRouteClassifier.cs:336-356` + at HEAD — the scoping's `:349-368` shifted under route 7's working diff): + `SetPositionSimple` + `AuthoritativeTeleportFlags` + + `TeleportHookPhase.AfterPositionOperation` + + `ConstrainPhase.AfterPositionOperation` + `ZeroVelocity: true` + + `SendPositionImmediately: false` + `PreserveHeading: false` + + `UnparentBeforeRouting: true` + `ApplyPlacementFrameBeforeRouting: + !HasAnimations`. It already encodes BOTH inversions (§4). Route 3 consumes + this route; it does not invent a parallel one, and the classifier body does + not change. +5. **#280 is SPLIT OUT** (scoping §7; campaign plan item 3). Not planned here, + not gated here. Route 3 keeps the Place gated on the SAME `ready` predicate + in `LocalPlayerTeleportController.Tick:489-493`, whatever radius that + predicate uses. +6. **The deferred-Place timing is the accepted architecture** (J6/E5, + user-gated repeatedly): retail places immediately on the accepted + destination Position and blocks SIMULATION on DAT prefetch behind the + portal viewport; acdream holds the PLACEMENT until reveal-readiness. Route + 3 changes the executor of the Place, never its timing (trap T3). §7's + register work records this. + +--- + +## 1. Retail ground truth (verified in `acclient_2013_pseudo_c.txt` at scoping `cff52c44`; the `PlayerTeleported` body byte-listed for this contract; re-verify at implementation) + +| claim | address | status | +|---|---|---| +| F751 (`SmartBox::HandlePlayerTeleport`) writes exactly three flags — `position_update_complete = 0`, `has_been_teleported = 0`, `waiting_for_teleport = 1` — after a wrap-safe TELEPORT_TS check. No position, no cell, no physics. | @0x00452150 (writes @0x00452193-0x004521A7) | ✓ scoping | +| `HandleReceivedPosition`'s LOCAL teleport branch (`newer_event(TELEPORT_TS)` @0x0045415F): `TeleportPlayer` @0x00454168 → **`ConstrainTo(player, &var_48 /* WIRE destination */, start, max)` @0x0045418A** → **`set_velocity(player, 0, 1)` @0x004541B4** → return. | @0x00453FD0 | ✓ scoping | +| `SmartBox::TeleportPlayer` = `SetPositionSimple(player, dest, 1)` @0x00453924 + `PlayerPositionUpdated(this, 1, FLT_MAX)` @0x00453932. `SetPositionSimple(…,1)` builds flags **0x1012** → generic `CPhysicsObj::SetPosition`. | @0x00453910 / @0x005162B0 | ✓ scoping | +| `PlayerPositionUpdated` teleport arm, AFTER the placement: `position_update_complete = 0`, **`waiting_for_teleport = 0` (the wait ends at PLACEMENT, not reveal)**, `has_been_teleported = 0`, **`teleport_hook(player)` @0x004538AE**, `cmdinterp->PlayerTeleported()` @0x004538B3, `set_viewer(&player->m_position, 1)` (camera reset), `LScape::update_viewpoint(0)`, blocking `CellManager::ChangePosition`. | @0x00453870 | ✓ scoping | +| **`CommandInterpreter::PlayerTeleported` @0x006B32B0 is exactly `SetAutoRun(0, 1)` then a tail-jump to `SendMovementEvent`.** | pseudo-C 699036-699042 | ✓ **byte-listed for this contract** | +| The local hook ordering FLIPS vs the remote route: local runs `teleport_hook` @0x00514ED0 AFTER `SetPositionSimple` returns (from `PlayerPositionUpdated`); the remote branch runs it BEFORE `SetPosition` (@0x005163EF). The hook `UnConstrain`s @0x00514F0C and `HandleReceivedPosition` re-arms @0x0045418A afterwards, so the leash survives. | | ✓ scoping | +| **`CPhysicsObj::enter_world` @0x00516170 is NOT on the portal path** — its local-player caller is the initial-login path only (@0x00455095). Portal arrival never re-runs `enter_world`/`HandleEnterWorld`; the cell install happens inside `SetPosition` itself. | caller sweep | ✓ scoping (agent-verified) | +| The FORCE_POSITION branch returns @0x0045409D before every `ConstrainTo` — route 2's no-re-arm finding reconfirmed. The TELEPORT branch DOES arm and DOES zero velocity. | | ✓ scoping | +| Retail places IMMEDIATELY on the accepted destination and blocks simulation on prefetch (`blocking_for_cells`; `SmartBox::UseTime` @0x00455410 runs only `CheckPrefetchStatus`) behind the portal viewport. acdream defers the PLACE to reveal-readiness — the accepted architecture (fact 6). | @0x004559B0 / @0x00455820 | ✓ scoping | + +Unverified residuals, none load-bearing (state, do not resolve): the exact +second argument of `teleport_hook` @0x004538AE (decompiler-elided, almost +certainly 1); `CheckPrefetchStatus`'s apparent 5 s re-check throttle (#280's +territory); where the 0x1012 flag bits are consumed below +`SetPositionInternal`. + +--- + +## 2. THE TWO INVERSIONS — read this before anything else + +An implementer arriving from routes 2 and 4b-3 carries two rules that are +**exactly wrong** on this route. Both are already encoded in the classifier's +LocalPlayer-teleport branch (fact 0.4) — **consume that route; do not +re-derive either rule from the neighboring routes' docs.** + +### Inversion A — route 2's "never re-arm the leash" flips + +Route 2 pinned (and its seam's class doc at +`RuntimeAcceptedPositionDriveController.cs:127-132` states) that the +constraint leash is NOT re-armed, because the FORCE_POSITION branch returns +@0x0045409D before every `ConstrainTo`. The local TELEPORT branch is the +opposite: retail arms `ConstrainTo` @0x0045418A — anchored at the **received +wire destination** (`&var_48`), not the resolved body position — AND zeroes +velocity @0x004541B4. The classifier carries both +(`ConstrainPhase.AfterPositionOperation`, `ZeroVelocity: true`). + +An implementer who generalizes the force arm's doc comment to the portal arm +ships a leash-less teleport (the 4b-3 A1 defect class: the ABSENCE of a +re-anchor where one should exist). Also inverted versus route 2: +`SendPositionImmediately` is **false** (retail's teleport branch returns +without `SendPositionEvent`; the outbound tail is `SendMovementEvent` + +LoginComplete — §5 D-T4/T11) and `PreserveHeading` is **false** (the wire +orientation applies). + +Note the anchor nuance: today's `RearmConstraintLeashAtCurrentPosition` +(`PlayerMovementController.cs:1834-1843`) anchors at `_body.CellPosition` — +the RESOLVED post-placement position — where retail anchors at the wire +destination. For a committed portal placement the two differ by at most the +placement adjustment (ring search/floor snap). Keep the existing shipped +anchor (the resolved position) and note it in the AD-2 amendment (§7): it is +the Campaign-P-accepted behaviour of `SetPositionCore`, the delta is +centimeters, and the leash anchor is write-only in the port (route-5 round-3 +C1). Do NOT silently switch anchors in this slice. + +### Inversion B — 4b-3's "hook before placement" flips + +4b-3's remote arm runs `teleport_hook` BEFORE the placement (@0x005163EF +before @0x00516420). The LOCAL route runs it AFTER: `teleport_hook` fires +from `PlayerPositionUpdated` @0x004538AE, after `SetPositionSimple` has +returned. The classifier carries the flip +(`TeleportHookPhase.AfterPositionOperation` local vs +`BeforePositionOperation` remote). Copying 4b-3's hook-first shape onto the +local arm is retail-wrong. + +Also: the local "hook + suffix" is not `RemoteTeleportHook`. Its live +actions for the local player are the controller-local UnStick/UnConstrain +(then the Inversion-A re-arm), `NotifyTeleported()` (the TargetManager pair), +the `PlayerTeleported` port (autorun cancel + movement event — §5 D-T4), and +the camera/viewpoint resets. All of these run at or after the committed +placement, never before it. + +--- + +## 3. Site inventory — re-verified at `ca96ea5e` + +Sites marked **[R7-flux]** sit in files route 7's uncommitted diff modifies; +re-verify their line numbers (symbols hold) after route 7 lands. + +### 3.1 The duplicate authorities to delete + +| # | site (symbol) | at HEAD | what it does today | +|---|---|---|---| +| D1 | `LocalPlayerTeleportPlacement.Place` | `LocalPlayerTeleportController.cs:214-278` (class `:183-290`, interface `ILocalPlayerTeleportPlacement:174-181`) | `_physics.Resolve(pos, cell, 0, StepUpHeight)` `:219` → `controller.SetPosition` `:230` (runs the whole `SetPositionCore` tail, §3.4) → direct `entity.SetPosition/ParentCellId/Rotation` `:242-244` → `RebucketLiveEntity` (throws on failure) `:254` → `_host.Host?.NotifyTeleported()` `:264` → `SetBodyOrientation` `:265` → camera resets `:267-268` → `_spatial.Reconcile()` `:269` → probe/log. No Runtime transaction, no receipt, no portal authority. ~55 non-comment lines. | +| D2 | `HeadlessSessionWorldProjection.ResynchronizeLocalPlayerForPortalArrival` | `HeadlessSessionWorldProjection.cs:788-836` (doc `:779-787`) | `CenterOn` → `Engine.Resolve(wire, lb, 0, 100f)` → `Engine.ResolvePlacement(0.48f/1.835f, IsPlayer\|EdgeSlide)` → `controller.SetPosition` → `SetBodyOrientation`. Called from `PrepareDestination:761`. ~40 non-comment lines. AD-42's last surviving citation. | + +### 3.2 The graphical drive (kept; rewired at the Place edge) + +| site | at HEAD | note | +|---|---|---| +| `LocalPlayerTeleportController` drive | `:385-789`; Place edge `Tick:509-533` | `ready` = destination + `!IsRecenterPending` + `_worldReveal.Evaluate(cell).IsReady` (`:489-493`); on `TeleportAnimEvent.Place`: `CanPlacePortalDestination` preflight `:514` → `_placement.Place(_pendingPosition, _pendingCell, _pendingRotation)` `:521` → `ObserveMaterialized` `:527` (a rubber-stamp AFTER the mutation — the structural defect this route retires). `PlayExitSound` → `RevealWorldViewport`; `FireLoginComplete` → `EnterWorld` + `SendLoginComplete` + `Complete` + `ResetTransit`. `_pendingPosition` is an App-frame-translated vector (`:401`) — dies with D1 (trap T4). | +| destination offer | `LiveEntityNetworkUpdateController.cs:2950-2957` | Last statement of `OnPosition` for a local `Apply`: `OfferDestination(FromAcceptedPosition(update), timestamps.TeleportAdvanced)`. Unchanged. | +| `WorldRevealCoordinator` | `WorldRevealCoordinator.cs`; `_hostProjections` private (`:40-62`); `TryBeginPortal:117-149` registers the host token | The producer does NOT need new exposure here — §5 D-T1 pins re-derivation through the transit owner. | +| local generic render-pose write | `LiveEntityNetworkUpdateController.cs:2284-2314`; stale comment `:2276-2277` | **Verified live at HEAD**: for the local player `earlyRemoteRoute` is null (`:2167-2175`), `OwnsSteadyState(null)` is false (`RuntimeRemoteSteadyStatePosition.cs:71-72` — both pattern-matches fail on null), so every accepted local Apply — including the portal destination Position — writes the raw wire pose onto the local player's `WorldEntity` AND rebuckets to the wire landblock while portal space covers the viewport. The comment "The local player never reaches this generic-remote code path at all" is FALSE. §5 D-T8 pins the handling. | + +### 3.3 The headless portal flow **[R7-flux]** + +`RuntimeLiveEntitySessionController.TryCompletePortal` (`:421-510` at HEAD; +route 7 added parent-drive methods above it): `TryGetAcceptedTeleportDestination` +→ `TryBeginPortalReveal` → `TryRegisterHostProjection` → ack +`ProjectionRegistered` → **`PrepareDestination(generation, destination)`** +`:450` (D2 runs here, plus `PlayerState.InWorld` and the readiness report) → +`AcknowledgeDestinationReadiness` → `AcknowledgePortalMaterialized` → ack +`SimulationReleaseProjected` → `RequireDestinationReservationRelease` → ack +`DestinationReservationReleased` → `AcknowledgeWorldViewportVisible` + +`Complete` → ack `TerminalProjected` → `SendGameAction(LoginComplete)` → +`EndTeleport`. **The whole suffix is synchronous in one call** — see §5 D-T7 +for the ordering consequence. + +### 3.4 Runtime surfaces this route builds against + +| site | at HEAD | relevance | +|---|---|---| +| `RuntimePortalPlacementAuthority` | `RuntimeSetPositionState.cs:105-119` **[R7-flux]** | `(bool Present, long RevealGeneration, ushort TeleportSequence, RuntimeWorldHostProjectionToken Projection)` with `IsEmpty`/`IsValid`. **The shape is RIGHT — no change.** It is exactly the tuple `RuntimeWorldTransitState` owns per reveal; `IsValid` already requires `Projection.Generation == RevealGeneration`. | +| Begin/Submit portal plumbing | `TryBeginExclusiveAuthoredPlacement:1444-1472` (portal param `:1448`); `BeginAcceptedPlacementCore` portal gate `:1528-1534`; `TryPrepareAndSubmitAuthoredPlacement` portal params `:1826/:1886` **[R7-flux]** | Begin refuses a non-empty portal unless `portal.IsValid && kind is LocalAuthoritative && portal.Projection.DestinationCell == (record.Snapshot.Physics?.Position ?? record.Snapshot.Position).LandblockId` — the LATEST merged Position. Transit pins the FIRST accepted destination per generation (J6.3). The mismatch edge is trap T5 / D-T5. | +| `RuntimeWorldTransitState` | `IsTeleportActive:96`, `ActiveTeleportSequence:97-98`, `TryRegisterHostProjection:189-231` (**idempotent for same generation+cell; refuses mismatch/cancelled/completed**), `IsCurrentPlacementAuthority:258-275`, `TryGetAcceptedTeleportDestination:522-527`, `CanPlacePortalDestination:534-541`, `AcknowledgePortalMaterialized:595` (requires `Readiness.IsReady`, refuses double-fire), `IsCurrentPortalDestination:870-882` (requires active + generation + Portal kind + !cancelled + !completed + cell match; does NOT require Materialized), `EndTeleport:547-556` | The producer's complete input set exists. Note the validation-window consequence: a portal-carrying Place projection receipt fails `IsCurrentPlacementAuthority` once `Complete`/`EndTeleport` has run — §5 D-T6/D-T7 pin the ordering. | +| `RuntimeAcceptedPositionDriveController` | seam `TryExecuteAcceptedLocalPosition:340-402` (ForcePosition-only gate `:348`); `SubmitAndResolve:670-773` (does not pass portal today); `ReconcileAndAcknowledge:830-840`; `SettlePending`/`_newestForce` re-issue funnel; `AbandonPending:312-329` (cancel + `PublishCancellation`) | Route 2's landed seam. The portal arm is a SIBLING entry sharing Begin/Submit/outcome handling but NOT the force funnel (trap T7 / D-T2). | +| `PlayerMovementController` | `SetPositionCore:1845-1920`; `RearmConstraintLeashAtCurrentPosition:1834-1843`; `CommitCanonicalForcePositionFrame:1944-1950` (the model: body write already canonical; controller-local reconciliation only) | The single largest risk: every `SetPositionCore` duty must be accounted for (D-T4). | +| classifier LocalPlayer teleport branch | `RuntimeAuthoritativePositionRouteClassifier.cs:336-356` **[R7-flux]** | Fact 0.4. Production classify callers today: the drive controller (Force only), `ClassifyRemoteAcceptedPosition`, the continuation executor. Route 3 is this branch's first steady-state producer. | +| `RuntimePlacementPresentationSink.TryPublishPlace` | `:210-241` | **Snapshots whatever the entity already holds — it writes no pose.** Confirmed at HEAD (`Snapshot(entity)` from `entity.Position/Rotation`). The committed-receipt suffix must therefore write the render entity itself (D-T6); route 2's B2 coverage gap (no test drives a local-player Place through the sink to a moved `WorldEntity`) becomes load-bearing here and MUST close. | +| `SpawnPlacementSettler` | first-entry only (`RuntimeLocalPlayerPhysicsPublicationState.cs:812`) | NOT invoked on the portal path; retail has no settle sweep here — contact resolves inside the SLIDE placement (trap T10). Do not add it. | +| world frame | `RuntimePhysicsState.ObserveLocalWorldFrame` (rebases on `teleportAdvanced` at merge); App `LiveWorldOriginState` recenters asynchronously | The `!IsRecenterPending` conjunct in `ready` is what makes both frames agree at Place time (#283, trap T4). The canonical arm keeps that gating and resolves through Runtime's frame (`resolveWorldOffsetFromRuntimeFrame: true`). | +| autorun latch | `RuntimeLocalPlayerMovementState.CancelAutoRun:226-234`; `ResetInputIntent:335-344` | **Verified gap:** no portal-arrival path cancels autorun today (`CancelAutoRun` callers: run-lock toggle, Stop/posture commands, attack prep; `ResetInputIntent` only on session reset). Retail's `PlayerTeleported` @0x006B32B0 does. D-T4 ports it. | + +### 3.5 What is already canonical and must not regress (J6) + +`RuntimeWorldTransitState` owns the reveal generation, destination latch, +F751 correlation (both packet orders), materialization/simulation edge, +viewport observation, wait cue, completion/cancellation, and the 4-stage host +acknowledgement suffix (J6.2/J6.3/J6.4). Route 3 moves NONE of it. The ONLY +transit-adjacent change: `AcknowledgePortalMaterialized` fires from the +committed placement receipt instead of rubber-stamping after a host mutation. +The lifecycle gate's `transitOwnership` zero-at-stable-checkpoint discipline +(`tools/run-connected-world-lifecycle-gate.ps1`) covers this state. + +--- + +## 4. What must REMAIN true (process rule 1 — for every path, including every refusal) + +1. **Inversion A holds**: on every COMMITTED portal placement the constraint + leash is armed exactly once, post-operation (`ConstrainTo` @0x0045418A + analog — today's `RearmConstraintLeashAtCurrentPosition`, relocated into + the teleport frame commit), after the hook-tail `UnStick`/`UnConstrain`; + and the body velocity is zero (retail @0x004541B4 / + `StopCompletelyAtPhysicsObjectBoundary`). The force arm's no-re-arm rule + and its class doc stay force-scoped — the doc gains one sentence scoping + itself to ForcePosition (process rule 6), nothing more. +2. **Inversion B holds**: no hook-family action (UnStick, UnConstrain, + NotifyTeleported, autorun cancel, camera reset, input-edge reset) runs + BEFORE the canonical placement on this route. The suffix runs from the + committed receipt. +3. **The Place timing does not move** (trap T3): the canonical arm executes + at each host's existing placement edge — graphical `TeleportAnimEvent.Place` + after `CanPlacePortalDestination` and the same `ready` predicate; headless + inside `PrepareDestination`'s slot in `TryCompletePortal`. No packet-accept + placement, no readiness-predicate change, no `TeleportAnimSequencer` + change, no `WorldRevealReadinessBarrier` change (#280's territory). +4. **On every refusal/rejection/contention outcome the player remains + presentable and the transit remains coherent**: the body stays `InWorld` + with an active object clock; presentation is never torn from the body (no + Withdraw survives — `restoreCancelledPark: true` on every cancellation, + `AbandonPending`'s exact shape); the portal viewport, wait cue, and + readiness evaluation are NOT touched by the failed placement (the reveal + either stays active for a retry or is cancelled through the EXISTING + transit cancellation — never a half-state); and no path leaves the player + permanently in portal space with a dead operation (trap T5 / D-T5). +5. **Reveal/readiness interaction on non-commit paths — nothing changes + there**: a refused placement must NOT fire `ObserveMaterialized`/ + `AcknowledgePortalMaterialized` (it would assert a materialization that + did not happen), must NOT `RevealWorldViewport`, must NOT advance the + anim-event stream's terminal events, and must NOT reset the wait cue + machinery. The materialization ack fires from the COMMITTED receipt only. +6. **The pose still advances on a committed placement's presentation**: after + the committed-receipt suffix, the render `WorldEntity` pose == + the RESOLVED canonical body pose, `ParentCellId` == the resolved cell, the + draw bucket moved (`RebucketLiveEntity`), the root pose/camera reset, and + the local-player collision shadow agrees (#312's layer — tests assert it). +7. **Exactly zero outbound `AutonomousPosition` events from the portal arm**, + on any outcome (`SendPositionImmediately: false`; retail's teleport branch + sends none). The outbound tail is exactly: one `SendMovementEvent`-family + movement refresh from the `PlayerTeleported` port (D-T4), and + LoginComplete at its existing `FireLoginComplete` edge (T11 — unchanged; + TS-28 narrowing stands; C3c's login-edge reasoning does NOT transfer). +8. **The force arm is byte-identical**: `TryExecuteAcceptedLocalPosition`'s + gate, `_newestForce`, `PositionEventOwed`, `SettlePending`, and every + route-2 test keep their expectations. Zero force-arm test changes is a + regression tripwire. +9. **J6 lifecycle ownership is untouched** (§3.5). The 4-stage host + acknowledgement suffix, F751 correlation, destination latch, wait cue, + AD-38 viewport-retire timing, and `TeleportViewPlaneController` do not + change. +10. **The world-frame invariant (#283) holds**: the arm submits only under + the existing `!IsRecenterPending` gating and resolves world offsets + through Runtime's frame (`resolveWorldOffsetFromRuntimeFrame: true`). + The App-translated `_pendingPosition` vector dies with D1; no second + conversion site appears. +11. **No settle sweep** (trap T10), **no allocation work** (trap T9 — + once-per-teleport), **no `enter_world`/`HandleEnterWorld` additions** + (retail does not run them here — §1). +12. **Headless keeps its non-placement duties** (trap T12): + `PrepareDestination`'s `CenterOn`, readiness report, and `PlayerState` + writes stay host-owned; `BeginTeleport`'s `PlayerState.PortalSpace` write + is out of scope. +13. **Route 7's surfaces are untouched**: `RuntimeEntityObjectLifetime`'s + parent/cell propagation, `EquippedChildRenderController`, the headless + parent-realize drive, `ParentAttachmentState`. If route 3 genuinely needs + an edit there, STOP and report the collision — do not plan through it. +14. **The recorded-not-consumed route facts stay recorded-not-consumed**: the + LocalPlayer teleport route's `UnparentBeforeRouting` / + `ApplyPlacementFrameBeforeRouting` have no reader today; this slice adds + no reader and does not delete the facts (4b-3's precedent). + +--- + +## 5. Design decisions — pinned, not open for redesign + +### D-T1 — the producer: construct the authority from live transit facts; NO new WorldRevealCoordinator exposure + +The authority's shape does not change (§3.4). Construction is pinned as: + +``` +new RuntimePortalPlacementAuthority( + Present: true, + RevealGeneration: , + TeleportSequence: transit.ActiveTeleportSequence, + Projection: ) +``` + +- **Graphical**: generation is `_pendingRevealGeneration` + (`LocalPlayerTeleportController:404`); the host token is re-derived through + the transit owner's **idempotent** `TryRegisterHostProjection:189-231` — + the same generation+cell returns the existing registered token (`:215-216`); + a stale generation, wrong cell, cancelled, or completed reveal returns + FALSE. This is the safety property the scoping's open gap 3 asked for, by + construction: **a superseded token cannot be handed to the producer**, + because re-derivation refuses. On refusal the arm does not run — treat as + the cancellation shape of D-T5. `WorldRevealCoordinator._hostProjections` + stays private; no accessor is added. +- **Headless**: generation, `destination.TeleportSequence`, and `projection` + are all already in scope inside `TryCompletePortal`; construct directly. +- The authority is passed to BOTH `TryBeginExclusiveAuthoredPlacement` and + `TryPrepareAndSubmitAuthoredPlacement` (both already take it); Begin's gate + (`:1528-1534`) and the sinks' `IsCurrentPlacementAuthority` do the + validating. **Do not add a third validation site.** + +### D-T2 — the portal arm: a sibling entry on `RuntimeAcceptedPositionDriveController`, sharing the core, NOT the force funnel + +New entry (name at implementer's discretion, e.g. +`TryExecuteAcceptedPortalArrival(record, in route, in portal)`), pinned in +behaviour: + +1. **Preconditions**: local player, canonical body present, no active + initial-Create residence, valid portal authority (D-T1), and the transit's + `CanPlacePortalDestination` preflight already passed at the caller. On any + precondition failure: `NotApplicable`-equivalent, write nothing. +2. **Classification**: build the LocalPlayer route through the SHARED builder + with the classifier's dormant teleport branch (fact 0.4) — from the + **retained accepted destination** (the transit's pinned destination / + the offer's captured `TeleportAdvanced` fact), never re-derived from live + timestamps at the Place edge (by then no timestamp is "advancing" — the + packet merged seconds ago). The classifier body does not change. +3. **Execution**: `TryBeginExclusiveAuthoredPlacement(record, version, + LocalAuthoritative, portal)` → `TryPrepareAndSubmitAuthoredPlacement(…, + portal, resolveWorldOffsetFromRuntimeFrame: true)` — the same shape as + `SubmitAndResolve`, either by extending that method with the portal + pass-through (preferred if the outcome handling stays shared) or a sibling + private core. `route.ZeroVelocity` is honored at the commit; the teleport + frame commit (D-T4) runs on the committed outcome; + `AcknowledgePortalMaterialized` fires from the committed receipt. +4. **NOT inherited from the force arm** (trap T7): `_newestForce`, the + re-issue funnel, `PositionEventOwed`, `SendImmediatePosition`, and + `SettlePending`'s ack semantics. ACE sends one destination per teleport; + the portal route owes no AutonomousPosition; a dead-watch re-issue of a + portal placement would fight the transit generation. The portal arm's + non-commit outcomes route to D-T5, not to a retry marker. + `DeferredCell` handling MAY reuse the pending/watch machinery for the + collision-generation wake (the destination was centered by the host before + submit, so a park should be rare) — but its wake path must re-validate the + portal authority before committing, and its cancellation must take + `AbandonPending`'s exact shape (`restoreCancelledPark: true` + + `PublishCancellation`). + +### D-T3 — the teleport frame commit: `SetPositionCore` re-homed, every duty accounted for + +Under the canonical commit, the BODY write (`SnapToCell`) moves into +Runtime's `CommitCanonical`. Every OTHER `SetPositionCore` duty +(`PlayerMovementController.cs:1845-1920`) must be mapped, in the +implementation commit, to exactly one of: (a) already performed by the +canonical commit, (b) performed by the new teleport analog of +`CommitCanonicalForcePositionFrame` (e.g. `CommitCanonicalTeleportFrame`), +or (c) deliberately dropped with a retail anchor. Losing one silently is the +4b-2 round-1 defect class. The duties, enumerated: + +| duty | pinned destination | +|---|---| +| body snap (`_body.SnapToCell`) | (a) canonical commit | +| render-lerp anchor reset (`_prevPhysicsPos`/`_currPhysicsPos`) | (b) | +| `UpdateCellId` publication ("teleport" reason; render-root publish) | (b) | +| `TransientState = Contact\|OnWalkable\|Active` seeding | verify (a) vs (b) — the #265/#166 check_contact family; if the canonical SLIDE placement already resolves contact, state it; if not, (b) keeps the seed. Do not drop. | +| `Velocity = 0` | (b), honoring `route.ZeroVelocity` (retail @0x004541B4) | +| `StopCompletelyAtPhysicsObjectBoundary` (retail StopCompletely 0x00527E40) | (b) | +| input-edge/mouse state reset (`_prevForwardHeld` etc.) | (b) | +| `UnStick` + `UnConstrain` + leash re-arm | (b) — Inversion A; anchor per §2 | +| `_body.LastUpdateTime = 0` + `_objectClock.ResetForEnterWorld()` | (b) | +| **NEW — `PlayerTeleported` port**: `CancelAutoRun()` on the J5.4 owner + one movement-event send (`SendMovementEvent` family) | (b) — retail @0x006B32B0, byte-listed §1. TWO NAMED BEHAVIOUR CHANGES (autorun now cancels on arrival; one movement refresh goes out) — call both out in the commit message, route-2 style. | + +The frame commit is Runtime-owned (called from the portal arm's committed +path), exactly like `CommitCanonicalForcePositionFrame`. Shared private +helpers between the two commits are sanctioned; changing the force commit's +behaviour is not. + +### D-T4 — graphical `Place` edge rewrite: acknowledge, don't author + +`LocalPlayerTeleportPlacement.Place` (D1) is deleted as an authority. The +Place edge becomes: build the authority (D-T1) → call the portal arm (D-T2) +→ on the committed receipt run the presentation suffix → `ObserveMaterialized` +(which now gates a mutation that already happened canonically — the +rubber-stamp inverts into a real acknowledgement). The presentation suffix, +pinned (all sourced from the RESOLVED committed body, not the wire pose): + +1. `entity.SetPosition(resolved)` + `entity.ParentCellId = resolvedCell` + + `entity.Rotation` + `RebucketLiveEntity` — the same writes D1 performs + today, re-sourced as a projection of the canonical result (the sink's + `TryPublishPlace` writes no pose — §3.4 — so this suffix is the render + entity's mover; the sink publishes world-state/shadow/visibility from it). +2. `NotifyTeleported()` (TargetManager pair), camera resets + (`Legacy.Update`/`ResetViewerToPlayer` — retail `set_viewer` analog), + `_spatial.Reconcile()`. +3. The probe line (D-T9). + +Whether `ILocalPlayerTeleportPlacement` survives as a thin acknowledge-only +seam or is deleted and inlined into the controller is the implementer's +choice; if it survives as a trivial adapter, record it as a C5 sweep +candidate rather than deleting here. + +**Ordering caveat (proof obligation P2):** the placement projection +subscription may acknowledge the Place receipt synchronously INSIDE the +SetPosition call — before this suffix runs — so the sink can snapshot the +pre-suffix entity pose (the T8 wire pose). Pin the END state, not the +intermediate: by the end of the Place edge every presentation surface the +sink touched (world snapshot store, local-player shadow, root pose) must +reflect the resolved pose. If tracing shows a surface that is written only +by the sink's snapshot and never refreshed, re-publish it from the suffix — +and state which in the commit. + +### D-T5 — refusal, rejection, contention, and the Begin cell-mismatch edge: defined, not discovered + +The genuinely new edge (scoping §4): Begin validates the portal's destination +cell against the LATEST merged snapshot while transit pins the FIRST accepted +destination per generation. A second local Position merging between the offer +and the Place edge makes Begin refuse (default token). Today benign (ACE +sends one destination per teleport) — but every non-commit outcome is pinned: + +| outcome | pinned behaviour | +|---|---| +| D-T1 re-derivation refuses (stale generation / cancelled / completed) | the arm does not run; the Place edge returns without mutating; the transit's own cancellation/supersession machinery (already J6-owned) is the authority on what happens next. No new cancellation path is invented. | +| Begin refuses (cell mismatch, contention, retained completion) | no body write, no presentation write, no materialization ack, no viewport change. The Place edge logs the probe line with the refusal cause and RETURNS — the anim stream stays where it is, so the NEXT Tick re-attempts the Place edge (the anim event re-fires while `ready` holds; verify and pin with a test — if the Place anim event is one-shot, the re-attempt must be driven by the same Tick predicate that produced it, and THAT mechanism must be stated in the commit). A permanent refusal (mismatched second destination) converges through the transit's existing supersession (the newer destination starts its own generation) or session reset — never a silent wedge in portal space. | +| `Contention`/retryable preparation | same visible behaviour as Begin-refusal: nothing mutates, re-attempt on a later edge/pump; any retained pending must carry the portal authority and re-validate it at wake (D-T2.4). | +| `DeferredCell` park | allowed through the shared machinery; the wake commit re-validates the portal authority; cancellation restores the park (`restoreCancelledPark: true`). The player stays in portal space (viewport intact) until the commit — which is exactly today's user-visible wait-cue behaviour. | +| `Rejected`/cancelled after Begin | cancel the token (`CancelToken` shape), write nothing, no materialization ack; same re-attempt/supersession convergence as Begin-refusal. | +| teardown / disconnect mid-transit | `AbandonPending` + the session reset's existing transit reset; the ledger (`AcceptedPositionDrivePendingCount` or the portal arm's own registration) converges to zero — asserted by the reset suite. | + +The invariant across every row: **the pose the player last had remains +presented** (portal space owns the viewport, the body did not move, and no +Withdraw was published), and **the transit lifecycle is exactly as J6 left +it**. + +### D-T6 — headless: `PrepareDestination` flips to the same arm + +`ResynchronizeLocalPlayerForPortalArrival` (D2) is deleted. +`PrepareDestination` keeps `CenterOn` + readiness reporting + `PlayerState` +writes and calls the SAME portal arm (via the session controller's existing +drive access) with the authority built from the in-scope +generation/sequence/projection. Ordering pinned: the placement commits +**before** `AcknowledgePortalMaterialized` (as today — D2 runs inside +`PrepareDestination`, ahead of the materialization ack at `:469`), while the +transit is active and current, so the synchronous projection acknowledgement +inside the commit validates against a live authority. Because +`TryCompletePortal` then runs `Complete` + `EndTeleport` synchronously, **a +portal-carrying Place receipt left unconsumed past `EndTeleport` fails +`IsCurrentPlacementAuthority` forever** — proof obligation P3 establishes +that the headless sink consumes or the cancellation retires every portal +receipt before that edge, or the design adjusts (e.g. the commit's +synchronous ack is confirmed to be the only consumer headless). + +The headless arm reuses the identical Runtime entry — dual-host parity is a +test obligation, not an aspiration (§8). + +### D-T7 — the T8 second writer: tolerated, comment corrected, nothing suppressed + +The local generic render-pose write + rebucket +(`LiveEntityNetworkUpdateController.cs:2284-2314`) KEEPS running for accepted +local Apply packets, including the portal destination Position. It is +pre-existing (AP-131/#275 territory, C5's scope), hidden behind the portal +viewport, and the committed-receipt suffix (D-T4) overwrites it with the +resolved pose. Route 3 does NOT suppress it during the teleport window — +suppression would be an unowned behaviour change on the ordinary local Apply +path. The stale comment at `:2276-2277` ("The local player never reaches +this generic-remote code path at all") is corrected in this slice to state +what the code does (process rule 6). The tolerance is stated in the AD-2 +amendment (D-T9) so the next reader knows the overwrite ordering is +load-bearing. + +### D-T8 — probe + +`ACDREAM_PROBE_LOCAL_TELEPORT=1`, `PhysicsDiagnostics`-owned, TEMPORARY +family (strip with the physics-probe family). One line per portal-arrival +attempt: cause (`portal`/`recall`/`admin`), host (`graphical`/`headless`), +placement status, portal generation/sequence, destination cell, resolved +cell, `hookTail=ran`, `leash=armed`, `autorun=cancelled`. The connected gate +is a pass ONLY with probe evidence (process rule 5). + +### D-T9 — register, issue, and documentation bookkeeping, in the implementation commit + +- **AD-42 is DELETED.** Its last surviving citation is D2's two-call + `Resolve`+`ResolvePlacement` split; the replacement is the canonical + placement family (the faithful port). Register rule: the commit that ports + the retail mechanism deletes the row. +- **AD-2 is AMENDED**, not given a sibling row: add the deferred-Place + sentence — retail places immediately on the accepted destination Position + and blocks simulation on DAT prefetch (`blocking_for_cells`, + `SmartBox::UseTime` @0x00455410, `CellManager::PreFetchCells` @0x00455820); + acdream defers the PLACEMENT itself to the reveal-ready Place edge behind + the portal viewport, executed by the canonical Runtime transaction — plus + the T8 overwrite-ordering note (D-T7) and the leash-anchor nuance (§2 + Inversion A). This closes the scoping's open gap 1 (no existing row states + the placement-timing adaptation; AD-2 is the row that owns this + architecture). +- **The 2026-07-16 pseudocode correction LANDS IN THIS SLICE** (decision on + scoping documentation defect 1): `2026-07-16-portal-completion-pseudocode.md` + attributes portal arrival to `enter_world` (`player.enter_world(destination)` + in its §"accepted portal destination becomes ready" listing, and the + `enter_world` discussion above it). That is the LOGIN path + (@0x00455095-caller); portal arrival is `SmartBox::TeleportPlayer` → + `SetPositionSimple` (§1). Add a dated correction banner citing the caller + sweep; the doc's conclusion (commit the cell before releasing simulation) + survives. Rationale for in-slice: it is the retail record for the exact + mechanism this commit ports, and this commit's own citations contradict it + — leaving it is the "a register row asserting behaviour the code does not + have" class applied to a research doc. +- **The `:2276` stale comment correction LANDS IN THIS SLICE** (decision on + scoping documentation defect 2) — D-T7. The behaviour itself is untouched. +- **The campaign plan's gap line is corrected**: plan `:92-93` ("zero + producing call sites; the adapter … does not exist") becomes a dated + correction — the consumption/validation half was live before route 3; route + 3 added only the producer. Same commit family as the route's docs update. +- **The force-arm class doc** (`RuntimeAcceptedPositionDriveController.cs:127-132`) + gains the one force-scoping sentence (§4 item 1). +- **ISSUES.md**: none closed by this slice. #280, #275, #316 untouched. + AP-131/AP-135 untouched. + +--- + +## 6. Proof obligations (prove, not assume; stated in the implementation commit) + +- **P1 — the D-T3 duty map**: every `SetPositionCore` duty mapped to + (a)/(b)/(c) with the contact-seeding question answered from the canonical + commit's actual behaviour (read `CommitCanonical`'s transient-state + handling; do not guess). +- **P2 — presentation end-state** (D-T4 caveat): trace every surface the + sink's `TryPublishPlace` writes for the local player (world snapshot store, + `_localPlayerShadow`, visibility sinks) and establish each reflects the + resolved pose by the end of the Place edge — or is re-published by the + suffix. Name the mechanism per surface. +- **P3 — no wedgeable portal receipt** (D-T6): walk both hosts' + projection-subscription consumption for a portal-carrying Place: establish + that the receipt is consumed while `IsCurrentPortalDestination` still + holds (synchronous ack inside the commit, or pumped before + `Complete`/`EndTeleport`), and that every failure path (declined sink, + teardown, supersession) retires it through the existing + cancellation/Discard machinery. A receipt nothing can ever consume or + retire is a FIFO wedge — the failure mode the sinks' own doc comments warn + about. +- **P4 — the re-attempt mechanism on a refused Place edge** (D-T5): establish + whether `TeleportAnimEvent.Place` re-fires on subsequent Ticks while + `ready` holds (read `TeleportAnimSequencer`); if it is one-shot, name and + test the actual re-attempt driver. +- **P5 — ledger convergence**: teardown, session reset, and generation + change with a portal operation in flight converge the drive's pending + count and the transit ownership counters to zero (the reset suites + + `transitOwnership` checkpoints). +- **P6 — the movement-event half of the `PlayerTeleported` port**: confirm + the outbound movement event goes through the existing + `LocalPlayerOutboundController` seam with retail's shape (one refresh, + reflecting the stopped post-teleport state), and that ACE accepts it + without side effects (the connected gate observes the arrival stance on a + second client). + +--- + +## 7. Deletion inventory + +| site | action | lines | +|---|---|---| +| `LocalPlayerTeleportPlacement.Place` body (`LocalPlayerTeleportController.cs:214-278`) | authority deleted; class rewritten to the D-T4 acknowledge suffix (or inlined; interface fate per D-T4) | ~55 non-comment deleted; suffix re-added smaller | +| `LocalPlayerTeleportPlacement.CellLocalForSeed` (`:280-289`) | dies with D1 (the App frame translation — trap T4) | ~9 | +| `_pendingPosition` App-frame plumbing in the controller (`:401`, its writes in `TryAimAcceptedDestination`) | replaced by the transit's retained destination (D-T2.2) | small | +| `HeadlessSessionWorldProjection.ResynchronizeLocalPlayerForPortalArrival` (`:788-836` + doc `:779-787`) | deleted; `PrepareDestination:761` call replaced by the portal-arm drive (D-T6) | ~40 non-comment + 9 doc | +| `SessionPlayerComposition.cs:888` (`new LocalPlayerTeleportPlacement(...)`) | rewired to the surviving suffix shape | ~8 ctor args | +| `tests/AcDream.App.Tests/Streaming/LocalPlayerTeleportControllerTests.cs` | the fake-placement scenarios re-expressed against the canonical arm + suffix — each scenario maps to a successor or is named obsolete-with-reason in the commit (route-5 discipline), never dropped as collateral | audit | +| stale comments (process rule 6) | `:2276-2277` (D-T7); `HeadlessSessionWorldProjection:779-787` TODO block (dies with D2); the drive controller class doc force-scoping sentence; grep `LocalPlayerTeleportPlacement`/`ResynchronizeLocalPlayerForPortalArrival` across `src/` + `docs/architecture/` and re-point every survivor | — | + +Net: ~225-400 added non-comment production lines (Runtime portal arm +90-150; teleport frame commit 40-80 incl. the `PlayerTeleported` port; +graphical producer + Place rewrite 60-110; headless flip 15-30; probe ~10; +D-T1 needs no coordinator exposure so the scoping's 10-20 there drops out), +net roughly +150 to +250. Tests are the larger share (~500-900 lines). + +--- + +## 8. Test plan + +Rules: assert the layer that historically broke (presentation, transit +ownership, ledger — not only `InWorld`/clock); positive facts, not only +negatives; every new test must fail against a broken implementation (route-5 +round-3's self-verifying-discriminator standard where staging permits). + +Focused Runtime tests (`tests/AcDream.Runtime.Tests`): + +1. **Producer validity**: the authority built through D-T1 satisfies + `IsValid` and `Begin`'s gate for the pinned destination; a stale + generation, cancelled reveal, completed reveal, or wrong cell makes + re-derivation refuse and the arm return without writing (positive half: + the transit snapshot and body are bit-unchanged). +2. **Committed portal placement**: body at resolved destination, cell + committed canonically, velocity zero, leash armed exactly once + post-operation at the resolved anchor (count the arms — 4b-3 P3's + observable), autorun latch cleared, exactly one movement event and zero + AutonomousPosition events queued, object clock reset, input edges reset, + `AcknowledgePortalMaterialized` observed by the transit (Materialized + true, simulation available), probe fields. +3. **The Begin cell-mismatch edge** (T5): merge a second local Position with + a different landblock between offer and Place → Begin refuses → nothing + mutates, no materialization, transit still active; then the D-T5 + convergence path (supersession or reset) drains the ledger to zero. +4. **Refusal matrix** (one test per D-T5 row): refused re-derivation; + contention; `DeferredCell` park + collision-generation wake (wake + re-validates the authority; commit then fires materialization); rejected + after Begin; teardown mid-park (`restoreCancelledPark` restores, ledger + zero). Every row asserts the positive facts: body unmoved, `InWorld`, + clock active, viewport/wait-cue state untouched, no Withdraw published. +5. **Inversion tests, both directions**: (a) the force arm still never arms + (route 2's existing partition test untouched) while the portal arm always + arms on commit; (b) no hook-family action runs before the canonical + commit on the portal arm (observable ordering, e.g. the leash is + UnConstrained-then-re-armed only after the commit's receipt). +6. **Dual-host parity**: the headless flow (D-T6) drives the same arm; a + headless portal completion produces the same canonical body/cell/leash/ + autorun/ack facts as the graphical one, and `TryCompletePortal`'s + acknowledgement suffix still converges (P3's no-wedge property asserted: + no unconsumed portal receipt survives `EndTeleport`). +7. **Ledger/reset**: P5. + +App-layer tests (`tests/AcDream.App.Tests`): + +8. **The committed-receipt presentation suite (#312's layer + route 2's B2 + closure)**: after a committed portal placement through the REAL sink + + suffix, the render `WorldEntity` position/rotation/`ParentCellId` equal + the resolved body, the draw bucket moved, the local-player shadow agrees, + and the sink's Place receipt was consumed with a VALID portal authority + (the portal gate finally exercised live — assert it discriminates: a + stale-authority receipt is not consumed and is retired by the + cancellation path, not wedged). +9. **The T8 overwrite ordering**: an accepted portal destination Apply + writes the wire pose (the tolerated generic write), and the Place edge's + committed suffix then overwrites with the resolved pose — asserting the + END state and that the intermediate never leaks past the Place edge. +10. **Refused Place edge presentation**: a refused arm leaves the portal + viewport owning presentation, fires no materialization/reveal/exit-sound + event, and the player entity is untouched (positive: the pre-teleport + pose is still the presented pose). +11. **Sabotage check (manual, once, before finalizing)**: break the suffix + (skip the render-entity write) → test 8 fails; break the leash re-arm → + test 2 fails; hardcode the force route onto the portal arm + (`ConstrainPhase.None`) → test 5a fails; skip `CancelAutoRun` → test 2 + fails. If a sabotage survives, fix the test. + +--- + +## 9. Gates + +- **Focused**: §8 suites green. +- **Complete Release suite**: + `$env:ACDREAM_PAK_PATH = "$env:USERPROFILE\Documents\Asheron's Call\acdream.pak"`, + `dotnet test AcDream.slnx -c Release -m:1`. **Baseline 11,063 passed / 4 + skipped / 0 failed at `cff52c44`.** Route 7 will move this number before + route 3 starts — measure the post-route-7 baseline first and record the + new figure; never inherit. Two known flakes, never chase and never + conflate: **#302** (`PortalProjectionTests.ClipToRegion_FrameOwnedStore_…`, + GC-allocation assertion, App.Tests) and **#308** (`NakEmissionTests.LossSoak_…`, + wall-clock deadline, Core.Net.Tests, full-suite load only). If either + appears, re-run and say which. +- **Connected graphical gate (user-run, REAL and MANDATORY).** Release + build, `ACDREAM_RETAIL_UI=1`, `ACDREAM_PROBE_LOCAL_TELEPORT=1`, live ACE. + One session exercising, in order: + 1. a physical outdoor portal (e.g. Holtburg portal); + 2. a dungeon portal (indoor destination — the EnvCell readiness path); + 3. `/ls` lifestone recall AND one spell recall (the F751 recall family); + 4. an ACE admin teleport of the LOCAL player (`@teleto`/`@teleloc`) — + advances ObjectTeleport and, per the route-2 visual-gate doc, exercises + exactly this route; + 5. a same-destination revisit (ACE may omit CreateObject on revisit); + 6. **autorun through a portal**: engage autorun, walk into a portal — + arrival must be at REST (the `PlayerTeleported` port observable); + 7. graceful close. + + **Pass requires ALL of** (a clean-looking session is NOT a pass — + process rule 5): + - one probe line per arrival with `placement=Committed`, the portal + generation/sequence, the resolved destination cell, `leash=armed`, + `autorun=cancelled` (arrivals 1-5), and zero `Refused`/`Contention` + lines in ordinary play; + - user visual: the purple materialization silhouette without an opaque pop + or late tail (the 2026-07-25 accepted baseline), camera reset behind the + player, movement works immediately (walk out with W held — the + input-edge half), idle stance (no run-in-place), no rubber-band/tether + after arrival AND no leash absence (the probe's `leash=armed` field is + the observable — do not invent a visual for it); + - a second client observing an arrival sees a normal materialization and + stance (P6's movement-event observable); + - the exact lifecycle/reconnect gate + (`tools/run-connected-world-lifecycle-gate.ps1`) passes with every + `transitOwnership` counter zero at every stable checkpoint. +- **Headless parity gate**: the K-style connected four-stop portal route + (K3 closeout recipe) with the probe enabled — the same + probe-line-per-arrival requirement on the no-window host, proving D2's + replacement executed there. +- **Honest gap to record up front (4b-3 style)**: mid-transit supersession + (a second teleport before the first materializes) and mid-transit + disconnect are hard to provoke against ACE on demand. If the session does + not produce them, record the stale-generation/cancellation behaviour as + test-verified-only — never fold it into a blanket "gate passed". + +--- + +## 10. Budget and stop conditions + +**Budget: ~225-400 added non-comment production lines, ONE slice** (§7). +Calibration: 4a 364; 4b-2 350-500; 4b-3 ~250 net; route 5 ~131. + +**Stop and report rather than pushing through when:** + +1. Added production lines exceed ~500. +2. The design starts needing changes to `RuntimeWorldTransitState`'s + lifecycle semantics, the `TeleportAnimSequencer` timings, or + `WorldRevealReadinessBarrier` — each is a sign #280 or a J6 regression is + being smuggled in. +3. The Begin cell-mismatch edge (D-T5) turns out to be reachable in ordinary + play (ACE double-destination) — the refusal design needs the user's eyes. +4. P3 finds a portal receipt no mechanism can consume or retire — the + FIFO-wedge shape changes the design, not the test. +5. Route 3 needs an edit inside route 7's surfaces (§4 item 13). +6. Any force-arm (route 2) test changes expectation. +7. The complete Release suite deviates from the measured post-route-7 + baseline beyond the two named flakes. + +--- + +## 11. What this slice does NOT do + +- **#280** — split out; its own slice with its own visual gate (campaign + plan item 3). The session handoff's "rides with route 3" is overridden — + reported, not smoothed. +- **AP-131 / #275** — the shared merge call and the ordinary local Apply + path (including D-T7's tolerated generic write) stay for C5. +- **AP-135, #276, #316** — untouched. +- **Route 7's ownership** — `RuntimeEntityObjectLifetime` parent/cell + propagation, `EquippedChildRenderController`, headless parent drive. +- **AP-1/AD-1 retirement, the legacy-deletion sweep, parity tests, the + final connected matrix** — C5. Surviving trivial seams + (`ILocalPlayerTeleportPlacement` if reduced to an adapter, the test-only + `BeginAcceptedPlacement`/`BeginAuthoredPlacement` wrappers) are recorded + as C5 sweep candidates, not deleted here. +- **No headless remote consumer, no reveal-gate changes, no + presentation/anim/viewport changes, no `enter_world`/`HandleEnterWorld` + additions, no settle sweep, no allocation work.** +- **The leash-anchor nuance** (§2 Inversion A) — kept as shipped, recorded; + switching to retail's wire-destination anchor is its own decision if ever + taken. + +--- + +## 12. Contradictions and open questions — reported, not smoothed + +1. **The session handoff vs the campaign plan on #280**: the handoff says + "#280 rides with [route 3]"; the plan sequences it as its own item. This + contract follows the plan (scoping §7's mechanical argument: disjoint + blast radii, disjoint review lenses, both changes gate the same session + but share no mechanism). If the reviewer prefers the handoff's bundling, + that is a scope decision for the user — the technical recommendation is + SPLIT. +2. **The campaign plan's `:92-93`** ("the adapter … does not exist") + overstates the gap — the validation half is live production code; only + the producer is missing (scoping §1.1, re-verified here). D-T9 corrects + the plan line in the docs commit. +3. **The 2026-08-02 route inventory** remains wrong in the eight ways the + scoping's §3 enumerated; it is a dated research record (route-5 A11 + precedent: acceptable), and this contract supersedes its route-3 section. +4. **Scoping delta**: the scoping's §8 budgeted 10-20 lines for + "`WorldRevealCoordinator` host-token exposure"; this contract's D-T1 + removes that item entirely (re-derivation through the transit's idempotent + `TryRegisterHostProjection` is safer — a stale token is unobtainable by + construction — and needs no new surface). The scoping's classifier line + range (`:349-368`) is already stale under route 7's working diff + (`:336-356` at `ca96ea5e` + diff); symbols hold. +5. **New since the scoping, found while pinning**: (a) + `CommandInterpreter::PlayerTeleported` @0x006B32B0 byte-listed — + `SetAutoRun(0,1)` + `SendMovementEvent` — and the autorun-cancel gap + confirmed real (no arrival path cancels the J5.4 latch today), so D-T3 + ports it as two named behaviour changes; (b) the sink's `TryPublishPlace` + writes no pose (route-5 R3's finding re-confirmed for the LOCAL player), + making the committed-receipt suffix the render entity's mover — pinned in + D-T4 rather than discovered in review; (c) the headless + `TryCompletePortal` suffix is fully synchronous, producing the + receipt-past-`EndTeleport` wedge hazard P3 exists for. + +Open questions routed to the reviewers: + +- **To the retail-conformance reviewer**: (a) verify the §1 table against + the pseudo-C independently, especially the `PlayerPositionUpdated` action + order and the `enter_world` caller sweep (the load-bearing negative); + (b) judge the leash-anchor nuance (§2) — resolved-position anchor kept vs + retail's wire-destination anchor — is the recorded delta acceptable or + should this slice switch it?; (c) confirm `SendMovementEvent`'s outbound + shape for the post-teleport refresh (P6) matches retail's (autonomy + handling, stance content). +- **To the architecture reviewer**: (a) P3's no-wedge walk on both hosts; + (b) whether the portal arm should share `SubmitAndResolve` (extended with + the portal pass-through) or a sibling core — pinned behaviour either way, + but the sharing decision affects the force arm's blast radius; (c) the + D-T5 re-attempt mechanism (P4) once read from the sequencer.