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>
29 KiB
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:
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:
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):
- Make the Place edge idempotent-and-latched at the controller: keep a
_placementCommittedflag; on a refusal, do NOT let the stream reachPlayExitSound/FireLoginComplete— gate those two cases on the latch and drive a bounded re-attempt from the samereadypredicate 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. - 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-771runsReconcileAndAcknowledgePortal— 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.PlaceandObserveMaterializedare unreachable (the anim event is one-shot, A1).- The commit publishes a
Placereceipt whoseToken.Portalstill names the ended reveal.RuntimeWorldTransitState.IsCurrentPlacementAuthority:258-275requiresIsCurrentPortalDestination(:870-882), which requires_teleportActive— cleared byEndTeleport(:547-556). It returns false forever. RuntimePlacementPresentationSink.TryApply:100-106therefore returnsfalse;RuntimePlacementProjectionSubscription.OnPlacement:134-136leaves the receipt at the FIFO head. Every later placement receipt for every entity is blocked behind it, andAcceptedPositionDrivePendingCountnever returns to zero, soGameWindowLifetime.DisposeGameRuntimethrows 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:
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-232writes_localPlayerShadow.Set(entity.Position, entity.Rotation, record.FullCellId).LocalPlayerShadowState.Setupdates only the cache — it never touchesPhysicsEngine.ShadowObjects.LocalPlayerShadowSynchronizer.SyncPose:59-70dedups against that same cache (cellIdequal AND position within 1 cm AND orientation within tolerance ⇒ return without publishing).LocalPlayerProjectionController.Project:102early-returns forPlayerState.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:
- 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-5058callsPhysicsObjUpdate.CommitSetPositionContactTransitionunconditionally inside the canonical commit, andCommitSetPositionContactPrefix(PhysicsObjUpdate.cs:153-175) derivesContact/OnWalkable/WaterContactfrom the placement result's ownInContact/OnWalkable. Not re-seedingTransientStateinCommitCanonicalTeleportFrameis correct and is more faithful thanSetPositionCore's unconditionalContact|OnWalkable|Activeoverwrite, exactly as claimed. - P3, happy path, both hosts.
RuntimeEntityObjectEventStream .PublishPlacement:164-171→EnqueueAndDrain→RuntimePlacementProjectionSubscription.OnPlacement:122-150is synchronous, insideCommitCanonical. Headless's placement therefore commits and its receipt is consumed insidePrepareDestination, strictly beforeAcknowledgePortalMaterialized/Complete/EndTeleport. The receipt-past-EndTeleporthazard is discharged for the committed path. (The residual is A2's park.) - Trap T7 / route-2 blast radius. No portal pending reaches
SettlePendingor_newestForce: the threepending.Portal.Presentguards at:763,:794, and:820fence every terminal path, andSubmitAndResolvePortalis a genuine sibling ofSubmitAndResolverather than an overload of it.git diffshows 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_ArmDoesNotRunAndNothingMutatesbuilds an authority withRevealGeneration: 0, which failsRuntimePortalPlacementAuthority.IsValidatTryExecuteAcceptedPortalArrival:459— beforeClassifyPortalArrivalis 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:2276stale comment correction landed; the 2026-07-16 pseudocodeenter_worldcorrection landed. Row count 49→48 is consistent. - Build.
dotnet build AcDream.slnx -c Debugexit 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:
-
The bug was real and total.
RuntimeWorldTransitState .TryBeginPortalReveal:159-183clears_hasAcceptedDestinationand_acceptedDestinationat:180-181on success.TryGetAcceptedTeleportDestination:522-527returns_teleportActive && _hasAcceptedDestination. SinceAimDestinationdrivesTryBeginPortalRevealthroughWorldRevealCoordinator.TryBeginPortal(:742), the slot is empty at every Place edge. The old re-read could never succeed — every real portal placement would have refused withcause=host-token-unavailable. Not a stale-destination guard; a hard failure. -
Write/clear is in exact lockstep with
_pendingCell._pendingRotation/_pendingCell/_pendingDestination/_hasPendingDestinationare written together at:780-783and cleared together at:801-805inResetTransit._pendingCell != 0uis itself thehaveDestinationpredicate (:481), so the two cannot diverge._pendingDestination.Position.ObjCellIdis_pendingCellby construction (Position position = destination.Position;at:715). -
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-497returnsfalseonce_destinationAcceptedis set, andTryBeginPortalRevealclears only_hasAcceptedDestination, leaving_destinationAcceptedlatched for the life of the reveal. So the destination is pinned from Aim to terminal, by the transit itself. -
The one torn window fails closed.
_pendingRevealGenerationis written at:749, before theIsCurrentLifetime/recenter guards at:750,:757,:764, while the other three are written at:780-783. Afalsereturn 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)failsIsCurrentPortalDestination'sdestinationCell == _snapshot.DestinationCellcheck, andTryRegisterHostProjection(newGen, oldCell)fails the same comparison atRuntimeWorldTransitState.cs:197-209. Neither can commit a stale pair. (This tearing predates the slice —_pendingCellalready had it;_pendingDestinationdoes not worsen it.) -
Terminal clearing. Commit →
FireLoginComplete→ResetTransit. Session reset / generation reset →ResetSession/ResetGenerationPresentation→ResetTransit. Cancellation throughResetTransit(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. |