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>
434 lines
22 KiB
Markdown
434 lines
22 KiB
Markdown
# 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`
|
||
|
||
```csharp
|
||
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:
|
||
|
||
1. `TryAdvancePortalCommit` → `TryExecuteAcceptedPortalArrival` →
|
||
`TryPrepareAndSubmitAuthoredPlacement` → **canonical body commit**
|
||
(`CommitCanonical`, retail `SetPositionSimple` @0x005162B0).
|
||
2. `ReconcileAndAcknowledgePortal` → `CommitCanonicalTeleportFrame`
|
||
(UnStick @0x00514EEE / UnConstrain @0x00514F02 / re-arm @0x0045418A,
|
||
velocity zero @0x004541B4, StopCompletely) — **after** the placement.
|
||
3. `CancelAutoRun()` + movement refresh — the `PlayerTeleported`
|
||
@0x006B32B0 port, **after** the hook tail.
|
||
4. Only then does the sequencer see `worldReady=true`, emit `Place`, and
|
||
run the presentation suffix (`NotifyTeleported`, camera reset,
|
||
reconcile) and `ObserveMaterialized`.
|
||
|
||
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 and `set_velocity` @0x004541B4 run
|
||
in `HandleReceivedPosition` *after* `PlayerPositionUpdated` returns,
|
||
i.e. after `SendMovementEvent` and `set_viewer`. acdream runs both
|
||
inside `CommitCanonicalTeleportFrame`, before them. Nothing reads the
|
||
leash between those points, and `MoveToStatePack` @0x006B4720 packs
|
||
`InqRawMotionState` + `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. after `PlayerTeleported`, 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; retail `set_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.Assert`s 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 literally `return this->autonomy_level != 2`.
|
||
acdream's `RuntimeCharacterState.UsePositionFromServer` (`:122`) is
|
||
`AutonomyLevel != FullAutonomyLevel(2)` — **an exact port.**
|
||
- `CommandInterpreter::SendMovementEvent` @0x006B4680 gates on
|
||
`this->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 inside
|
||
**`SmartBox::HandleCreateObject` @0x00454C80**. ✓
|
||
- `CObjectMaint::CreateObject` is 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 follows `SmartBox::init_player` +
|
||
`CellManager::ChangePosition` in the player branch. ✓
|
||
- The load-bearing negative still holds: the `Position*` overload
|
||
@0x00516310 has exactly those two callers and the `int` overload
|
||
@0x00516170 is reached only from @0x00516327 — `enter_world` is 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 real `LiveEntityRuntime`/`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`
|
||
|
||
```csharp
|
||
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 `Skip` attribute 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
|
||
|
||
1. **N1** — stop inferring commit from `PendingCount`; report a
|
||
portal-specific terminal outcome, and log the two silent
|
||
non-committing `Advance` branches.
|
||
2. **R7** — add the AD register row for the autonomy approximation, or
|
||
thread the raw `AutonomyLevel` through and make it exact. Binding
|
||
project rule; not an implementer judgment call.
|
||
3. **N3** — do not throw on `NotApplicable`; split it from `Rejected`.
|
||
|
||
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.
|