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>
840 lines
58 KiB
Markdown
840 lines
58 KiB
Markdown
# 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.
|