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>
22 KiB
C4 route 3 — retail-conformance review, round 2 (delta) — 2026-08-05
Verdict: FAIL — but a near miss. All three round-1 MAJOR retail findings (R1, R3, R8) are genuinely fixed, and the R1 fix is the right shape for the right retail reason. What blocks is small and cheap: one unsound "the park committed" inference that reopens R1's failure mode on a narrow path (N1), one approximation shipped with a code comment instead of the register row the project's binding rule requires (R7), and one transient condition converted into a fatal exception on the headless endurance path (N3).
R4/A5 (the presentation suite) explicitly does NOT block — see §D. That call changed from round 1 because the A10 correction is true: I verified the canonical Place receipt, not the suffix, is the render entity's mover, and both halves of that path already have tests.
Scope: delta against my round-1 report
(2026-08-04-c4-route-3-retail-review.md). Same working tree, HEAD
cd3129e9, uncommitted. Review only.
§A — round-1 findings: disposition
| # | round-1 finding | round-2 status |
|---|---|---|
| R1 | Place one-shot; no re-attempt driver; reveal completes with an unplaced body |
FIXED — §B, and the fix is retail-correct |
| R2 | headless discards the arm's status, acks a materialization that never happened | FIXED — §B.3 |
| R3 | probe's leash field can never read armed |
FIXED — Constraint?.IsConstrained at RuntimeAcceptedPositionDriveController.cs:838 |
| R4 | App presentation suite missing; assertions weakened | PARTIALLY closed, does not block — §D |
| R5 | headless dual-host parity untested | FIXED — HeadlessPortalPrepareDestinationParksThenCommitsOnCollisionGenerationWake |
| R6 | enter_world caller-sweep correction mis-stated the retail record |
FIXED and independently re-verified — §C.4 |
| R7 | retail's movement refresh is autonomy-gated; acdream's was not | PARTIALLY fixed — BLOCKS on register discipline — §C.3 |
| R8 | probe fired only on commit; refusals invisible under the pinned env var | MOSTLY fixed — two App-side causes remain invisible, §E.2 |
| R9 | stale "TryBeginPortal (below)" | FIXED (:751 now reads "above") |
| R10 | AD-131 does not exist |
FIXED → AP-131 |
| R11 | probe hardcoded autorunCancelled: true |
FIXED — reads CancelAutoRun()'s bool |
§B — the R1 fix: correct, and correct for the right retail reason
LocalPlayerTeleportController.cs:527
bool placementReady = dataReady && TryAdvancePortalCommit(sequence);
...
var (_, events) = _presentation.Tick(deltaSeconds, placementReady);
Inverting the readiness feed instead of touching the sequencer is the
right call, for a reason worth recording: it is retail's own shape.
Retail holds the player in portal space on CellManager::blocking_for_cells
and SmartBox::UseTime @0x00455410 runs only CheckPrefetchStatus until
the destination is usable — the hold lives in the readiness predicate,
not in the animation. Feeding the sequencer "the canonical commit has
already happened" rather than "the data is ready" reproduces that hold
without changing a single sequencer timing (stop condition 2 respected).
TeleportAnimSequencer.cs is untouched — confirmed by git diff.
B.1 — arrival ORDER is preserved; Inversion B intact
Verified by tracing the single tick on which the commit succeeds:
TryAdvancePortalCommit→TryExecuteAcceptedPortalArrival→TryPrepareAndSubmitAuthoredPlacement→ canonical body commit (CommitCanonical, retailSetPositionSimple@0x005162B0).ReconcileAndAcknowledgePortal→CommitCanonicalTeleportFrame(UnStick @0x00514EEE / UnConstrain @0x00514F02 / re-arm @0x0045418A, velocity zero @0x004541B4, StopCompletely) — after the placement.CancelAutoRun()+ movement refresh — thePlayerTeleported@0x006B32B0 port, after the hook tail.- Only then does the sequencer see
worldReady=true, emitPlace, and run the presentation suffix (NotifyTeleported, camera reset, reconcile) andObserveMaterialized.
Retail: SetPositionSimple @0x00453924 → PlayerPositionUpdated
@0x00453932 → teleport_hook @0x004538AE → PlayerTeleported
@0x004538B3 → set_viewer @0x004538D5. Same order. Inversion B
(local hook AFTER placement) holds; the gating did not move it.
Two sub-order deltas versus retail, both traced and both unobservable — stated so a future reader does not re-derive them:
- Retail's
ConstrainTo@0x0045418A andset_velocity@0x004541B4 run inHandleReceivedPositionafterPlayerPositionUpdatedreturns, i.e. afterSendMovementEventandset_viewer. acdream runs both insideCommitCanonicalTeleportFrame, before them. Nothing reads the leash between those points, andMoveToStatePack@0x006B4720 packsInqRawMotionState+m_position+ contact + longjump + timestamps — no velocity — so the outbound bytes are identical either way. NotifyTeleported()(teleport_hook's TargetManager teardown) now runs in the presentation suffix, i.e. afterPlayerTeleported, where retail runs the whole hook before it. Purely local; no interaction with the outbound send.
B.2 — the refusal path is now genuinely held, and tested with teeth
RefusedPlace_HoldsTheStreamAndConvergesOnlyAfterContentionClears
(App.Tests) forces a real Runtime Contention by taking the entity's
placement token, drives 100 ticks at 0.1 s — 10 s, roughly 3× the
tunnel's own 2–5 s timing, well past where the pre-fix code fired
FireLoginComplete — and asserts the positive facts: body unmoved,
IsActive, Snapshot.Completed == false, LoginCompleteCount == 0.
It then releases the competing operation and asserts the very next tick
commits at the offered destination. That is a real discriminator, not a
negative-only assertion, and it directly kills R1.
A permanent refusal now holds in the tunnel showing retail's centered
wait cue (_holdSeconds accumulates on !placementReady) until
supersession or session reset — which is exactly what contract D-T5
pinned and §4 item 4 requires.
B.3 — headless
PrepareDestination now throws if no drive is wired, gates the attempt
on _collision.IsReady(destination.CellId), and returns
IsCollisionReady: committed;
RuntimeLiveEntitySessionController.TryAdvancePortalCompletion returns
early on !IsCollisionReady, retried by PumpPortalCompletion from
HeadlessSessionHost.Tick. The materialization ack can no longer
precede a placement. R2 closed.
B.4 — P3 (no wedgeable portal receipt) is now ESTABLISHED
Neither review had closed this. RuntimePlacementProjectionSubscription.OnPlacement
(:122-150) calls _sink.TryApply(in head) synchronously on publish,
so a portal-carrying Place receipt is applied and acknowledged inside the
commit call, while the transit is still active and
IsCurrentPlacementAuthority still holds. This matters most on headless,
where TryAdvancePortalCompletion runs Complete + EndTeleport
synchronously right after the commit and the FIFO is only republished at
the end of the same Tick — had delivery been deferred, that receipt
would have failed the portal gate forever and wedged the placement FIFO
for every entity. It does not. P3 satisfied on both hosts.
§C — retail verification of the round-2 changes
C.1 — A4: the route fields carry retail semantics (with one granularity defect)
Verified in RuntimeAuthoritativePositionRouteClassifier.cs:164-171:
ConstrainAfterRouting => ConstrainPhase is AfterPositionOperation— correct and discriminating. It separates the three real retail cases: force (None, early return @0x0045409D), local teleport (After,ConstrainTo@0x0045418A), local non-teleport (Before,ConstrainTo@0x004541EC). The sabotage the contract's §8 item 11 demanded can now fail as designed.ZeroVelocity— read directly; retailset_velocity@0x004541B4.
N4 (LOW) — RunsTeleportHook gates too much.
RuntimeAcceptedPositionDriveController.cs:795-801 uses
route.RunsTeleportHook (TeleportHookPhase is not None) to gate the
entire CommitCanonicalTeleportFrame call. But that method does far
more than retail's teleport_hook @0x00514ED0 (CancelMoveTo, UnStick,
StopInterpolating, UnConstrain, TargetManager, report_collision_end): it
also resets the render-lerp anchors, publishes UpdateCellId, runs
StopCompletely, resets the input edges, and resets the object clock —
none of which retail conditions on the hook. Retail's SetPositionInternal
@0x00515330 does the frame/cell work unconditionally. Today the portal
route always sets the phase, so there is no live effect; but a future
TeleportHookPhase.None would silently skip the render-root cell publish
— the doorway-FLAP class. Gate only the UnStick/UnConstrain/re-arm
block on the hook phase.
RunsTeleportHook also collapses Before and After into one boolean.
Harmless here because the call site is unconditionally post-commit, but
it means the field cannot express Inversion B by itself.
C.2 — the "sequence" plumbing
TryExecuteCanonicalPortalPlacementCore now takes the authority's
TeleportSequence from _pendingDestination and Debug.Asserts it
equals the transit's ActiveTeleportSequence. Sound: the two can only
diverge through a bug, and RuntimeWorldTransitState.OfferTeleportDestination:495-503
accepts exactly one destination per active sequence
(if (_destinationAccepted) return false;), so the Aim-time snapshot is
unique per sequence by construction. (This also disposes of a hazard I
went looking for: a second destination cannot supersede within a
sequence, and a new sequence routes through OnTeleportStarted →
ResetTransit, which clears _placementCommitted/_awaitingDeferredWake
at :932.)
C.3 — R7: the autonomy gate — the approximation is real and needs its register row (BLOCKING)
The retail reading is now exactly right, and I verified both halves:
CommandInterpreter::UsePositionFromServer@0x006B3B40 (pseudo-C:699506-699510) is literallyreturn this->autonomy_level != 2. acdream'sRuntimeCharacterState.UsePositionFromServer(:122) isAutonomyLevel != FullAutonomyLevel(2)— an exact port.CommandInterpreter::SendMovementEvent@0x006B4680 gates onthis->autonomy_level != 0(@0x006B46BB, pseudo-C:700283).
So !UsePositionFromServer sends at level 2 only; retail sends at levels
1 and 2. The implementer's characterisation is accurate, and the
divergence is currently unreachable — TrySetAutonomyLevel has zero
production callers (only RuntimeCharacterStateTests), so
AutonomyLevel is always 2 and the two gates agree. Retail's own default
is 2 (command_line_autonomy_level = 0x2, pseudo-C :1088429).
That is precisely what an approximation is: correct today, wrong the day
someone wires level 1. CLAUDE.md's register rule is binding and admits no
implementer discretion — "Any commit that introduces a deviation (an
adaptation, an approximation, a stopgap, a 'retail does X but we…') adds
its register row IN THE SAME COMMIT. … A deviation found without a row is
a bug twice over." The shipped disposition is a code comment at
RuntimeAcceptedPositionDriveController.cs:812-824 saying "not
register-worthy on its own (no user-visible#-labeled symptom yet)". A
symptom is not the threshold; a deviation is. Either thread the raw
AutonomyLevel through (the exact port, and the constructor already
takes two optional funcs so the marginal cost is one more) or add the
row. A comment is not the register.
C.4 — R6: the corrected enter_world banner is now accurate
Re-verified independently against the pseudo-C, not merely re-read:
:93797@0x004550EC and:93824@0x00455095 both sit insideSmartBox::HandleCreateObject@0x00454C80. ✓CObjectMaint::CreateObjectis invoked at @0x00454FD8 inside that function — a callee, not the enclosing scope. ✓- @0x004550EC is in the
if (arg3 != this->player_id)non-player branch; only @0x00455095 followsSmartBox::init_player+CellManager::ChangePositionin the player branch. ✓ - The load-bearing negative still holds: the
Position*overload @0x00516310 has exactly those two callers and theintoverload @0x00516170 is reached only from @0x00516327 —enter_worldis not on the portal path.
The banner now states all four facts correctly. This citation is safe for future sessions to cite.
§D — R4/A5: still open, and it does NOT block. Here is why the call changed.
Round 1 rated this MAJOR on the premise (taken from contract §3.4) that
the sink "writes no pose", making the suffix the render entity's only
mover. That premise was wrong, and the A10 correction is right. I
verified LiveEntityRuntime.TryApplyRuntimePlacementPlace
(LiveEntityRuntime.cs:1386-1420): a Place receipt calls
entity.SetPosition(projection.WorldPosition), sets entity.Rotation,
sets entity.ParentCellId = token.ExactCellId, and calls
_spatial.RebucketLiveEntity — commitPose defaults true and is passed
false only for WithdrawalRestored (:1364-1372). The canonical
receipt IS the mover; the suffix's writes are redundant repeats, exactly
as the rewritten class comment now says. The comment is verified true.
With that established, the coverage picture is materially different from round 1:
- the sink's Place-receipt render-entity reframe + rebucket —
covered (
RuntimePlacementPresentationSinkTests.Place_ReframesAndRebucketsExactSidecarWithoutMutatingRuntimePhysics); - the sink's portal gate discriminating — covered
(
PortalPlace_RequiresExactCurrentTransitHostAndSequence, which drives a valid authority and a mismatched sequence); - the suffix's own entity write + destination bucket ordering —
covered (the two
ConcretePlacement_*tests, adapted to the new signature against a realLiveEntityRuntime/WorldEntity/GpuWorldState); - the canonical body pose and cell after an App-driven portal commit
— now covered: the four weakened assertions were restored against
harness.Movement.Controller.Position/.CellId, including A6's superseded-destination discriminator, and the App harness now runs a real Runtime rather than a fake placement.
What remains uncovered is narrower than "the presentation suite was not built": no single test composes teleport-controller → canonical commit → real sink → suffix; the local-player collision shadow after a portal commit (#312's own layer) is unasserted; the T8 overwrite ordering is unasserted; and the refused-Place test asserts body/LoginComplete/IsActive but not the render entity.
That is a genuine gap and it is #312-adjacent, so it must be recorded and closed in C5 — the implementer flagged it explicitly rather than claiming closure, which is the right behaviour. But it is no longer a "nothing asserts the only mover" hole, and it does not block this slice.
§E — new findings
N1 — MEDIUM — PendingCount == 0 does not mean "the portal committed"; R1's failure mode survives on a narrow path
LocalPlayerTeleportController.cs:653-657
if (_awaitingDeferredWake)
{
if (_acceptedPositionDrive.PendingCount != 0)
return false;
_awaitingDeferredWake = false;
_placementCommitted = true; // ← inference
return true;
}
and identically HeadlessSessionWorldProjection.cs:818
(committed = _acceptedPositionDrive.PendingCount == 0;).
PendingCount is _pending is null ? 0 : 1
(RuntimeAcceptedPositionDriveController.cs:330) — a shared, arm-agnostic
counter. Three Advance paths clear a portal pending, and only one
of them commits:
Advance site |
commits? | logs? |
|---|---|---|
:918 completed-wake, authority current |
yes → ReconcileAndAcknowledgePortal |
yes |
:918 completed-wake, authority stale |
body committed by RetryDeferred, suffix skipped |
AbandonedAtWake |
:973 watch died — "most likely a subsequent accepted Position's merge-time Forget" |
no | no line at all |
:1020 !IsPlacementCurrent on the prepare-retry |
no | no line at all |
On the last two the caller latches _placementCommitted = true for a
placement that never happened: the sequencer is released, Place fires,
ObserveMaterialized acks a materialization that did not occur, and the
player is revealed at the origin. That is exactly R1's shape,
re-entered through the deferred door.
Reachability is genuinely low — both hosts now gate the attempt behind
collision readiness (_worldReveal.Evaluate(...).IsReady graphically,
_collision.IsReady(...) headless), so a park is rare — and the
Runtime-layer test PortalDeferredCell_WakeAbandonsInsteadOfReconcilingWhenAuthorityWentStale
proves at least one non-committing convergence exists
(Assert.Equal(0, drive.PendingCount) with no reconcile). Nothing tests
the caller's inference against it.
The fix is small: have the drive report a portal-specific terminal
outcome (it already distinguishes them well enough to log
AbandonedAtWake) instead of the caller inferring commit from a shared
counter. Also give the two silent branches a probe line — under D-T8 they
are portal-arrival attempts that ended.
N2 — LOW/MEDIUM — headless IsUnhydratable is now hardcoded false
HeadlessSessionWorldProjection.cs:874 reports IsUnhydratable: false
unconditionally, where it previously reported !ready. The old value was
itself a conflation (not-yet-resident ≠ unhydratable), so this is not a
regression in meaning — but headless can now never report an
unhydratable destination, so a genuinely unhydratable claim spins in
PumpPortalCompletion forever instead of taking AD-2's stated "loud
unhydratable-placement path". Either derive the real predicate or state
in the method's doc that headless does not model it.
N3 — MEDIUM — a transient headless condition is now fatal
HeadlessSessionWorldProjection.cs:854 throws InvalidOperationException
on the default: arm, which covers NotApplicable as well as
Rejected. NotApplicable is returned by
TryExecuteAcceptedPortalArrival for record.PhysicsBody is null and
for an active initial-Create residence
(RuntimeAcceptedPositionDriveController.cs:243-251) — hydration-race
conditions, not "the reveal is stale". The exception message asserts a
diagnosis ("the reveal itself is stale or the local player has no
canonical body, neither recoverable by waiting") that is true for
Rejected and not established for NotApplicable.
Refusing to fake success is right; converting a possibly-transient
condition into a process-killing throw on the host that must survive
K4's 30-session / two-hour endurance profile is the wrong end of that
trade. Split the arm: throw on Rejected, treat NotApplicable as a
retryable wait with a bounded attempt budget (or a loud log plus
IsCollisionReady: false).
N4 — LOW — RunsTeleportHook over-gates the frame commit (§C.1)
N5 — LOW — the plan-doc correction contradicts itself
docs/plans/2026-08-02-placement-cutover.md now opens "This line was
accurate for both halves at the time it was written", explains that
route 3 added the consumer and producer together, and then closes with
"'Zero producing call sites; the adapter does not exist' was accurate
only for the producer half" — which contradicts the opening sentence.
The substance is right (the type, IsValid, Pending.Portal, the sinks'
gates and BeginAcceptedPlacementCore's gate pre-dated route 3; the arm
did not); the paragraph needs one pass so a future reader can cite it.
§E.2 — R8 residual
LogPortalArrivalAttempt now fires on every Runtime-side exit under the
gate's own ACDREAM_PROBE_LOCAL_TELEPORT, and a live Contention
refusal was observed — R8's substance is closed and §9's gate is now
passable as specified: leash=armed reads from
ConstraintManager.IsConstrained, which ConstrainTo @0x00556240 sets
unconditionally, so a committed arrival prints armed.
Two App-side refusal causes still never reach Runtime and therefore emit
no [local-tp] line: cause=stale-reveal
(LocalPlayerTeleportController.cs:666, the CanPlacePortalDestination
preflight) and cause=host-token-unavailable (:737). Both log through
PhysicsDiagnostics.LogTeleport, gated by the different
ACDREAM_PROBE_TELEPORT. Under the pinned gate environment these are
invisible. Either route them through LogLocalTeleportArrival or add
ACDREAM_PROBE_TELEPORT=1 to §9's environment line.
§F — gate evidence
- Release build: green, 0 warnings / 0 errors.
AcDream.Runtime.Tests: 1,164 passed / 0 failed / 0 skipped.AcDream.Headless.Tests: 85 passed / 0 failed / 0 skipped.LocalPlayerTeleportControllerTests: 21 passed / 0 failed / 0 skipped.- No
Skipattribute in any touched test file. No test weakened: the four round-1 weakenings were restored with stronger targets (the real canonical body rather than the deleted fake's captured argument), and three genuinely new discriminators were added (RefusedPlace_…,PortalCommitted_UnderServerControlSendsNoMovementEvent,PortalDeferredCell_WakeAbandonsInsteadOfReconcilingWhenAuthorityWentStale, plus the headless park test). - Per process rule 3, none of this is evidence of correctness — it is evidence that nothing regressed while the above holes remain.
§G — what must land to pass
- N1 — stop inferring commit from
PendingCount; report a portal-specific terminal outcome, and log the two silent non-committingAdvancebranches. - R7 — add the AD register row for the autonomy approximation, or
thread the raw
AutonomyLevelthrough and make it exact. Binding project rule; not an implementer judgment call. - N3 — do not throw on
NotApplicable; split it fromRejected.
Cheap follow-ups, non-blocking: N2, N4 (gate only the UnStick/UnConstrain/
re-arm block on RunsTeleportHook), N5, and R8's two App-side causes.
Record as a dated, named C5 item: R4/A5's residual — the end-to-end composition test, the local-player collision shadow after a portal commit, and the T8 overwrite ordering.