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>
This commit is contained in:
Erik 2026-08-04 22:24:41 +02:00
parent ca96ea5e32
commit 19ebf043e3

View file

@ -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: <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::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.