acdream/docs/research/2026-08-04-c4-route-3-contract.md
Erik 19ebf043e3 docs: pin the C4 route 3 contract (portal producer adapter)
The last C4 route. Portalling works today; route 3 removes a duplicate
placement authority (LocalPlayerTeleportPlacement.Place plus headless's
ResynchronizeLocalPlayerForPortalArrival), it does not fix a bug.

~225-400 added non-comment production lines, one slice. Every site
re-verified at HEAD by reading, not inherited from the scoping.

The authority's shape is RIGHT as-is and redesign is forbidden:
RuntimePortalPlacementAuthority's 4-tuple is exactly what transit owns per
reveal, and IsValid already cross-checks generation. Only the PRODUCER is
missing — the campaign plan's "the adapter does not exist" overstates the gap,
since consumption and validation are live production code at three layers.
The producer additionally needs no new WorldRevealCoordinator exposure: it
re-derives the host token through transit's idempotent
TryRegisterHostProjection, which makes a superseded token unobtainable by
construction.

Retail's local portal arrival is the GENERIC path for the third route running:
SmartBox::TeleportPlayer @0x00453910 is SetPositionSimple(player, dest, 1)
with flags 0x1012 — route 2's exact primitive — plus PlayerPositionUpdated.

TWO rule inversions are the contract's loudest section, because an implementer
arriving from the routes just landed will otherwise carry the wrong rule:
route 2's "never re-arm the leash" INVERTS (the teleport branch arms
ConstrainTo @0x0045418A and zeroes velocity @0x004541B4), and 4b-3's
hook-before-placement ordering INVERTS (the local teleport_hook runs AFTER
placement, from PlayerPositionUpdated @0x004538AE).

Three findings new since scoping: PlayerTeleported @0x006B32B0 byte-confirmed
as SetAutoRun(0,1) + SendMovementEvent, with the autorun-cancel gap verified
real — nothing cancels the J5.4 latch on arrival today; TryPublishPlace writes
no pose, so the committed-receipt suffix is the render entity's mover; and
headless TryCompletePortal's fully-synchronous suffix creates a
receipt-past-EndTeleport FIFO-wedge hazard, covered by proof obligation P3.

SEQUENCING BLOCKER recorded in the contract's front matter: route 7's
concurrent diff modifies five route-3 surfaces. The collision is textual, not
semantic — route 7 adds parent-cell machinery and touches neither the portal
transit, the drive controller, nor either duplicate authority — but route 3
must not start until route 7 commits, and must then re-verify its inventory by
symbol and re-measure the Release baseline.

#280 is SPLIT OUT, siding with the campaign plan's own separate sequencing
over the session handoff's "rides with route 3" claim.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 22:24:41 +02:00

58 KiB
Raw Blame History

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 — 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 — 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 — 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 — 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) @0x0045418Aset_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 UnConstrains @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) :219controller.SetPosition :230 (runs the whole SetPositionCore tail, §3.4) → direct entity.SetPosition/ParentCellId/Rotation :242-244RebucketLiveEntity (throws on failure) :254_host.Host?.NotifyTeleported() :264SetBodyOrientation :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) CenterOnEngine.Resolve(wire, lb, 0, 100f)Engine.ResolvePlacement(0.48f/1.835f, IsPlayer|EdgeSlide)controller.SetPositionSetBodyOrientation. 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) :521ObserveMaterialized :527 (a rubber-stamp AFTER the mutation — the structural defect this route retires). PlayExitSoundRevealWorldViewport; FireLoginCompleteEnterWorld + 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): TryGetAcceptedTeleportDestinationTryBeginPortalRevealTryRegisterHostProjection → ack ProjectionRegisteredPrepareDestination(generation, destination) :450 (D2 runs here, plus PlayerState.InWorld and the readiness report) → AcknowledgeDestinationReadinessAcknowledgePortalMaterialized → ack SimulationReleaseProjectedRequireDestinationReservationRelease → ack DestinationReservationReleasedAcknowledgeWorldViewportVisible + Complete → ack TerminalProjectedSendGameAction(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:  <the active reveal generation>,
    TeleportSequence:  transit.ActiveTeleportSequence,
    Projection:        <token from transit.TryRegisterHostProjection(generation, destinationCell, out token)>)
  • 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::TeleportPlayerSetPositionSimple (§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):

  1. 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).
  2. 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.
  3. 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).
  4. 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 ownershipRuntimeEntityObjectLifetime 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.