acdream/docs/research/2026-08-03-c4-route-2-review-findings.md
Erik 9966b53174 feat(physics): C4 route 2 — ForcePosition through the canonical placement
A local-player ForcePosition had TWO independent writers for one accepted
packet: LocalForcePositionTransaction snapped the physics body
(PlayerMovementController.BlipPosition, a raw SnapToCell with no collision
resolve), while LiveEntityNetworkUpdateController's generic tail separately
wrote position/cell/rotation to the render WorldEntity from the raw wire and
rebucketed it. Two stores, one packet — the divergence class 670f307c fixed on
the remote path. The outbound AutonomousPosition ack also fired BEFORE any
canonical commit existed: we told ACE "got it, I'm here" before deciding where
"here" was, and the trailing isCurrent() could only suppress the continuation,
never recall the packet.

RuntimeAcceptedPositionDriveController is now the one Runtime-owned seam. Both
hosts call the identical TryExecuteAcceptedLocalPosition; App and headless
project the committed result through the existing placement projection sink
(LiveEntityRuntime.TryApplyRuntimePlacementPlace already performed the same
four writes, from committed state rather than a wire guess).

Retail: SmartBox::HandleReceivedPosition @0x00453FD0's FORCE_POSITION branch is
get_heading -> Frame::set_heading -> SmartBox::BlipPlayer @0x00453940 -> stamp
POSITION_TS -> SendPositionEvent @0x00454091 -> return @0x0045409D. BlipPlayer
is CPhysicsObj::SetPositionSimple @0x005162B0 with flags 0x1012
(Teleport|Slide|SendPositionEvent) — a real collision-resolving SetPosition,
not a snap. The pinned classifier already encoded this exactly.

Named behaviour changes:

* The ack is now an OUTPUT of the committed route, fired strictly after the
  canonical commit and exactly once per accepted force packet.
* The ForcePosition route no longer re-arms the constraint leash. The force
  branch returns at 0x0045409D, ahead of all three ConstrainTo sites
  (0x00454272, 0x0045418A, 0x004541EC); the old re-arm cited retail's "Player,
  normal" branch, which BlipPlayer is not on. The teleport, CommitPreparedPosition
  and first-entry callers legitimately still constrain and are untouched.
* A force correction that terminates WITHOUT committing still sends its
  position event and is not retried — retail's BlipPlayer discards
  SetPositionSimple's SetPositionError return and acks unconditionally.

A single _pending funnel owns the in-flight placement, deciding on the token's
PositionAuthorityVersion against the record's: equal -> clear; advanced with the
newest accepted event still a force -> re-issue, re-classified; advanced to an
ordinary Apply -> clear, since newer server truth owns that pose. This closes a
double-apply/double-ack and a silently-dropped correction that two earlier
iterations of this slice each introduced.

AD-62 records the residual: a ForcePosition our async collision publication
cannot carry to a committed placement is not re-applied. Retail has no park —
its world is fully resident and its placement synchronous — so the state is
unreachable there. AP-131 is NOT retired; its legacy Position caller is route 4.

Deleted: LocalForcePositionTransaction, PlayerMovementController.BlipPosition,
HeadlessSessionWorldProjection.BlipLocalPlayer.

Gates: complete Release solution 10,858 passed / 4 skipped / 0 failed (baseline
10,844/4/0). Two independent Opus reviews (retail-conformance and
architecture/adversarial) PASS on the final diff after three FAIL rounds; every
intermediate state was fully green, so the suite caught none of the four real
defects. Connected acceptance is NOT run: nothing a user can do makes ACE emit
a ForcePosition without retail's @pklite, which acdream does not implement — see
docs/research/2026-08-03-c4-route-2-visual-gate.md.

Known gap, recorded not claimed: the plan's acceptance item 2 is unmet. The App
double-write check is a source pin, and "the committed projection moves the
render entity" is uncovered at any layer (#292). Filed alongside: #286-#291,
#293-#296.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 18:46:36 +02:00

26 KiB

C4 route 2 — dual review findings and required fixes (2026-08-03)

Both mandated reviews returned FAIL on the first implementation pass. Nothing is committed. This is the consolidated fix list; it supersedes the implementer's own closing report where they disagree.

Reviews: retail-conformance (Opus) and architecture/adversarial (Opus), run independently against the same uncommitted diff.

Verified correct — do not churn these

Both reviews independently confirmed, with addresses:

  • AuthoritativeTeleportFlags = Teleport|Slide|SendPositionEvent = 0x1012, byte-exact against CPhysicsObj::SetPositionSimple @0x005162B0.
  • Leash re-arm removal is retail-correct: the FORCE_POSITION branch returns at 0x0045409D, strictly before all three ConstrainTo sites (0x00454272, 0x0045418A, 0x004541EC). The teleport / CommitPreparedPosition / ArmConstraintLeashAtCommittedPlacement callers correctly still constrain.
  • Heading preservation happens exactly once, upstream in InboundPhysicsStateController.ApplyAcceptedPosition:788-798. The seam does not re-apply or drop it.
  • Ack ordering is correct and fires exactly once on both the synchronous and the deferred path; the CanSendPositionEvent gate matches retail's CommandInterpreter::SendPositionEvent @0x006B4770, and correctly does NOT apply ShouldSendPositionEvent's rate limit (retail's force branch calls SendPositionEvent directly).
  • Route-request field derivation matches the continuation executor field for field.
  • Contract items 2, 4 (the Runtime command itself), 6, and 7 pass. AP-131 correctly not retired.
  • The re-modelled PlayerMovementControllerTests are relocations, not weakenings.

Required fixes, in priority order

R1 — HIGH — the DeferredCell park cannot survive in production

RuntimeEntityObjectLifetime.cs:1636 calls Physics.SetPosition.Forget(canonical) on EVERY accepted Position, which unconditionally cancels the entity's in-flight operation. ACE broadcasts at 5-10 Hz, so any park lasting longer than ~100-200 ms is guaranteed to be cancelled before its collision generation commits — exactly the far-destination case the deferred path exists to serve.

Chain: park -> cancelled -> RetryDeferred never runs -> no Place receipt -> ReconcileAndAcknowledge never runs -> the body is never moved and no ack is ever sent. The old LocalForcePositionTransaction / BlipLocalPlayer pair applied the correction synchronously and unconditionally. Retail's BlipPlayer @0x00453940 has no "give up quietly" state at all.

Park-and-hope is not a valid mechanism here. Required direction:

  1. Do not open a park that can never wake. Before submitting, establish that the destination's collision is publishable (R2), and mirror the existing C3c-R1-F7 guard shape (IHeadlessCollisionNeighborhood.IsWithinServiceWindow, HeadlessSessionWorldProjection.cs:20-27) rather than inventing a new one.
  2. Where a park still legitimately occurs, the seam must detect that its operation was cancelled — RuntimeSetPositionState.IsPlacementCompletionTracked (:1245) is the existing read-only query — and re-issue the placement from the current canonical snapshot on the next accepted Position or Advance(). The snapshot already carries the latest accepted pose, so re-issuing is correct, not a replay of stale state.
  3. A force correction must never be silently dropped. That is the retail invariant this route exists to preserve.

Do NOT resolve this with a timeout, a settle window, a retry counter, or by exempting ForcePosition from Forget. If you conclude the correct answer is a deliberate, cited divergence, STOP and report rather than shipping one.

R2 — HIGH — headless lost its destination-collision publication

HeadlessSessionWorldProjection.BlipLocalPlayer (deleted) called _collision.CenterOn(position.LandblockId). The headless collision neighborhood is a hard 3x3 window (BuildPublicationPlan, :462-488) moved ONLY by CenterOn. The surviving callers are spawn (:562), the controller-null login branch (:603), teleport prep (:640) and portal arrival (:683) — a ForcePosition on a live local player now reaches none of them. PumpFirstEntry (:624) polls the stale _requestedLocalPlayerCell, which nothing updates on this path either.

Restore a real mechanism (re-center plus the _requestedLocalPlayerCell update), or gate on IsWithinServiceWindow and handle out-of-window explicitly. The deleted CenterCount assertion in HeadlessSessionHostTests.cs:430 is the invariant; restore it rather than the changed number.

The in-test justification ("retail's BlipPlayer has no streaming-window concept") is true of retail and irrelevant: the window is OUR adaptation, and retail has no equivalent because retail has every landblock resident.

R3 — HIGH — login-window ForcePosition now does nothing at all

RuntimeLiveEntitySessionController.cs:229-254. Previously every accepted local Position ran _worldProjection.ProjectPosition, whose controller-null branch (HeadlessSessionWorldProjection.cs:593-607) set _requestedLocalPlayerCell, called CenterOn, and pumped _firstEntry.DriveAll(). Now a ForcePosition takes the new branch, the drive returns NotApplicable (residence active), and nothing happens.

Restore the pump for the controller-absent case, and delete the comment at :239-240 claiming "there is no legacy fallback to run instead" — there was one; it is the else branch this change routed around.

R4 — MEDIUM-HIGH — the force-ack steals a receipt the sink declined

RuntimeAcceptedPositionDriveController.cs:366-375 unconditionally calls AcknowledgeProjection(outcome.Projection). RuntimePlacementProjectionSubscription deliberately leaves a declined Place at the FIFO head for a later retry (:118-121); this consumes and destroys it.

The sink declines for real production reasons — !_spatial.IsLoaded(landblock) (LiveEntityRuntime.cs:1210-1219) and stale transit authority (RuntimePlacementPresentationSink.cs:90-96). In those cases entity.SetPosition / Rotation / ParentCellId / RebucketLiveEntity / IsSpatiallyProjected are never written, and because the generic tail is now skipped there is no second writer to cover it — the render entity silently stays put while the canonical body moved.

The RuntimeFirstEntryDriveController mirror is safe ONLY because a residence makes the sink decline by design and a follow-up ExecutorCompleted receipt re-binds presentation. Route 2 has no such follow-up. Drop the force-ack and let the subscription's retry contract stand, or provide a real follow-up binding.

R5 — MEDIUM-HIGH — Contention and Rejected silently drop the correction

RuntimeAcceptedPositionDriveController.cs:265-271 returns Contention when TryBeginExclusiveAuthoredPlacement fails; neither status creates a pending entry, and both hosts then return without blipping or acking (LiveEntityNetworkUpdateController.cs:1140). The Contention doc comment promises "a later accepted Position, or this controller's own Advance pump, retries" — the pump provably cannot retry something never recorded, and a later Position carries a different pose. Retail always applies.

The most likely trigger is R1's parked operation, which makes every subsequent ForcePosition Contention. Fixing R1 largely fixes this; the status handling must still not silently drop.

Also fix the Rejected enum doc: it claims "No SetPosition ran; no ack was sent", which is false at the two SubmitAndResolve sites (:361, :415) where a SetPosition ran and was cancelled.

R6 — MEDIUM — _pending leaks and can be silently overwritten

RuntimeAcceptedPositionDriveController.cs:289-324. Advance() exits only on record-key release or acknowledged completion. A mid-session cancellation (supersession, lost-cell deadline, ParkCollisionResidents, generation cancel — all route through ForgetPlacementCompletionCore) leaves _pending set forever, so AcceptedPositionDrivePendingCount keeps IsConverged false for the rest of the session. GameWindowLifetime.DisposeGameRuntime:490-498 throws on non-convergence.

Plan §4c required dropping the pending ack on cancellation, supersession, teardown, generation change and reset. Only teardown and reset shipped. Use IsPlacementCompletionTracked. Also: the _pending = null cleanups are all guarded by if (!firstAttempt), and SubmitAndResolve(firstAttempt: true) assigns _pending without inspecting an existing one — make it refuse to overwrite a live pending.

R7 — MEDIUM — the 0.48 fixture, the changed assertion, and the false doc

This is a test-fixture artifact, NOT a production regression. The headless fixture's LoadedSetupCollisionSource returns one sphere (Vector3.Zero, 0.48f) — centre AT the origin, bottom 0.48 m below the feet. The real human Setup 0x02000001 is (0,0,0.475) r=0.48 plus (0,0,1.350) r=0.48 (Ts46SphereListConformanceTests.cs:35-39), so the foot sphere's bottom is origin - 0.005 and a settled origin lands on the floor within 5 mm. The control is in this same changeset: the new Runtime fixture uses the dummy sphere (offset == radius) and asserts the origin lands exactly on the floor (RuntimeAcceptedPositionDriveControllerTests.cs:178).

Required:

  1. Give the headless fixture the retail offset (0f, 0f, 0.475f) r=0.48 and restore the Z == 50f assertion. Changing an assertion to match new output is the plan's own forbidden move.
  2. Delete the false comment at HeadlessSessionHostTests.cs:419-427 — it describes the sphere CENTRE and then asserts it about controller.Position, which is the ORIGIN (PlayerMovementController.cs:304 -> PhysicsBody.cs:153, retail CPhysicsObj::m_position.frame.origin).
  3. Correct the docs/ISSUES.md #285 "retail fidelity gain" paragraph. Retail's BlipPlayer has never lifted the origin by a sphere radius. Left as-is this becomes the citation a future session trusts.

R8 — MEDIUM — acceptance gaps

  • No App-layer test exists proving the generic tail no longer double-writes the local player and that the committed projection is what moves the render entity. The plan's acceptance item 2 is unmet; three App tests were deleted and replaced with a comment. Given R4, this is precisely the seam that is broken.
  • RuntimeAcceptedPositionDriveControllerTests.cs:310-311 asserts acksAfterFirstResolve <= 1, so the test passes with zero acks — the DeferredCell park -> wake -> commit -> single-ack sequence is unverified, while docs/ISSUES.md claims it is covered. Fix the fixture so the contact gate is satisfied and assert exactly one, or state plainly that it is unverified. Do not leave the overclaim in the record.

R9 — LOW — hygiene

  • RuntimeAcceptedPositionDriveController is public sealed with an internal ctor and all-internal members; its template RuntimeFirstEntryDriveController is internal sealed. Make it internal unless the public surface is genuinely required (if it is, say why).
  • PlayerMovementController.cs:632 has a stale <see cref="BlipPosition"/> to a deleted member. Harmless only while GenerateDocumentationFile is off; TreatWarningsAsErrors is on, so it breaks the build the day docs are enabled.
  • HeadlessSessionHost._currentSession is never cleared on teardown.
  • Headless without a content lease leaves the drive controller null, so the ForcePosition and its ack are dropped entirely (HeadlessSessionHost.cs:568-581); previously the ack fired unconditionally.
  • LiveEntityNetworkUpdateController.cs:1140-1155 fires MarkLiveOwnerPoseDirty and ObserveAcceptedLocalPosition for ANY non- NotApplicable status including Rejected and Contention — moving the streaming observer to a landblock we explicitly refused to place into.

Gate

Unchanged: complete Release solution suite, not a focused subset. Pre-change baseline is 10,844 / 4 skipped / 0 failed; the first pass reached 10,848 with the defects above, so a green suite is necessary and demonstrably not sufficient. Both reviews must be re-run on the fixed diff before commit.


ROUND 2 — residuals after the R1-R9 fix round (2026-08-03)

Both delta reviews returned FAIL again. Suite is green at 10,853 / 4 / 0, which again proves nothing. R2, R3, R4, R5, R6, R7 and R9 are confirmed genuinely fixed and must not be churned. Three blocking residuals remain, and two of them are defects in the R1 reissue mechanism itself.

Root of the problem: _pending has no single owner and no single lifecycle rule. Round 1 bolted reissue onto ad-hoc per-branch bookkeeping. B1 wants MORE reissuing, N1 wants LESS, and N2 wants reissue to be a DIFFERENT route — they look contradictory only because there is no unifying rule. There is one.

The unified mechanism (implement exactly this — it replaces the ad-hoc rules)

RuntimeEntityPlacementToken already carries PositionAuthorityVersion (RuntimeSetPositionState.cs:50). Make that the single decision input.

One rule: the drive owns at most one in-flight placement for the local player. After any terminal outcome, and on every Advance(), compare the committed/parked token's PositionAuthorityVersion against the live record's current PositionAuthorityVersion:

  • Equal — the canonical accepted authority has not moved since this operation began. Nothing is outstanding. Clear _pending. Do not reissue.
  • Advanced — a newer accepted Position arrived while we were in flight, and it may have been the thing that killed our operation. Consult the newest accepted event's disposition (do NOT reuse stale.Route):
    • still ForcePosition — reissue, re-classifying from the current record.
    • now an ordinary Apply — clear _pending and do NOT reissue. The correction was superseded by newer server truth; the ordinary route owns that pose. This is not a silent drop: retail applies each event as it arrives, and a force correction overtaken by a newer position is moot.

Route every terminal branch through one _pending funnel. No branch may assign or clear it directly.

B1 — BLOCKING — a force correction is still silently dropped (conformance)

When a parked operation wakes and its Place is ACCEPTED by the sink, the operation leaves _operations but the completion is retained. CancelCoreDeferred then returns early at RuntimeSetPositionState.cs:5141 without reaching ForgetPlacementCompletionCore, so the retained completion survives. The next ForcePosition hits HasRetainedCompletion (:1319) -> invalid token -> Contention (RuntimeAcceptedPositionDriveController.cs:289-295); both hosts return without placing or acking, and the next Advance() consumes the OLD completion and acks the OLD pose. That packet's correction is lost.

One-frame window on the graphical host only (_session.Tick() inbound dispatch precedes RetryPending() in RetailLiveFrameCoordinator); headless is immune because its pump is adjacent to the readiness check. The DECLINED-Place variant is unaffected and needs no change.

The unified rule fixes this: the new packet advanced PositionAuthorityVersion past the committed token, and the newest event is a ForcePosition, so it reissues.

N1 — BLOCKING — stale _pending causes a second placement AND a second ack

SubmitAndResolve(firstAttempt: true) (:297) never inspects _pending, and only the !firstAttempt branches clear it (:508-511, :482-484, :549-551). So: park -> Forget wipes the watch -> the same packet's ForcePosition Begins cleanly and Commits -> ack fires -> _pending still holds the dead P1 -> next Advance() finds the watch dead -> ReissueFromCanonical -> a second canonical placement and a second outbound AutonomousPosition for one server correction.

That is the double-apply/double-ack class this entire slice exists to delete (670f307c), reintroduced. AssignPending does not catch it because it is only reached on the DeferredCell/retryable branches. The unified funnel fixes it: equal versions -> clear, no reissue.

N2 — BLOCKING — reissue applies force semantics to an ordinary pose

ReissueFromCanonical (:418-447) reuses stale.Route verbatim — SetPositionSimple + Teleport|Slide|SendPositionEvent + SendPositionImmediately: true. But the commonest way a park dies is an ordinary Apply Position, whose retail route (RuntimeAuthoritativePositionRouteClassifier.cs:368-388) is Interpolate/NoPositionOperation, PhysicsSetPositionFlags.None, ConstrainPhase.BeforePositionOperation, SendPositionImmediately: false.

So the reissue converts an ordinary server echo into a hard Teleport|Slide canonical placement, sends an ack retail would never send on that branch, and skips the ConstrainTo the ordinary branch runs. My round-1 direction sanctioned re-issuing THE CORRECTION; it did not sanction re-classifying a different disposition's pose as a force. The unified rule fixes this by consulting the newest accepted disposition.

If any residual divergence remains after this, it needs a docs/architecture/retail-divergence-register.md row in the same commit.

B2 — BLOCKING (record accuracy) — the plan claims coverage it does not have

docs/plans/2026-08-02-placement-cutover.md:287 reads "R8 added the App-layer double-write source pins the plan's own acceptance item required." That is a claim of coverage. The truth, per the adversarial review:

  • acceptance item 2's first half is source-pinned, not proven — the Assert.Single regex would still pass if a second write were spelled differently, and no test exercises the branch;
  • acceptance item 2's second half — "the committed projection is what moves the render entity" — is uncovered at any layer. No test drives a route-2 ForcePosition through RuntimePlacementPresentationSink / TryApplyRuntimePlacementPlace and asserts the WorldEntity moved.

Correct the text to record the gap explicitly. A documented gap is acceptable; a false claim of coverage is not. Same rule that produced R7.

Non-blocking — record, do not fix in this round

  • N3 — headless never calls RetryPending after construction (grep finds no caller outside src/AcDream.App/). R4's fix depends on the subscription's retry, so a declined headless Place would wedge the ordered stream. Latent, not proven reachable. File it.
  • N4Advance() lacks the _driving reentrancy latch its template RuntimeFirstEntryDriveController.DriveAll:128-148 has. No live re-entrant path today. Hygiene.
  • N5CenterOnAcceptedForcePosition does not restore controller.LocalEntityId = record.LocalEntityId ?? 0u (inert today, but an unreplaced deletion); _movementTruthDiagnostics.OnServerEcho no longer fires for a local ForcePosition (diagnostic only).
  • R9 residueConstraintManager.cs:25 and PhysicsBody.cs:442 still cite the deleted BlipPosition in <c> tags (build-safe, but false docs).
  • Route-1 ack — the plan required confirming whether the ack fires while an initial-Create residence owns the record. It does not; SendPositionImmediately is consumed only as a trace fact in the continuation executor (:717, :2576). Not a regression, but the plan said file it. File it.
  • AD register row — the headless 3x3 collision window is now a named member of the Runtime-facing IRuntimeDirectWorldProjection contract with an ordering requirement retail has no analogue for, and no existing row covers it (AD-6 is retired; AD-2 is the graphical reveal barrier). Recommend a row.
  • Stale comment — the HeadlessSessionHostTests comment references "the previous 50.48f assertion", which does not exist at HEAD.

ROUND 3 — final round (2026-08-03)

The round-3 adversarial review returned PASS. The round-3 conformance review returned FAIL on one item. This round closes that item and files the adversarial review's non-blocking findings. The round-2 unified _pending funnel was confirmed sound by both reviewers and was NOT restructured.

The blocker — a terminal-without-commit sent no ack (CLOSED)

SettlePending's Equal branch was reached both by a successful commit and by a terminal outcome that never committed (non-retryable prepare failure, the default: Rejected/Cancelled branch, and both Advance death branches). In the non-commit case the body never moved AND no outbound AutonomousPosition left.

The conformance reviewer proposed a committed flag plus a one-re-issue-per-Advance guard. That is more than retail requires, and the retail evidence was re-verified from docs/research/named-retail/acclient_2013_pseudo_c.txt before coding:

  • SmartBox::BlipPlayer @0x00453940 (line 92528) calls CPhysicsObj::SetPositionSimple(this->player, edi_1, 1) @0x00453968 and discards its return value. BlipPlayer itself returns void.
  • CPhysicsObj::SetPositionSimple @0x005162B0 (line 284276) is declared enum SetPositionError __thiscall. Other retail call sites DO test it — if (CPhysicsObj::SetPositionSimple(...) == OK_SPE) at @0x0055605D and @0x00556021 — which proves the discard in BlipPlayer is deliberate, not a decompiler artifact.
  • SmartBox::HandleReceivedPosition @0x00453FD0's FORCE_POSITION branch calls SmartBox::BlipPlayer @0x00454074, stamps update_times[0] @0x00454079, then runs cmdinterp->SendPositionEvent() @0x00454091 unconditionally and returns @0x0045409D.

Retail's semantics are therefore: attempt the placement once; if it fails the body simply does not move; acknowledge regardless; never retry. No commit flag and no recursion guard are needed to express that.

Implemented. The ack is now sent exactly once per BEGUN placement, at its terminal outcome — from ReconcileAndAcknowledge on the commit path, or from SettlePending on a non-commit terminal. SettlePending gained a positionEventOwed parameter; Pending gained a PositionEventOwed field so the re-issue retry marker (which stands for a packet whose placement was never begun) cannot double-ack. The commit paths pass false because their ack has already left. The non-commit ack runs no reconciliation — the body did not move, so there is no committed frame to reconcile — and therefore carries the body's unchanged pose, which is exactly what retail's ack carries after a failed SetPositionSimple and is informative to the server: its force did not take. The CanSendPositionEvent gate was left untouched; it is retail's own (CommandInterpreter::SendPositionEvent @0x006B4770 tests the transient-state contact bits), so a legitimately airborne body still suppresses the send on both paths.

ReconcileAndAcknowledge was split so both paths share one outbound site, SendPositionEvent.

Other items closed this round

  • AD-62 rewritten. The DeferredCell-park precondition is dropped — it was never required, and the never-parked failure paths reach the same outcome. The row now leads with the general rule and keeps the named shapes as examples, adds the externally-blocked Contention shape (nothing recorded, nothing pumps it) and the other PositionAuthorityVersion advances (TryApplyPickup RuntimeEntityObjectLifetime.cs:1116, CommitPositionChannelUpdate :2041, AdvanceCreateAuthority :2466), and separates the shapes that now lose only the re-apply from the narrower shapes that still lose the ack too.
  • Overstated doc corrected. AcceptedForceObservation's comment claimed the record's current version equals the recorded force's version if and only if the newest accepted event was that force. False — AdvancePositionAuthority has four call sites. It is now stated as the one-way test it actually is.
  • Vacuous assertion deleted. The gameActions.Count <= 2 assertion in Advanced_ReissuesWhenTheNewestAcceptedEventIsStillAForcePosition could not fail: that fixture's CommitLandblockCollision adds both landblocks at worldOffsetX/Y: 0f while the world frame places the deferred landblock at +192/+192, so the body lands over no terrain, InContact is false, and every ack is suppressed — the count is 0. It was also too loose to encode "at most one per packet". Deleted rather than shipped; the test's real discriminators (body position, PendingCount) stay.

Tests

Two added, both with a verified discrimination check (the implementation was temporarily broken in each direction and the intended test observed to fail, then reverted and re-verified):

  • TerminalWithoutCommit_SendsExactlyOnePositionEventAndLeavesTheBodyUnmoved — a park retired without committing sends exactly one AutonomousPosition, performs no placement of its own, and never repeats on further pumps.
  • Committed_SendsExactlyOnePositionEventAcrossTheCommitAndTheSettle — the new settle-side ack does not become a second ack on the committed path.

Two existing tests changed their ack expectation from Assert.Empty to Assert.Single, because they exercise terminal-without-commit paths whose Empty encoded the defect this round removes: Equal_ClearsPendingWithoutReissuingWhenNoNewerAcceptedAuthorityArrived and Advanced_DoesNotReissueWhenTheNewestAcceptedEventIsAnOrdinaryApply. In the latter the single ack belongs to the FORCE packet, not to the ordinary echo — retail's ordinary branch has no unconditional SendPositionEvent.

Measurement note, recorded because it corrects an assumption in this file's round-2 text: the DeferredCell park these fixtures use is the POST-engine quiescence park, so _physics.Engine.SetPosition has already moved the canonical body to the destination while the placement itself is withdrawn and parked. The new test therefore captures its unmoved/no-further-placement baselines at the park, and asserts them across the terminal settle, which is the thing under test.

Filed, not fixed

docs/ISSUES.md #293 (the DeferredCell branch still consumes Withdraw receipts the sink may have declined — the same shape R4 removed for Place), #294 (ReconcileAndAcknowledge runs before the funnel's currency guard on the deferred wake), #295 (the retry marker inflates AcceptedPositionDrivePendingCount, which is an in-flight-placement counter), #296 (a retryable prepare is reported to hosts as Contention, conflating a retained-and-pumping case with a dropped one).

Gate

Complete Release solution suite, unchanged discipline: green is necessary and demonstrably not sufficient — all four prior states were green and three were defective.