acdream/docs/research/2026-08-04-c4-route-4b-2-round3-correction.md
Erik 7f1c1f5aa6 feat(physics): C4 route 4b-2 — remote far snap through the canonical placement
Flips the SetPositionSimple classification (contact, PlayerDistance >= 96 m) for
remotes onto 4b-1's drive controller and deletes both legacy far blocks, both
duplicated 96f/4f constant pairs, and both `?? Vector3.Zero` fabrications. The
4 m constant now exists exactly once. Teleport and cell-less stay legacy for
4b-3.

Retail: MoveOrTeleport @0x00516330's far branch runs StopInterpolating
@0x005163CB before SetPositionSimple @0x005163D9 and returns 1 @0x005163E8
regardless — the SetPositionError is discarded — so HandleReceivedPosition arms
ConstrainTo @0x00454272 post-move on commit AND on failure. The x87 parity
decode at @0x00516393-@0x0051639E puts exactly 96.0 on the far branch.
SetPositionSimple @0x005162B0 builds flags 0x1012 at @0x005162C4.

Non-commit outcomes still advance the body, because retail's SetPositionInternal
@0x00515BD0 commits the destination via store_position @0x00515CE2 when no cell
resolves. The partition is by STAGE, not heuristic, enforced by an exhaustive
switch: Refused/Contention/NotApplicable/RejectedPreparation store (the placement
never executed); Committed/Deferred/RejectedByPlacement do not (the engine ran
and refused, matching retail's non-storing returns @0x00515CB2 and @0x00515CD5).
Without this a refused far snap froze the remote with an emptied queue.

Also fixes a shipped defect this route made live: ParkDeferred's quiescence parks
withdrew the entity (InWorld=false, clock suspended, residency removed) and were
never restorable, while Forget(restoreCancelledPark: true) runs for every
accepted Position on every entity. The restorable decision now lives inside
ParkDeferred AFTER SnapToCell, reading body.CellPosition.ObjCellId — the value
RestoreParkWithdrawal actually restores at — against every live quiescence
rather than one minimum-OperationId token. The three pre-snap fields are hoisted
into locals because SnapToCell ends with InWorld = true. ParkCollisionResidents
passes restorableOnCancel: false explicitly; the plain unplaceable park is
provably unchanged. RestoreParkWithdrawal re-tests the prefix at restore time so
a retained route-2 park cannot re-admit into a prefix that began quiescing
during the park.

CanAttemptDestination is retained as an OPTIMISATION only, with the two Core
predicates it cannot reproduce written down at the pre-flight, plus the two
properties that depend on it staying there.

Four fix rounds and eight Opus reviews. The slice was fully green at 10,990,
10,997 and 11,004 while containing real defects — a frozen remote pinned as
correct by its own test, a fallback that over-wrote on the exact retail paths
that decline to store, and a park guard incomplete on two independent axes.

Register: AP-137 (leftover classifications take AP-87's catch-up; states the
cell-less enqueue-vs-place delta deferred to 4b-3, that RejectedData is applied
anyway, and the headless divergence), AP-138 (the refusable far placement),
AP-136 narrowed to match the relocation. #309's acceptance steps rewritten —
step 5 previously asserted a recovery the code does not perform — and gated on a
new ACDREAM_PROBE_PARK=1 signal so the check cannot pass while broken.

Suite 11,009 passed / 4 skipped / 0 failed against a measured 10,968 baseline.
The 10,973 figure recorded earlier was wrong and is corrected here.

Connected gate outstanding: the two-client far-snap walk and #309.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 07:55:56 +02:00

211 lines
12 KiB
Markdown

# C4 route 4b-2 — round 3 correction (2026-08-04)
Both round-2 reviews returned **FAIL**. The findings are now small and local;
this is a closing-out round, not a structural change.
**Do NOT revert the shared-core park change.** Both reviewers verified it removes
a session-permanent strand from routes 1 and 2:
`RuntimeEntityObjectLifetime.cs:1767-1769` calls
`Forget(canonical, restoreCancelledPark: true)` on **every** accepted Position for
**every** entity, so before this round a local-player ForcePosition or a
first-entry placement that hit either quiescence park was left `InWorld = false`,
clock suspended, `FullCellId = 0`, with its operation destroyed by the next
packet. The change is directionally right and fixes shipped behaviour.
## A1 — one relocation closes both shared-core MAJORs
`IsRestorableQuiescencePark` (`RuntimeSetPositionState.cs:3877-3880`) has two
independent defects, and moving the decision fixes both:
1. **It compares against one quiescence, not the set.** It tests
`quiescence.Token.LandblockPrefix` — the token `TryGetBlockingQuiescence`
returned — but both overloads (`:3906-3925`, `:3927-3949`) return the
**minimum-`OperationId`** match, not the only one. `_collisionPrefixQuiescence`
is a per-prefix dictionary (`:589`, `:885`) and streaming opens one per
landblock mutation (activation `RuntimePhysicsState.cs:1820-1824`, retirement
`:2025-2030`), so a recenter has several live. Source `S` quiescing before
destination `D`: the predicate tests `D != S`, returns true, and the restore
re-admits the spatial root into `D`, which *is* quiescing — the AP-136 pin. At
5-10 Hz the park→cancel→restore→re-park cycle can starve `D`'s mutation
permission for as long as `S` stays quiescing (`:927`).
2. **Its input is not the value the restore uses.** It is fed `deferred.CellId` /
`held.CellId` (`:2982`, `:3036`), but `RestoreParkWithdrawal` reads
`body.CellPosition.ObjCellId` (`:3481`), set by `ParkDeferred`'s
`body.SnapToCell` (`:4346-4349`) → `PhysicsBody.StageDormantCellFrame`
(`PhysicsBody.cs:212-223`), which for any outdoor cell runs
`LandDefs.AdjustToOutside`. That function's own doc (`LandDefs.cs:115-122`)
says the resulting cell id **may belong to a neighbour landblock**.
Reachability NOT VERIFIED, and the relocation removes the question.
**Fix: move the restorable decision inside `ParkDeferred`, after `SnapToCell`,
and express it as `!IsCollisionPrefixQuiescing(body.CellPosition.ObjCellId)`.**
That method (`:827-829`) already exists, is `internal`, masks its own argument,
and is the pre-flight's own predicate; it subsumes the current test exactly.
Make it non-static as needed; the `CollisionPrefixQuiescence` parameter then goes
away.
**Blast radius note that must not be lost:** the remote far-snap arm is *masked*
from defect 1 by `CanAttemptDestination`'s destination pre-flight
(`RuntimeRemotePlacementDriveController.cs:599`, `:1155`). The exposed callers are
**routes 1 and 2**, which have no such pre-flight
(`RuntimeAcceptedPositionDriveController.TryExecuteAcceptedLocalPosition:330-392`
goes straight from `ClassifyForcePosition` to `TryBeginExclusiveAuthoredPlacement`).
All three new quiescence tests drive the remote arm and explicitly assert the
**safe** configuration (`:1193`, `:1268`), so none of them can see it.
## A2 — two tests the round is missing
- **Concurrent quiescences**, source `OperationId` < destination `OperationId`,
far snap `S → D`: assert the record is not re-admitted as a spatial root in `D`,
or that `D`'s mutation permission still converges.
- **A route-2 local-player ForcePosition** parking at `:2948` under a quiescing
source: assert `body.InWorld && record.ObjectClock.IsActive &&
record.FullCellId != 0` after the next packet's merge-time `Forget`.
## A3 — say "blocking prefix", or make it true
`IsRestorableQuiescencePark`'s doc (`:3866-3868`) and AP-136's narrowed row both
claim "the rollback re-admits nothing into the retiring prefix." It re-admits
nothing into **the blocking** prefix, which is a different statement. A1's fix
makes the stronger claim true; if any wording survives it, scope it.
## A4 — #309's acceptance steps are now stale
AP-136 says this needs the two-client connected check in #309, but
`docs/ISSUES.md` is **unmodified in this diff**. #309's steps (`docs/ISSUES.md:82-95`)
describe only "a landblock the observer has not streamed" the plain
unplaceable park. They do not exercise a quiescing **source** or a quiescing
**swept neighbour**, which are exactly the shapes this round newly makes
restorable, and step 4 asks only that route 2's corrections "still land
unchanged" without naming the local-player-parks-into-a-quiescing-landblock case
that is now behaviourally different. **Deferring the run is fine; leaving its
scope stale is not.** Update the steps in this commit. Also AP-136's Risk column
still says "a remote" the local player now traverses the same restore.
## A5 — record the dependency at `CanAttemptDestination`
N3's "a retained retry can no longer strand" property and MAJOR 1's masking
**both** depend on the pre-flight staying in place. The class doc calls it "an
optimisation" with no mention of either. If it is ever deleted as redundant, both
re-open silently. Write that down at the pre-flight.
(The round's stated reason for declining N3's `CurrentCellId` re-check that it
would "re-derive a private Core predicate outside Core" is false;
`CanAttemptDestination` already calls Core's own `internal`
`IsCollisionPrefixQuiescing`. The conclusion still holds, for the reason above.
Correct the reason.)
## B1 — a 36-line doc block was detached from its method
`RuntimeRemotePlacementDriveController.cs:1009-1085`. `CountLivePending()` was
inserted **between** `CountLiveAwaitingAcknowledgement`'s existing doc and its
method. Now `:1009-1044` (which describes pruning `_awaitingAcknowledgement`, the
**second** ownership registration, and `_awaitingAcknowledgementScratch`)
documents `CountLivePending()` at `:1066` which prunes `_pending`, backs the
**first** registration, and uses `_pendingScratch`. Every specific is wrong for
the member it now sits on. `:1045-1065` is a second `<summary>` on the same
member, and `CountLiveAwaitingAcknowledgement()` at `:1085` has **no** doc.
## B2 — the stage rule is true by coincidence and stated as a falsehood
`:172-188` and `:993-1005` assert that `RejectedByPlacement`'s producers are
"returned AFTER `_physics.Engine.SetPosition` ran". `RuntimeSetPositionStatus.Rejected`
has **three** producers and only one is post-engine:
| site | what | stage |
|---|---|---|
| `:2854` | entry validation (`!ownsToken`, stage mismatch, null body, stale `IsPreparationAuthorityCurrent`, velocity-version mismatch) | **pre-engine** |
| `:2945` | `!IsStructurallyValid(canonicalRequest)` | **pre-engine** |
| `:3044` | `!result.IsSuccessful` after the engine ran at `:2997` | post-engine |
Both pre-engine sites reach `SubmitAndResolve`'s `default:` and are filed
`RejectedByPlacement` **no store** contradicting the round's own rule
(`RejectedPreparation`'s doc `:128-142`) that a producer which never reached the
engine stores.
The reviewer traced both **unreachable today**, but only by duplicated guards:
`PrepareMover` (`:1563-1577`) re-checks everything `:2830-2855` re-checks with
nothing reentrant between; `IsPreparationAuthorityCurrent` (`:5290-5306`) catches
all seven version dimensions one stage earlier as `RejectedAuthority`
`RejectedPreparation`; the `AwaitingCell` divergence needs `DormantLocalActivation`,
which `:1380` forbids for a record with a body; and `:2945` is shadowed by
`PrepareMover:1636`'s identical check.
**Either state in the `default:` comment that `:3044` is the only reachable
producer and why, or thread the stage out of Core so the split is enforced.** As
written the comment says something false about Core the same shape as round 1's
MAJOR A, which was also a false clause in that same comment.
## B3 — the deleted phrase came back
`RuntimeRemotePlacementDriveControllerTests.cs:1191`, an **added** line:
`// The complete pre-flight passes: it reads only the destination.`
`CanAttemptDestination`'s doc in the same diff (`:1111-1113`) says it is
"correcting this comment's earlier 'the complete pre-flight' framing", and the
whole of MAJOR B is that no pre-flight can be complete. The sentence also
contradicts itself in eight words.
## Minor
- **m1** `TryAdoptWireCellAfterRouting`'s new scope paragraph
(`LiveEntityNetworkUpdateController.cs:1128-1143`) enumerates commit and
`RejectedByPlacement` and "every outcome that resolved NO cell". `Deferred` is
in neither: `ParkDeferred:4345-4349` snaps to the parked result cell,
`WithdrawCanonical` zeroes `FullCellId`, and `RestoreParkWithdrawal:3485-3491`
re-commits from `body.CellPosition.ObjCellId` the **swept** cell for the
post-sweep park, not necessarily the wire cell. Behaviour is unaffected (the
suppression keys on the arm); the claim is not.
- **m2** `Contention`'s doc (`:118-127`) names two producers; there are at
least four. `TryBeginExclusiveAuthoredPlacement:1349-1354` also refuses on
`HasRetainedCompletion(key)` ordinary right after a `Committed` far snap
whose projection sink declined and `BeginAcceptedPlacementCore:1404-1418` on
`!_entities.IsCurrent(record)`. Both pre-engine, so the store side is right.
- **m3** the MAJOR D test (`:1516-1552`) reaches the superseded state via
`lifetime.Entities.RemoveActive(record)` and picks `Refused`, which never calls
`CancelToken`. It pins that the guard exists, not that it sits **after** the
synchronous cancellation receipt the defect's actual mechanism.
- **m4** `NotApplicable` via `record.Key is null` (body non-null) stores
(`:576-581` `:800`); the enum doc `:97-101` justifies only the no-body
sub-case. Unreachable in production
(`RuntimeEntityObjectLifetime.cs:600-612`).
- **m5** `ParkCollisionResidents` (`:3689-3696`) relies on `restorableOnCancel`
defaulting false. The old blanket doc was itself the guard against a forgetful
caller; now that it is gone, pass `false` explicitly.
- **n1** `:644` cites classifier `:467` for `StopInterpolating: !nearby`; it is
`:468`. Pre-existing.
- **n2** `ParkDeferred` carries two adjacent `<summary>` blocks
(`:4292-4301`, `:4302-4325`); only the second is emitted.
- **n3** the Runtime test class doc (`:30-33`) asserts every test was
revert-verified, with no per-test mapping in the tree. Either map them or drop
the claim to what the file can show.
## Do not churn — verified across both reviews
Retail decode of `SetPositionInternal` @0x00515BD0 (both storing and non-storing
branches) is exact and matches the enum docs line-for-line. The 7-value switch is
exhaustive with `_ => throw`, and no consumer outside the controller reads the
value for a decision, so the `Rejected` split cannot silently miss a case.
`Deferred`-not-storing is right, with a stronger unreachability proof than the
comment gives (`ParkCollisionResidents:3563-3568` **throws** rather than parking
an entity that already holds an operation). MAJOR D's guard is the correct
predicate (`RuntimeEntityDirectory.cs:75-77`), sufficient, and correctly placed
after the receipt at both sites. **N5 is genuinely unconstructible**
`:917-923`/`:935-940` throw on binding a component whose body differs from
canonical. Pose parity survives the `remote.Body` `record.PhysicsBody` change
byte-for-byte. The neighbour test's construction is arithmetically verified
(`191.95 mod 24 = 23.95 > 24 - 0.1` `lx+1` `0xB3`), it provably hits the
post-sweep park, and its Z arm discriminates (terrain 7, authored 6). N7's
reframing is accurate. All six required tests discriminate; the
cancelled-after-commit construction is genuinely clever. Route 2's re-issue
funnel does **not** interact with the restore (it decides on
`PositionAuthorityVersion`/`_newestForce`, none of which the restore moves).
`RestoreParkWithdrawal` writes exactly five things and no sweep footprint,
cross-cell registration, or BSP residency among them. **No added line in the diff
carries a source line-number citation** that failure mode is fixed.
## Gate
Complete Release suite. Baseline **10,968 / 4 / 0** at `1b631f12`. Round 2
measured **11,004 / 4 / 0** while containing everything above. Known flakes, do
not chase or conflate: **#302** (`PortalProjectionTests…`, App.Tests), **#308**
(`NakEmissionTests.LossSoak_…`, Core.Net.Tests).