acdream/docs/research/2026-08-04-c4-route-3-architecture-review-round2.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

411 lines
24 KiB
Markdown

# C4 route 3 — architecture / adversarial review, ROUND 2 (delta) — 2026-08-05
**Verdict: FAIL.**
Reviewed: the uncommitted working tree at HEAD **`cd3129e9`**, +2,570/-232 across
16 files. Round-1 report:
[`2026-08-04-c4-route-3-architecture-review.md`](2026-08-04-c4-route-3-architecture-review.md).
`dotnet build AcDream.slnx -c Debug` exits 0.
**Round-1 findings closed: A4, A6, A9, A10(a).** A1, A2, A3 were addressed with
real design work that is directionally right — the A1 fix in particular
(inverting the readiness feed instead of touching the sequencer) is the correct
architectural answer to a hard constraint, and I want that stated plainly.
**The FAIL is one defect, present symmetrically on both hosts, introduced by
the A1/A3 fixes themselves:** both new "am I committed yet?" gates infer
*commit* from *the drive controller's global pending slot being empty*. That
slot empties on at least three paths that do **not** commit — including the one
the drive's own doc comment names as the *expected* outcome of a park. When it
does, the graphical controller latches `_placementCommitted = true` and the
headless projection reports `IsCollisionReady: true`, and both hosts then march
the full completion sequence against an unmoved body. That is round-1's A1/A3
restored, and on the graphical side it is now *worse*, because
`AcknowledgePortalMaterialized` succeeds where it previously failed its
invariant.
Numbering continues as **B*n*** to avoid collision with round 1.
---
## MAJOR — the FAIL
### B1 — `PendingCount == 0` is not "committed"; both hosts infer commit from a signal that is also set by three non-committing paths
**Severity: MAJOR (FAIL basis). Both hosts. Uncovered by any test.**
Graphical, `src/AcDream.App/Streaming/LocalPlayerTeleportController.cs:646-658`:
```csharp
if (_awaitingDeferredWake)
{
if (_acceptedPositionDrive.PendingCount != 0)
return false;
_awaitingDeferredWake = false;
_placementCommitted = true; // <-- infers commit from "not pending"
return true;
}
```
Headless, `src/AcDream.Headless/Hosting/HeadlessSessionWorldProjection.cs:816-821`:
```csharp
if (_awaitingPortalWake)
{
committed = _acceptedPositionDrive.PendingCount == 0;
if (committed)
_awaitingPortalWake = false;
}
```
`PendingCount` is
`RuntimeAcceptedPositionDriveController.cs:330``_pending is null ? 0 : 1`.
It is (a) **global**, not portal-scoped, and (b) cleared by every terminal path,
committing or not. `Advance()` clears `_pending` at five sites; three of them
run **without** a portal commit:
| site | condition | committed? |
|---|---|---|
| `:975` | *"The watch died — most likely a subsequent accepted Position's merge-time `Forget`"* | **no** |
| `:920` | A2's new abandon-at-wake (`!IsPortalAuthorityCurrent`) | body moved by `RetryDeferred`, **suffix skipped** |
| `:998` | A2's new abandon at the prepare-retry branch (`CancelToken`) | **no — nothing ever placed** |
**The `:975` path is the modal case, not a corner case.** The method's own
doc comment (`:880-892`) says it verbatim:
> `RuntimeEntityObjectLifetime.TryApplyPosition` calls `Forget` on EVERY
> accepted Position for this entity … ACE broadcasts at 5-10 Hz, so a
> `DeferredCell` park surviving past one broadcast interval is cancelled
> before its collision generation can ever commit it — **the exact
> far-destination case the park exists to serve**.
So: park → within ~100-200 ms an ordinary broadcast `Forget`s it → `Advance`
clears `_pending``PendingCount == 0` → both gates declare success.
**Concrete failure scenario (graphical).** Portal to a landblock whose
collision generation has not committed. `TryExecuteAcceptedPortalArrival`
returns `DeferredCell`; `_awaitingDeferredWake = true`. One ACE broadcast
later the park is Forgotten and `_pending` clears. Next `Tick`:
`_placementCommitted = true``placementReady = true` → the sequencer leaves
`Tunnel` and fires `Place` → the `if (!_placementCommitted) return;` guard at
`:557` **passes**`_placement.Place(_pendingRotation)` writes the render
entity from the *unmoved* `controller.Position` and rebuckets to the *source*
cell → `ObserveMaterialized(_pendingRevealGeneration, sequence, _pendingCell)`
**succeeds** (the reveal is still active and current) → `PlayExitSound` reveals
the world viewport → `FireLoginComplete` sends LoginComplete, and
`_transit.Complete(generation)` now **passes** its
`portal-complete-before-materialized` check because materialization was falsely
acknowledged. The player is released into the world standing at the
pre-teleport position, the transit reports a clean completion, and nothing logs
an invariant failure. Round 1's A1 at least tripped
`FailInvariant("portal-complete-before-materialized")`; this does not.
**Concrete failure scenario (headless).** Identical shape:
`PrepareDestination` returns `IsCollisionReady: true`, so
`RuntimeLiveEntitySessionController.TryAdvancePortalCompletion` runs
`AcknowledgeDestinationReadiness``AcknowledgePortalMaterialized`
`Complete``TerminalProjected``LoginComplete``EndTeleport`, and sets
`controller.State = PlayerState.InWorld`, all with the body unmoved. That is
round-1 A3 verbatim.
**Secondary hazard from the same root:** because the slot is global, a portal
park killed by a merge-time `Forget` can be immediately replaced by the force
arm's own `RetainPending` from that same merge
(`TryExecuteAcceptedLocalPosition``RetainPending`). The portal gate then
polls a **ForcePosition** operation, waits for it, and latches "portal
committed" when the force operation settles.
**Why no test caught it.** Both new park tests
(`PortalDeferredCell_ParksThenCommitsExactlyOnceOnTheCollisionGenerationWake`,
`HeadlessPortalPrepareDestinationParksThenCommitsOnCollisionGenerationWake`)
commit the destination's collision generation so the park resolves by
committing. `PortalDeferredCell_WakeAbandonsInsteadOfReconcilingWhenAuthorityWentStale`
asserts the **drive's** behaviour (`Assert.Empty(gameActions)`) and stops
there — it never asks what the *host gate* concludes from the resulting
`PendingCount == 0`. The seam between "the drive retired the park without
committing" and "the host decides the portal is placed" is exactly where the
defect lives and is exactly what no test crosses.
**Fix direction.** Stop inferring. The drive already knows the answer with
certainty — `ReconcileAndAcknowledgePortal` runs on commit and only on commit.
Publish that fact:
- add a portal-commit observable to `RuntimeAcceptedPositionDriveController`
— e.g. `bool TryConsumePortalCommit(long revealGeneration, ushort teleportSequence)`
latched in `ReconcileAndAcknowledgePortal` and cleared on consumption, or an
`Action<RuntimePortalPlacementAuthority>` commit callback supplied at
construction alongside `isPortalAuthorityCurrent`;
- have both host gates consume **that**, keyed on the reveal
generation/sequence they are waiting for, so a force pending, a Forgotten
park, and an abandoned park are all correctly "still not committed";
- give the park a terminal "abandoned" outcome the host can see, so it can
either re-attempt cleanly (`_awaitingDeferredWake = false` and try Begin
again next tick) or converge through the existing transit cancellation
rather than silently succeeding.
**Required tests (both must fail against the current code):**
1. Graphical: park, then kill the park with a merge-time `Forget` (an ordinary
accepted Position — the fixture's `OfferDestination` already performs the
merge) instead of committing the collision generation; drive 100 ticks;
assert `Placement.Called == false`, `Movement.Controller.Position` unchanged,
`Reveal.PortalMaterializationCount == 0`, `Session.LoginCompleteCount == 0`,
`Controller.IsActive == true`.
2. Headless: same, asserting `PrepareDestination` keeps returning
`IsCollisionReady: false` and `controller.State` stays `PortalSpace`.
3. Graphical: park, then let the force arm take the pending slot; assert the
portal gate does not latch when the *force* operation settles.
---
## MEDIUM
### B2 — A2's re-validation does not close the FIFO wedge it was written for; it only narrows reachability
The implementer's own note is accurate and I confirm it: `RetryDeferred`
`CommitCanonical` publishes the `Place` receipt asynchronously, and
`IsPortalAuthorityCurrent` runs only afterwards, inside `Advance`, gating the
*suffix*. But the wedge was never in the suffix — it is in the receipt:
- `Advance:920`'s abandon branch is reached only **after**
`TryPeekAcknowledgedPlacement` succeeded, i.e. the sink **already accepted**
the receipt. On that path there was never a wedge to prevent.
- The wedge path is the one where the sink **refuses**:
`RuntimePlacementPresentationSink.TryApply`
`RuntimeWorldTransitState.IsCurrentPlacementAuthority` false → return false →
`RuntimePlacementProjectionSubscription.OnPlacement` leaves it at the FIFO
head → every later placement receipt for every entity is blocked and the
drive's pending never converges. `IsPortalAuthorityCurrent` never runs on
that path, because `TryPeekAcknowledgedPlacement` never yields.
What the A1 fix *does* buy is reachability: with `placementReady` false during
a park, the sequencer cannot reach `FireLoginComplete`, so the transit no
longer ends underneath an outstanding park in the ordinary flow. The remaining
entries are mid-transit supersession (a second F751 while parked →
`OnTeleportStarted``ResetTransit``EndTeleport`), `ResetSession`, and
`ResetGenerationPresentation` — i.e. exactly the §9 "honest gap" cases.
That is a genuine narrowing and I credit it. It is not closure, and the
consequence remains unbounded (whole-FIFO stall + non-convergent shutdown), so
contract stop condition 4 ("P3 finds a portal receipt no mechanism can consume
or retire — the FIFO-wedge shape changes the design, not the test") still
applies.
**Fix direction.** The retire path has to exist at the receipt, not the suffix.
Either (a) let the sinks treat a `Place` whose entity/versions are current but
whose portal authority is dead as acknowledge-and-ignore (the same shape
`Discard`/`ExecutorCompleted`/`WithdrawalRestored` already use, and for the
same stated reason — refusing wedges the ordered stream), or (b) have
`CommitCanonical` drop a portal suffix it can already see is not current rather
than publishing a receipt nothing can consume. (a) is smaller and matches the
existing precedent in both sinks' own doc comments.
### B3 — Headless `_awaitingPortalWake` is not reset across teleports within one session
`HeadlessSessionWorldProjection.cs:762`. The graphical twin
(`_awaitingDeferredWake`) is cleared in `ResetTransit:932`, which
`OnTeleportStarted` calls — clean. The headless field has no equivalent: it is
cleared only when the poll declares success, and `HeadlessSessionWorldProjection`
is constructed per *session*, not per teleport.
**Scenario.** Teleport 1 parks → `_awaitingPortalWake = true`. Before the next
`PumpPortalCompletion`, a new F751 arrives; `TryCompletePortal` overwrites
`_pendingPortalCompletion` with reveal 2. The next pump calls
`PrepareDestination(reveal 2)`, which takes the **poll** branch left over from
reveal 1 — so reveal 2's placement is never even attempted, and if
`PendingCount` happens to be 0 it is immediately declared committed. This is
B1's inference bug plus a stale latch, on a path that does not require a
Forgotten park.
**Fix direction.** Key the latch to the reveal generation (or clear it in
`BeginTeleport`, which already runs per teleport on this host).
### B4 — Both hosts' retry loops are unbounded and undiagnosable
A refusal loop (`Contention`, stale reveal, or a park that never converges)
now retries forever with no timeout and no terminal path.
- **Graphical**: the player holds in the tunnel with the retail wait cue after
5 s. This is a *modelled* end state — AD-2 already documents "predicate never
satisfies → portal transit remains in the authored tunnel and presents the
centered wait cue" — so an infinite stall is strictly better than round 1's
silent release, and I do not consider it blocking. Two gaps, though: AD-2
attributes that state to streaming/DAT failure only, and now a *placement*
refusal produces the identical user-visible state; and `_holdSeconds` /
`ObserveWait` are now driven by `!placementReady` rather than `!dataReady`,
which is a real semantic change to the wait cue's meaning. The
`[tp-probe] REFUSED cause=…` line does distinguish them in the log — good —
but AD-2 should say so.
- **Headless**: worse, because there is no cue and no bound. A bot whose
destination collision never becomes resident sits in `PlayerState.PortalSpace`
indefinitely, connected and healthy-looking; `PumpPortalCompletion` is
entirely silent. Under K4's 30-session envelope this is an invisible stuck
session. At minimum emit one probe line on the first N retries; ideally bound
the wait and fail loudly.
### B5 — §8 items 8/9/10 (the committed-receipt presentation suite) — **does NOT block**, with conditions
Answering the coordinator's direct question.
**Does not block route 3.** Reasons, in order of weight:
1. The render-entity half now has a *proven mechanism*, not a claim.
`LiveEntityRuntime.TryApplyRuntimePlacementPlace:1394-1402` performs
`entity.SetPosition(projection.WorldPosition)`, `entity.Rotation = …`,
`entity.ParentCellId = token.ExactCellId` and `RebucketLiveEntity`,
synchronously inside `CommitCanonical`, before `TryPublishPlace` snapshots.
That path is shared with route 2's force arm and C3c's first entry and has
existing coverage. Round 2 correctly rewrote the class doc to say so
(round-1 A10(a) closed).
2. The restored `Movement.Controller.Position`/`CellId` assertions prove the
canonical body resolved the offered destination — which is the half route 3
actually changed ownership of.
3. The shadow half is a **pre-existing route-2 defect**, not a route-3
regression: `LocalPlayerShadowState.Set` (written by the sink) updates only
the dedup cache, never `PhysicsEngine.ShadowObjects`, while
`LocalPlayerShadowSynchronizer.SyncPose` dedups against that same cache. It
self-heals on the player's first >1 cm move. Route 3 widens the window (via
`LocalPlayerProjectionController.Project:102`'s PortalSpace early return) but
does not change its kind.
**Conditions — all three, or it does block:**
- It is recorded as an **open issue with a number** (#312's layer / route 2's
B2 gap, now two campaigns old) and carried explicitly into C5's parity-test
scope. Not a comment; a tracked item.
- The connected gate is **not** scored as covering it. The probe fields
(`leash`, `autorun`, `hookTail`) say nothing about the shadow; there is no
visual for it. If the user's session passes, the shadow claim remains
test-verified-nowhere and must be reported that way (the §9 "honest gap"
discipline).
- The register gets one line under AD-2 or a sibling row naming the
cache-without-publish asymmetry, so the next reader does not assume
`LocalPlayerShadowState.Current` means "published".
**Concretely, what the test needs** (in
`tests/AcDream.App.Tests/World/RuntimePlacementPresentationSinkTests.cs`, whose
fixture already owns `BeginPortal` and a `LocalShadow`):
1. **Committed local-player portal Place through the real sink.** Pre-seed the
render `WorldEntity` at a *different* (wire) pose — this is also §8 item 9's
T8-ordering half. Drive a `Place` receipt for the local player carrying a
VALID portal authority. Assert, after: `entity.Position`/`Rotation`/
`ParentCellId` equal the receipt's `WorldPosition`/`Orientation`/
`ExactCellId` (the wire pose did not survive); the spatial bucket moved to
the destination landblock; `LocalShadow.Current` equals the resolved pose;
**and `PhysicsEngine.ShadowObjects` actually holds a row for the player at
the destination cell** — that last assertion is the one that discriminates
cache-only from published, and is the whole point.
2. **Discrimination half.** The same receipt with a stale/superseded portal
authority must be refused **and then retired** — not left at the FIFO head.
This is also B2's regression test.
3. **Refused-Place presentation (§8 item 10).** Already partly covered by
`RefusedPlace_HoldsTheStreamAndConvergesOnlyAfterContentionClears`; extend it
to assert the pre-teleport pose is still the *presented* pose (entity +
world snapshot store), not only that the body is unmoved.
4. **Sabotages that must fail:** remove the sink's `entity.SetPosition` → (1)
fails; leave the shadow write as cache-only → (1)'s `ShadowObjects`
assertion fails; make the stale-authority receipt return `false` forever →
(2) fails.
---
## MINOR
### B6 — `isPortalAuthorityCurrent` should be a required constructor parameter
The coordinator's specific concern, checked: **both production sites wire it**
`SessionPlayerComposition.cs:597-603` and `HeadlessSessionHost.cs:665-671`,
both to `RuntimeWorldTransitState.CanPlacePortalDestination`. Four test sites
do not, which is fine.
The residual is that a null default silently restores the round-1 defect, and
nothing catches a future production site that forgets it — there is no
architecture guard for this the way
`RuntimePhysicsOwnershipTests.ProductionHostsUseSharedPlacementSubscriptionWithoutDirectChannel`
guards the placement channel. With only 2 production + 4 test constructions,
making the parameter required (tests pass `static _ => true`) converts a silent
regression into a compile error for ~6 lines of churn. Same argument will apply
to B1's commit observable.
### B7 — `_placementCommitted` is checked once and never re-validated
`LocalPlayerTeleportController.cs:557`. Round 1's Place handler ran
`CanPlacePortalDestination` immediately before mutating; round 2 moved that
check into `TryAdvancePortalCommit`'s **non-deferred** branch only
(`:668-676`). Once `_placementCommitted` latches, the only guard before
`_placement.Place()` / `ObserveMaterialized` is `IsCurrentLifetime`. If the
reveal is cancelled between the commit and the Place event,
`ObserveMaterialized` refuses (`IsCurrentPortalDestination`) but the
presentation suffix has already run and the stream continues to
`FireLoginComplete` with an unmaterialized reveal. Much less severe than B1 —
the body genuinely is at the destination — but it is the same family. Cheap
fix: keep the `CanPlacePortalDestination` re-check in the Place handler
alongside `_placementCommitted`.
### B8 — A8 (round 1) confirmed still open and confirmed non-blocking
Five portal tests still call `ConvergePortalHost` as the last statement of the
test body rather than in a `finally`, so an assertion failure is still masked
by a `Dispose()` throw during unwinding. Correctly flagged rather than silently
dropped. Test hygiene only — no production effect. It does mean that when B1's
new tests are written, a genuine failure may again present as a teardown throw;
fixing the `try/finally` first would save that debugging round.
### B9 — round-1 A11/A12 unchanged
`PhysicsDiagnostics.LocalTeleportHostKind` process-global (accepted);
`AddSyntheticIndoorCell` geometry-free (accepted — and less load-bearing now
that A6's position assertions are restored).
---
## Closed since round 1
| round-1 finding | status | evidence |
|---|---|---|
| **A4** — route facts unread, inversions hardcoded | **CLOSED** | `RuntimeAuthoritativePositionRoute.RunsTeleportHook:164` / `.ConstrainAfterRouting:170`; `ReconcileAndAcknowledgePortal` reads both; `CommitCanonicalTeleportFrame(bool zeroVelocity, bool rearmConstraintLeash)` branches on them. `ConstrainPhase.None` sabotage now fails `PortalCommitted_MovesBodyArmsLeashOnceCancelsAutorunAndSendsExactlyOneMovementEvent`'s stale-anchor assertion, as the contract's §8 item 11 intended. |
| **A6** — superseded-teleport discriminator removed | **CLOSED** | `NewerStart_ReplacesOldDestinationWithoutReusingIt` asserts `Movement.Controller.Position.X/Y == 2` and the cell; `SameLandblockDestination_…` asserts `(20,30,4)` + `0x20210123`; the Z-vs-X/Y reasoning in the comment is sound. |
| **A9** — two sequence sources | **CLOSED** | The authority now uses `destination.TeleportSequence`, with a `Debug.Assert` pinning it equal to the caller's copy. |
| **A10(a)** — class doc claimed the suffix was the render entity's only mover | **CLOSED** | Doc rewritten to state the writes are redundant repeats of the canonical receipt's mutation, with the ordering cited. |
| **A1** — refused Place did not stop the anim stream | **partially** — the mechanism is right (readiness inversion, sequencer untouched), and the `Contention` path is now correctly held and tested (`RefusedPlace_HoldsTheStreamAndConvergesOnlyAfterContentionClears`). **B1 reopens it for the `DeferredCell` path.** |
| **A2** — no wake re-validation | **partially** — D-T2.4's re-validation now exists and is wired in both hosts and tested; **B2** shows it does not close the FIFO wedge. |
| **A3** — headless discarded the status | **partially** — status now honoured, throws on `Rejected`/`NotApplicable` and on a missing drive, retryable pump added. **B1/B3 reopen it.** |
| **A5** — presentation suite | **still open** — see B5 for the definite blocks/does-not-block answer. |
| **A7** — dual-host parity | **CLOSED** | `HeadlessPortalPrepareDestinationParksThenCommitsOnCollisionGenerationWake` drives a real wired drive end to end and asserts body position, cell, and `PlayerState`. |
| **A8** | open, non-blocking (B8). |
| **A11 / A12** | unchanged (B9). |
## Also verified this round
- `_pendingDestination` lifetime — round-1 judgment stands, not re-litigated.
- The sequencer's own invariants survive the late `worldReady`: `Tunnel` is
explicitly a hold state (*"Hold here until worldReady"*), `TickTunnel` still
runs on the hold path so `CurrentAnimationFrame` keeps advancing into
`TunnelContinue`'s exit window, and `maxForce` at 5 s covers a stale frame.
`worldReady` has exactly one consumer (`case TeleportAnimState.Tunnel`), so
no other transition changed meaning.
- No double-Begin headless: `TryCompletePortal`'s own
`TryGetAcceptedTeleportDestination`/`TryBeginPortalReveal` prefix cannot
succeed twice for one reveal (the destination slot is consumed), and
`_awaitingPortalWake` suppresses a second concurrent `Begin` while parked.
(The stale-latch problem is B3, a different failure.)
- `_placementCommitted` / `_awaitingDeferredWake` are both cleared in
`ResetTransit:931-932`, so the graphical latches do not survive a new F751,
a session reset, or a generation reset.
- Build green.
---
## Summary
| # | Severity | Finding |
|---|---|---|
| B1 | MAJOR | `PendingCount == 0` inferred as "committed" on both hosts; three non-committing paths clear it, including the drive's own documented modal outcome. Reintroduces A1 (graphical, now with a *successful* false materialization) and A3 (headless). Untested. |
| B2 | MEDIUM | A2's re-validation gates the suffix, not the receipt; the FIFO wedge is narrowed (supersession/reset only) but not closed. |
| B3 | MEDIUM | Headless `_awaitingPortalWake` is not reset per teleport; a stale latch can skip reveal N+1's placement attempt entirely. |
| B4 | MEDIUM | Unbounded, undiagnosable retry loops on both hosts; headless has no cue, no bound, and no log. |
| B5 | — | §8 8/9/10 presentation suite: **does not block**, subject to three stated conditions; required test spelled out. |
| B6 | MINOR | Make `isPortalAuthorityCurrent` (and B1's commit observable) required constructor parameters. |
| B7 | MINOR | `_placementCommitted` never re-validated before the presentation suffix. |
| B8 | MINOR | A8 still open (5 tests), confirmed non-blocking. |
| B9 | MINOR | A11/A12 unchanged. |