acdream/docs/research/2026-08-04-c4-route-3-architecture-review.md
Erik e0f96a55bf fix(physics): C4 route 3 — portal placement authority (local player)
Removes a duplicate placement authority for local-player portal arrival.
Portalling worked before this change and works after it — this is not a
bug fix, EXCEPT that it found and fixed one dead-code production bug.

THE PRODUCTION BUG: TryExecuteCanonicalPortalPlacement re-read the
accepted destination at Place time, but TryBeginPortalReveal already
consumes that slot at Aim time — so the arm was 100% dead code and every
real portal Place refused with host-token-unavailable. Found only
because we refused to accept 7 skipped tests instead of chasing the
count to zero.

RETAIL IS THE GENERIC PATH FOR THE THIRD ROUTE RUNNING:
SmartBox::TeleportPlayer @0x00453910 = SetPositionSimple(dest, 1) with
flags 0x1012, followed by PlayerPositionUpdated.

BOTH INVERSIONS, WITH THEIR ANCHORS: unlike route 2, the leash IS armed
here (ConstrainTo @0x0045418A) and velocity is zeroed
(set_velocity @0x004541B4); unlike route 4b-3, the local teleport_hook
runs AFTER placement (@0x004538AE).

THE THREE-ROUND DEFECT CHAIN, HONESTLY:
- Round 1 released the player at the pre-teleport position while the
  anim stream marched on — the contract wrongly assumed Place re-fires
  (process rule 1's third occurrence this campaign).
- Round 2's fix inferred commit from a global PendingCount, which three
  non-committing paths also clear — making the SAME bug complete
  cleanly and silently. Strictly worse than round 1: round 1 at least
  tripped portal-complete-before-materialized.
- Round 3 latches the commit where it actually happens
  (ReconcileAndAcknowledgePortal), keyed on reveal generation and
  teleport sequence, via TryConsumePortalCommit. Two of the three
  required regression tests landed and are sabotage-verified on both
  hosts (ParkedPlace_ForgottenByOrdinaryMergeDoesNotLatchAsCommitted /
  HeadlessPortalPrepareDestinationForgottenByOrdinaryMergeDoesNotLatchAsCommitted).
  The third (force-arm-takes-the-slot) was judged unnecessary on review:
  with the inference gone, PendingCount is only a "don't ask yet" guard
  at both gates, so a force operation occupying or vacating the slot no
  longer changes an input the commit decision reads — the case collapses
  into what the landed test already discriminates.

THE B2/P3 RESOLUTION: both round-2 reviews were right about different
branches of the same synchronous call. RuntimePlacementProjectionSubscription
.OnPlacement acknowledges the FIFO head only when TryApply returns true;
a Place whose portal authority went stale (transit ended/superseded
while parked) used to return false, wedging every later entity's
placement receipt behind it forever. Both sinks
(RuntimePlacementPresentationSink, HeadlessRuntimePlacementProjectionSink)
now acknowledge-and-ignore a stale-authority Place instead of refusing
it. The regression test (RuntimePlacementPresentationSinkTests
.PortalPlace_StaleTransitHostOrSequenceIsAcknowledgedAndIgnored) had
been asserting the old, wrong `false` behaviour; it now asserts and
sabotage-verifies the fix.

Also lands: AP-144 (register discipline — the portal movement-event
send reuses the stricter UsePositionFromServer gate where retail's
SendMovementEvent is the looser autonomy_level != 0 test, diverging
only at level 1, currently unreachable), AP-145 + issue #318 (the
local-player collision-shadow presentation write bypasses its own
publisher's ShadowObjects write via a direct cache .Set(), self-healing
only once dedup diverges — filed, not fixed, pending a composition
test), AD-42 deleted (its last citation retired by the canonical portal
arm), AD-2 updated (the wait-cue's trigger predicate now covers a
second cause), and two documentation corrections: the enter_world
misattribution (both call sites are in SmartBox::HandleCreateObject,
only one in the player branch — portal arrival is TeleportPlayer, not
enter_world) and the stale "local player never reaches this path"
comment on the generic-remote-render-pose write.

Suite: 11,090 passed / 4 skipped / 0 failed. No new skips, nothing
weakened.

STILL OWED: the connected two-client gate, with
ACDREAM_PROBE_LOCAL_TELEPORT=1, scored only if [local-tp] lines
actually appear in the capture — and explicitly NOT scored as covering
issue #318 (no composition test yet asserts PhysicsEngine.ShadowObjects
directly).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 03:57:37 +02:00

550 lines
29 KiB
Markdown

# C4 route 3 — architecture / adversarial review (2026-08-04)
**Verdict: FAIL.**
Reviewed: the uncommitted working-tree diff (`git diff HEAD` + untracked) on
`claude/acdream-physics-divergence-5aa784` at HEAD **`cd3129e9`** (route 7's
commit, "child cell propagation moves from a render tick into Runtime"). 16
files, +1,573/-216.
Reference documents read in full: the route-3 contract
(`2026-08-04-c4-route-3-contract.md`), the route-3 scoping, the route-2
contract, and the route-5/route-7 review defect classes.
Independent verification performed against source, not against the
implementer's summary: `RuntimeWorldTransitState`, `RuntimeSetPositionState`'s
commit tail, `RuntimeEntityObjectEventStream`/
`RuntimePlacementProjectionSubscription` (publication synchronicity),
`RuntimePlacementPresentationSink` + `LiveEntityRuntime
.TryApplyRuntimePlacementPlace`, `LocalPlayerShadowSynchronizer`,
`LocalPlayerProjectionController`, `TeleportAnimSequencer`, and both hosts'
`Advance()` pump sites. `dotnet build AcDream.slnx -c Debug` exits 0.
**The `_pendingDestination` fix — the item flagged as highest risk — is
correct, and is not the reason for the FAIL.** See §"Judgment on the
`_pendingDestination` lifetime" at the end.
The FAIL rests on A1 and A2: the slice adds five new ways for the Place edge
to refuse, and the refusal path it hands them to does not stop the teleport
animation stream. One of those five (`DeferredCell`) additionally commits the
placement out of band after the transit has ended, which both splits the body
from presentation and leaves a placement receipt nothing can consume — proof
obligations **P3** (graphical half) and **P4** are undischarged, and D-T2.4's
"the wake path must re-validate the portal authority before committing" is not
implemented at all.
---
## MAJOR
### A1 — A refused canonical Place does not stop the teleport animation; the player is released into the world without ever having been placed
**Severity: MAJOR (FAIL basis).**
`src/AcDream.Core/World/TeleportAnimSequencer.cs:134-142`:
```csharp
case TeleportAnimState.Tunnel:
if (worldReady)
{
evts.Add(TeleportAnimEvent.Place);
Advance(TeleportAnimState.TunnelContinue, enterTunnel: false);
_continueElapsed = 0f;
}
break;
```
`TeleportAnimEvent.Place` is emitted **exactly once**, and the sequencer
advances to `TunnelContinue` in the same statement block, unconditionally and
with no knowledge of whether the consumer's handler succeeded. There is no
path back to `Tunnel`.
`src/AcDream.App/Streaming/LocalPlayerTeleportController.cs:505-526`:
```csharp
case TeleportAnimEvent.Place:
if (!_worldReveal.CanPlacePortalDestination(...)) return;
if (!TryExecuteCanonicalPortalPlacement(sequence))
return; // <- new in this slice
...
_placement.Place(_pendingRotation);
...
_worldReveal.ObserveMaterialized(...);
```
The `return` exits `Tick`, but the sequencer has already left `Tunnel`. Every
subsequent `Tick` therefore runs the rest of the stream with **no placement
and no materialization**:
| next event | what runs |
|---|---|
| `TunnelContinue``TunnelFadeOut` | — |
| `PlayExitSound` (`:532-542`) | `_worldReveal.RevealWorldViewport()` + `_presentation.ExitTunnel()` |
| `FireLoginComplete` (`:543-554`) | `_mode.EnterWorld()`, `_session.SendLoginComplete()`, `_worldReveal.Complete()`, `ResetTransit(clearSession: false)` |
`WorldRevealCoordinator.RevealWorldViewport` only needs a live host projection
(`WorldRevealCoordinator.cs:258-266`) — present. `Complete()` reaches
`RuntimeWorldTransitState.Complete` (`:701-706`), which hits
`FailInvariant("portal-complete-before-materialized")` and returns `false`;
`FailInvariant` (`:934-946`) only increments a counter and logs — it does not
throw and does not stop the caller. `ResetTransit` then runs `EndTeleport()` +
`_worldReveal.Cancel()`.
**Concrete failure scenario.** A `Contention` outcome (route 2's force arm, a
route-7 parent drive, or an earlier park still owns the entity's placement
token at the Place edge) makes `TryExecuteAcceptedPortalArrival` return
`Contention` at `RuntimeAcceptedPositionDriveController.cs:492-501`. The
player watches the portal tunnel finish normally, the world viewport is
revealed, `LoginComplete` is sent to ACE — and the player is standing at the
**pre-teleport position** in the **pre-teleport cell**. ACE has them at the
destination. Every subsequent server broadcast fights the client. Nothing in
the client logs above `[world-reveal] event=invariant-failure` (a `SafeLog`
line), and the D-T8 probe never emits because
`ReconcileAndAcknowledgePortal` never ran.
**Why this is the slice's problem and not inherited.** The pre-existing
`CanPlacePortalDestination` early return (`:506-512`) has the same shape, but
it fires only when the transit no longer owns this reveal — a case where
marching on is at worst redundant, because a newer transit owns the world.
This slice adds **five new refusal causes that all fire while the transit is
perfectly healthy**: `host-token-unavailable`, `NotApplicable`, `Rejected`,
`Contention`, and `DeferredCell` (`LocalPlayerTeleportController.cs:596-625`,
which treats everything except `Committed` as a refusal at `:619`).
**Contract obligations violated.** §4 item 4 ("on every refusal … the transit
remains coherent … never a half-state … no path leaves the player permanently
in portal space with a dead operation"); §4 item 5 ("a refused placement must
NOT … `RevealWorldViewport`, must NOT advance the anim-event stream's terminal
events"); D-T5's Begin-refusal row ("the anim stream stays where it is, so the
NEXT Tick re-attempts the Place edge … 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"); and proof obligation **P4**
verbatim. P4 was to be discharged by reading `TeleportAnimSequencer`. It is
one-shot. No re-attempt driver exists.
**Fix direction.** Two shapes are available without touching the sequencer
(stop condition 2 forbids sequencer timing changes):
1. Make the Place edge idempotent-and-latched at the controller: keep a
`_placementCommitted` flag; on a refusal, do NOT let the stream reach
`PlayExitSound`/`FireLoginComplete` — gate those two cases on the latch and
drive a bounded re-attempt from the same `ready` predicate that produced
the Place event (the contract's stated fallback). A refusal that never
converges must then take the existing transit cancellation
(`ResetTransit(clearSession: false)` — which already cancels the reveal and
restores presentation) rather than a silent world release.
2. Or treat a refusal as an immediate transit cancellation and let the
existing supersession path own recovery. Louder, smaller, and it satisfies
D-T5's "never a silent wedge" — but it needs the user's eyes because it is
user-visible (the portal fails and the player stays put) rather than a
silent desync.
Either way this needs a test: *refused arm → the anim stream does not reach
`RevealWorldViewport`/`FireLoginComplete` with an unplaced body.*
---
### A2 — A `DeferredCell` portal park splits the body from presentation and leaves a placement receipt nothing can consume (P3 undischarged on the graphical host; D-T2.4's re-validation missing)
**Severity: MAJOR.**
`RuntimeAcceptedPositionDriveController.SubmitAndResolvePortal:614-645` parks a
`DeferredCell` outcome into `_pending` carrying the portal authority.
`LocalPlayerTeleportController.TryExecuteCanonicalPortalPlacement:619` returns
`false` for it, so A1's march runs: the stream reaches `FireLoginComplete`,
`ResetTransit` calls `_transit.EndTeleport()` and `_worldReveal.Cancel()`.
The park is still live. `Advance()` is pumped by the graphical host at
`src/AcDream.App/Net/GraphicalSessionEventRoute.cs:117` and
`src/AcDream.App/World/LiveEntityHydrationController.cs:416`. When the
destination landblock's collision generation eventually commits:
- `Advance:763-771` runs `ReconcileAndAcknowledgePortal` — the body moves, the
leash re-arms, autorun cancels, and **one outbound movement event is sent**
seconds after the player was already released into the world at the old
position. Presentation is never told: `_placement.Place` and
`ObserveMaterialized` are unreachable (the anim event is one-shot, A1).
- The commit publishes a `Place` receipt whose `Token.Portal` still names the
ended reveal. `RuntimeWorldTransitState.IsCurrentPlacementAuthority:258-275`
requires `IsCurrentPortalDestination` (`:870-882`), which requires
`_teleportActive` — cleared by `EndTeleport` (`:547-556`). It returns false
**forever**.
- `RuntimePlacementPresentationSink.TryApply:100-106` therefore returns
`false`; `RuntimePlacementProjectionSubscription.OnPlacement:134-136` leaves
the receipt at the FIFO head. Every later placement receipt **for every
entity** is blocked behind it, and `AcceptedPositionDrivePendingCount` never
returns to zero, so `GameWindowLifetime.DisposeGameRuntime` throws on
shutdown.
That is precisely the failure mode P3 exists to rule out ("A receipt nothing
can ever consume or retire is a FIFO wedge"). P3 was discharged only for the
headless *happy path*; the graphical park was not walked.
Independently, **D-T2.4's explicit requirement is not implemented**: "its wake
path must re-validate the portal authority before committing". Neither
`Advance`'s `AwaitingCommitWake` branch (`:763-771`) nor its
`IsPlacementCurrent` re-submit branch (`:813-819`) re-checks the portal
authority — they pass `pending.Portal` straight through.
**Fix direction.** (a) Re-validate the portal authority at both wake points
(`_entityObjects` has no transit handle today — the drive needs a
`Func<bool>`-style currency predicate or the authority passed back through the
transit owner); on failure take `AbandonPending`'s exact shape
(`restoreCancelledPark: true` + `PublishCancellation`) so the park is retired
rather than committed. (b) Independently, A1's fix must prevent the transit
from ending while a portal park is outstanding.
---
## MEDIUM
### A3 — Headless discards the arm's status entirely; a failed placement is silent
`src/AcDream.Headless/Hosting/HeadlessSessionWorldProjection.cs:768-780`:
```csharp
if (_acceptedPositionDrive is not null)
{
var authority = new RuntimePortalPlacementAuthority(...);
_ = _acceptedPositionDrive.TryExecuteAcceptedPortalArrival(destination, authority);
}
```
Two problems. First, the status is discarded: any non-`Committed` outcome
leaves the body unmoved while `TryCompletePortal`
(`RuntimeLiveEntitySessionController.cs:530-585`) proceeds through
`AcknowledgeDestinationReadiness``AcknowledgePortalMaterialized`
`Complete``TerminalProjected``LoginComplete``EndTeleport` — asserting
a materialization that did not happen (§4 item 5) and telling ACE the login
completed. The deleted `ResynchronizeLocalPlayerForPortalArrival` was
unconditional and could not fail this way. Second, the `is not null` guard
means a composition regression that fails to wire the drive silently disables
headless portal placement with no signal at all; the previous code had no such
mode.
**Fix direction.** Treat a non-`Committed` status as a hard failure on this
path (`TryCompletePortal` already throws on every other Runtime refusal —
match that), and make the drive a required constructor dependency for the
production projection.
### A4 — Both inversions are hardcoded; the classifier route facts that encode them are never read
`ReconcileAndAcknowledgePortal(RuntimeEntityRecord, in RuntimeAuthoritativePositionRoute route, in RuntimePortalPlacementAuthority)`
(`RuntimeAcceptedPositionDriveController.cs:667-698`) **never references
`route`**. `PlayerMovementController.CommitCanonicalTeleportFrame:1987-2035`
unconditionally zeroes velocity, runs `StopCompletelyAtPhysicsObjectBoundary`,
`UnStick`/`UnConstrain`, and `RearmConstraintLeashAtCurrentPosition`.
So `route.ZeroVelocity`, `route.ConstrainPhase`, and `route.TeleportHookPhase`
are recorded-not-consumed, even though D-T2.3 pinned "`route.ZeroVelocity` is
honored at the commit". The visible behaviour is correct today only because
the classifier's LocalPlayer-teleport branch happens to agree with the
hardcoded method.
Consequence for test quality: the contract's own §8 item 11 sabotage —
"hardcode the force route onto the portal arm (`ConstrainPhase.None`) → test
5a fails" — **cannot fail**, because no code path reads `ConstrainPhase`. No
test discriminates the classifier from the executor. A future classifier edit
(the classifier is a shared surface routes 2/4b-2/4b-3 also consume) diverges
from behaviour silently.
**Fix direction.** Either consume the route facts in the frame commit (branch
on `ZeroVelocity`/`ConstrainPhase`) and add the discriminating test, or delete
the unused `route` parameter and state explicitly, in the class doc and in
AD-2, that the inversions are enforced by `CommitCanonicalTeleportFrame` and
NOT by the classifier — so the next reader does not trust a route fact that
nothing reads.
### A5 — Contract §8 item 8's committed-receipt presentation suite is missing; invariant 6's "the local-player collision shadow agrees" is asserted nowhere
No test in this diff drives a committed portal placement through the **real**
`RuntimePlacementPresentationSink` plus the suffix.
`tests/AcDream.App.Tests/World/RuntimePlacementPresentationSinkTests.cs` is
untouched; its only portal test
(`PortalPlace_RequiresExactCurrentTransitHostAndSequence:467`) drives a
synthetic token, not a local-player placement. The App teleport tests assert
`harness.Placement.Called` against a *fake* placement.
That missing suite would have surfaced the following latent inconsistency,
which route 3 newly makes reachable on the portal path:
- `RuntimePlacementPresentationSink.TryPublishPlace:226-232` writes
`_localPlayerShadow.Set(entity.Position, entity.Rotation, record.FullCellId)`.
`LocalPlayerShadowState.Set` updates **only the cache** — it never touches
`PhysicsEngine.ShadowObjects`.
- `LocalPlayerShadowSynchronizer.SyncPose:59-70` dedups against that same
cache (`cellId` equal AND position within 1 cm AND orientation within
tolerance ⇒ return without publishing).
- `LocalPlayerProjectionController.Project:102` early-returns for
`PlayerState.PortalSpace`, so the sink's cache write is the last word until
the player re-enters the world.
Net: the first post-arrival `SyncShadow` sees a cache that already claims the
resolved pose and resolved cell, skips, and the player's collision shadow row
is not published at the destination. It self-heals the first time the player
moves more than ~1 cm, so the window is short — but during it, other entities
have no collider for the player at the destination. (The shape is pre-existing
from route 2's force arm, where `Project` runs every frame so the window is
one frame; route 3's portal-space skip widens it.)
**Fix direction.** Write the §8 item 8 suite. Independently, either have the
suffix re-publish the shadow through `LocalPlayerShadowSynchronizer`
(`force: true`) after writing the resolved entity pose, or stop the sink from
writing a "last published" cache entry it did not publish.
### A6 — The one test that discriminated *which* destination gets placed lost its discriminator
`tests/AcDream.App.Tests/Streaming/LocalPlayerTeleportControllerTests.cs`,
superseded-teleport scenario:
```diff
- Assert.Equal(new Vector3(2f), harness.Placement.Position);
+ Assert.True(harness.Placement.Called);
```
The old assertion proved the **second** destination `(2,2,2)` was placed and
not the first `(1,1,1)`. `Assert.True(Called)` cannot distinguish them. That is
exactly the property the `_pendingDestination` caching change puts at risk
(caching an Aim-time value instead of re-reading). Two sibling tests lose the
same class of assertion, and one now contradicts its own name:
`SameLandblockDestination_DoesNotRecenterAndKeepsTranslatedPosition` no longer
asserts any position or cell.
This is the route-5 "tests that assert only negatives"/weakened-successor
defect class. The replacement assertion is **available**: the harness now owns
a real Runtime `RuntimeLocalPlayerMovementState`, so
`Assert.Equal(expected, movement.Controller!.Position)` and `.CellId` are
reachable; the harness simply does not expose `movement`.
**Fix direction.** Expose the Runtime controller on the harness and restore a
positive position/cell assertion in each of the three tests, at minimum in the
superseded-teleport one.
### A7 — Dual-host parity (§8 item 6) is not met; the headless flip is untested
`tests/AcDream.Headless.Tests/HeadlessSessionHostTests.cs:413-424` now
documents that the fixture's projection is built without a drive controller,
so "the canonical portal arm this method now calls is a no-op here by
construction", and defers the headless committed-portal test as "an open item,
not attempted here given this session's time budget".
The contract states plainly: "The headless arm reuses the identical Runtime
entry — dual-host parity is a test obligation, not an aspiration (§8)."
Combined with A3, the entire headless production placement flip — the deletion
of `ResynchronizeLocalPlayerForPortalArrival`, the new `portal` parameter, the
new call — has zero behavioural coverage. The only surviving assertion change
is `CenterCount` 3→2.
---
## MINOR
### A8 — Portal-test cleanup runs at the end of the test body, so the first assertion failure is masked by a teardown throw
`ConvergePortalHost` is invoked as the last statement of each portal test
(`RuntimeAcceptedPositionDriveControllerTests.cs:1273/1380/1423/1519`). If any
earlier `Assert` throws, cleanup is skipped, `StartedRuntime.Dispose()` throws
during unwinding, and C# `using`/`try…finally` lets the finally-exception
**replace** the in-flight one.
The implementer diagnosed their own instance of this correctly (claim 1 —
verified: the mechanism is real and the two corrected assertions were genuine
test bugs, since an accepted `TeleportAdvanced` merge rebases the world frame
onto the destination per #283, and `ReconcileAndAcknowledgePortal` does
legitimately send one movement event). But the pattern remains in the shipped
tests, and it is exactly how a genuine host-projection leak would also present
— which is why it is worth removing rather than remembering.
**Fix direction.** `try { … } finally { ConvergePortalHost(…); }`, or make
`StartedRuntime.Dispose` record non-convergence and assert it explicitly.
`ConvergePortalHost` itself is otherwise sound: it cannot double-release —
`AcknowledgeHostProjection`'s `TerminalProjected` branch removes the record
(`RuntimeWorldTransitState.cs:343-355`) and a second call returns false — and
it cannot leak, because a forgotten call throws at Dispose.
### A9 — Two sources for one teleport sequence
`LocalPlayerTeleportController.cs:613` builds the authority's
`TeleportSequence` from `_transit.ActiveTeleportSequence` (passed in as
`sequence`), while `ClassifyPortalArrival`
(`RuntimeAcceptedPositionDriveController.cs:514-535`) derives its
accepted/prior pair from `destination.TeleportSequence`. They agree today —
`OfferTeleportDestination:490-497` refuses a second destination for an
already-accepted active sequence — but two sources for one fact is the
campaign's "mapping written against one caller's reachable set" shape.
**Fix direction.** Use `destination.TeleportSequence` in both, or assert
equality at the producer.
### A10 — Two documentation statements assert behaviour the code does not have
(a) The new class doc on `LocalPlayerTeleportPlacement`
(`LocalPlayerTeleportController.cs:188-193`) says the sink "snapshots whatever
the entity already holds and writes no pose itself — this is the render
entity's mover". `TryPublishPlace` writes no pose, but the sink's own upstream
call chain does: `RuntimePlacementPresentationSink.TryApply:108`
`LiveEntityRuntime.TryApplyRuntimePlacementProjection:1301`
`TryApplyRuntimePlacementPlace:1386-1420`, which performs
`entity.SetPosition(projection.WorldPosition)`, `entity.Rotation = …`,
`entity.ParentCellId = token.ExactCellId`, **and** `RebucketLiveEntity` — all
before `TryPublishPlace` snapshots. The suffix's entity writes and rebucket are
therefore redundant repeats of a mutation the canonical receipt already made.
This is harmless at runtime today, but it is the "a doc asserting behaviour
the code does not have" class — and the contract carries the same misreading
(§3.4, D-T4, §12.5(b)), so correcting the code comment alone is not enough.
(b) The plan correction in `docs/plans/2026-08-02-placement-cutover.md` says
"`RuntimeAcceptedPositionDriveController`'s portal arm reading it — was
already live before route 3 (from route 2's shared drive controller)". The
portal arm was added by **this** slice. A correction that itself asserts a
false fact is worse than the line it corrects.
### A11 — `PhysicsDiagnostics.LocalTeleportHostKind` is a process-global mutable set from a host
`src/AcDream.Core/Physics/PhysicsDiagnostics.cs` + `HeadlessSessionHost.cs:625`.
Correct under K3/K4 (all sessions in a headless process are headless), and the
doc comment says so — noted only so it is not later mistaken for per-session
state. No action required this slice.
### A12 — `AddSyntheticIndoorCell` is representative enough to pass the gate, and no more
The helper registers a `CellPhysics` with an empty `Resolved` polygon
dictionary, one `PortalInfo(0,0,0)`, and a leaf-only BSP root. It is not shaped
to make a specific assertion pass — it mirrors
`RuntimeSetPositionStateTests.AddSyntheticCell` and its only effect is to make
`PhysicsEngine.IsSpawnCellReady` return true for an indoor cell, which is a
genuine fixture gap (a bare `AddLandblock` passes an empty `CellSurface` list,
so indoor destinations parked `DeferredCell` forever). Accepted.
The caveat: because the destination cell has no geometry, the App-layer tests
prove "the arm returned `Committed`", not "the destination resolved somewhere
sane". That makes A6's missing position assertions more load-bearing, not
less.
---
## Verified correct (checked against source, not taken on report)
- **P1 (the D-T3 duty map).** `RuntimeSetPositionState.cs:5036-5058` calls
`PhysicsObjUpdate.CommitSetPositionContactTransition` unconditionally inside
the canonical commit, and `CommitSetPositionContactPrefix`
(`PhysicsObjUpdate.cs:153-175`) derives `Contact`/`OnWalkable`/
`WaterContact` from the placement result's own `InContact`/`OnWalkable`. Not
re-seeding `TransientState` in `CommitCanonicalTeleportFrame` is correct and
is more faithful than `SetPositionCore`'s unconditional
`Contact|OnWalkable|Active` overwrite, exactly as claimed.
- **P3, happy path, both hosts.** `RuntimeEntityObjectEventStream
.PublishPlacement:164-171` → `EnqueueAndDrain` →
`RuntimePlacementProjectionSubscription.OnPlacement:122-150` is
**synchronous**, inside `CommitCanonical`. Headless's placement therefore
commits and its receipt is consumed inside `PrepareDestination`, strictly
before `AcknowledgePortalMaterialized`/`Complete`/`EndTeleport`. The
receipt-past-`EndTeleport` hazard is discharged for the committed path. (The
residual is A2's park.)
- **Trap T7 / route-2 blast radius.** No portal pending reaches
`SettlePending` or `_newestForce`: the three `pending.Portal.Present` guards
at `:763`, `:794`, and `:820` fence every terminal path, and
`SubmitAndResolvePortal` is a genuine sibling of `SubmitAndResolve` rather
than an overload of it. `git diff` shows **zero** expectation changes in any
force-arm test — the contract's §4 item 8 tripwire is clean.
- **Implementer claim 1 (the teardown throw was a test bug).** Mechanism
verified. Both corrected assertions were genuinely wrong for the stated
reasons, and the `using`-finally exception-replacement is real. See A8 for
the residual.
- **Sabotage B's asymmetry.** Verified structurally:
`PortalProducerInvalidAuthority_ArmDoesNotRunAndNothingMutates` builds an
authority with `RevealGeneration: 0`, which fails
`RuntimePortalPlacementAuthority.IsValid` at
`TryExecuteAcceptedPortalArrival:459` — **before** `ClassifyPortalArrival` is
reached. Forcing the classifier to reject cannot change that test's outcome,
so the 4-of-5 asymmetry is exactly what the code shape predicts. Good
evidence.
- **Register bookkeeping.** AD-42's deletion is justified (its last citation
was D2's two-call `Resolve`+`ResolvePlacement`, which is gone); AD-2's
amendment states the deferred-place adaptation, the T8 tolerance, and the
leash-anchor nuance as D-T9 required; the `:2276` stale comment correction
landed; the 2026-07-16 pseudocode `enter_world` correction landed. Row count
49→48 is consistent.
- **Build.** `dotnet build AcDream.slnx -c Debug` exit 0 at the reviewed tree.
---
## Judgment on the `_pendingDestination` lifetime
**The cached destination's lifetime is correct. I found no way to Place
against a superseded destination, and the Place-time re-read it replaced
protected against nothing.**
The reasoning, checked against source:
1. **The bug was real and total.** `RuntimeWorldTransitState
.TryBeginPortalReveal:159-183` clears `_hasAcceptedDestination` and
`_acceptedDestination` at `:180-181` on success.
`TryGetAcceptedTeleportDestination:522-527` returns `_teleportActive &&
_hasAcceptedDestination`. Since `AimDestination` drives
`TryBeginPortalReveal` through `WorldRevealCoordinator.TryBeginPortal`
(`:742`), the slot is empty at every Place edge. The old re-read could
never succeed — every real portal placement would have refused with
`cause=host-token-unavailable`. Not a stale-destination guard; a hard
failure.
2. **Write/clear is in exact lockstep with `_pendingCell`.**
`_pendingRotation`/`_pendingCell`/`_pendingDestination`/
`_hasPendingDestination` are written together at `:780-783` and cleared
together at `:801-805` in `ResetTransit`. `_pendingCell != 0u` is itself the
`haveDestination` predicate (`:481`), so the two cannot diverge.
`_pendingDestination.Position.ObjCellId` **is** `_pendingCell` by
construction (`Position position = destination.Position;` at `:715`).
3. **A second Aim cannot produce a mismatched pair.** Supersession by a new
F751 goes through `OnTeleportStarted` → `ResetTransit(clearSession: false)`,
which clears all four fields and bumps `_lifetimeGeneration`. Supersession
by a second destination on the *same* sequence is impossible:
`OfferTeleportDestination:490-497` returns `false` once `_destinationAccepted`
is set, and `TryBeginPortalReveal` clears only `_hasAcceptedDestination`,
leaving `_destinationAccepted` latched for the life of the reveal. So the
destination is pinned from Aim to terminal, by the transit itself.
4. **The one torn window fails closed.** `_pendingRevealGeneration` is written
at `:749`, before the `IsCurrentLifetime`/recenter guards at `:750`,
`:757`, `:764`, while the other three are written at `:780-783`. A `false`
return from any of those guards leaves a NEW generation paired with an OLD
cell/destination. Both Place-edge gates then refuse:
`CanPlacePortalDestination(newGen, seq, oldCell)` fails
`IsCurrentPortalDestination`'s `destinationCell == _snapshot.DestinationCell`
check, and `TryRegisterHostProjection(newGen, oldCell)` fails the same
comparison at `RuntimeWorldTransitState.cs:197-209`. Neither can commit a
stale pair. (This tearing predates the slice — `_pendingCell` already had
it; `_pendingDestination` does not worsen it.)
5. **Terminal clearing.** Commit → `FireLoginComplete` → `ResetTransit`.
Session reset / generation reset → `ResetSession` / `ResetGenerationPresentation`
→ `ResetTransit`. Cancellation through `ResetTransit(clearSession: false)`.
A *refused* Place leaves the fields set — but so does `_pendingCell`, and
both Place-edge gates are keyed on the reveal generation, so a retained
value is inert until a new Aim overwrites it or a reset clears it.
The one thing the caching genuinely costs is test coverage, not correctness:
A6 removed the only assertion that could have distinguished a stale cached
destination from a fresh one. Restore it.
---
## Summary
| # | Severity | Finding |
|---|---|---|
| A1 | MAJOR | Refused Place does not stop the anim stream; player released into the world unplaced. P4 undischarged. |
| A2 | MAJOR | `DeferredCell` park commits after `EndTeleport`: body/presentation split + unconsumable Place receipt (P3 graphical half, D-T2.4 re-validation missing). |
| A3 | MEDIUM | Headless discards the arm's status; `is not null` guard silently disables placement. |
| A4 | MEDIUM | `route.ZeroVelocity`/`ConstrainPhase`/`TeleportHookPhase` never read; inversions hardcoded; contract sabotage 3 cannot fail. |
| A5 | MEDIUM | §8 item 8 presentation suite missing; local-player shadow invariant unasserted, and a real cache/publish desync sits behind it. |
| A6 | MEDIUM | Superseded-teleport test lost its destination discriminator; two sibling tests weakened, one now contradicts its name. |
| A7 | MEDIUM | Dual-host parity test obligation (§8 item 6) not met; headless flip untested. |
| A8 | MINOR | Portal-test cleanup outside `finally` masks the first failure behind a Dispose throw. |
| A9 | MINOR | Two sources for the teleport sequence (transit vs destination). |
| A10 | MINOR | Class doc and plan correction each assert behaviour the code does not have. |
| A11 | MINOR | Process-global `LocalTeleportHostKind` (accepted, noted). |
| A12 | MINOR | Synthetic indoor cell is geometry-free — fine as a gate, weak as a placement oracle. |