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>
This commit is contained in:
parent
22a5c95400
commit
9966b53174
25 changed files with 4292 additions and 195 deletions
|
|
@ -252,9 +252,70 @@ same commit) → docs/handoff commit. No workarounds; no fused slices.
|
|||
4 (remote Create/Position; delete `RemoteTeleportController`/`Placement`
|
||||
and the inline MoveOrTeleport duplicate), 5 (projectile authoritative),
|
||||
6 (drops + split-recovery marking), 7 (residual pickup/parent/delete
|
||||
polish). — OPEN at the 2026-08-03 handoff.** May land as more than one
|
||||
commit if a route proves large;
|
||||
each sub-landing keeps the full review discipline.
|
||||
polish). — route 2 implementation COMPLETE, pending review, at the
|
||||
2026-08-03 handoff; routes 3/4/5/6/7 remain OPEN.**
|
||||
**Route 2 (ForcePosition) — implemented 2026-08-03, contract:**
|
||||
[`2026-08-03-c4-route-2-contract.md`](../research/2026-08-03-c4-route-2-contract.md),
|
||||
**plan:** [`2026-08-03-c4-route-2-implementation-plan.md`](../research/2026-08-03-c4-route-2-implementation-plan.md).
|
||||
`RuntimeAcceptedPositionDriveController`
|
||||
(`src/AcDream.Runtime/Session/RuntimeAcceptedPositionDriveController.cs`)
|
||||
is the single accepted-Position execution seam for a ForcePosition on the
|
||||
already-live local player; `LocalForcePositionTransaction` and
|
||||
`HeadlessSessionWorldProjection.BlipLocalPlayer` are deleted, and the
|
||||
generic App render-tail is skipped for the local player's ForcePosition.
|
||||
Named behaviour changes (both retail-exact, ISSUES #285): the outbound
|
||||
ack now fires strictly after the canonical commit, and the constraint
|
||||
leash is no longer re-armed on this route (retail's FORCE_POSITION branch
|
||||
never reaches `ConstrainTo`).
|
||||
**Fix round (2026-08-03):** both independent dual reviews (retail-
|
||||
conformance + architecture/adversarial) FAILed the first pass — see
|
||||
[`2026-08-03-c4-route-2-review-findings.md`](../research/2026-08-03-c4-route-2-review-findings.md)
|
||||
for the full R1-R9 list. The critical finding (R1) was that the
|
||||
DeferredCell park could not survive a single ACE broadcast interval in
|
||||
production (`RuntimeEntityObjectLifetime.TryApplyPosition`'s unconditional
|
||||
`Forget` on every accepted Position cancelled it before its collision
|
||||
generation could commit), silently dropping the correction forever;
|
||||
`RuntimeAcceptedPositionDriveController.Advance` now detects the dead
|
||||
watch and re-issues from the entity's current canonical snapshot. R2/R3
|
||||
restored headless's collision re-centering and login-window fallback; R4
|
||||
stopped the force-ack from stealing a receipt the presentation sink had
|
||||
legitimately declined; R5/R6/R9 corrected false doc claims, closed a
|
||||
`_pending`-leak/overwrite gap, and fixed streaming-observer/pose-dirty
|
||||
side effects firing on a declined placement. R7 corrected a fixture bug
|
||||
(a dummy Setup sphere with its centre at the origin) that had been
|
||||
written up as a retail fidelity gain; R8 added App-layer double-write
|
||||
source pins and corrected an overclaimed single-ack test. Full detail:
|
||||
[`2026-08-03-c4-route-2-review-findings.md`](../research/2026-08-03-c4-route-2-review-findings.md).
|
||||
Complete Release solution after the fix round: **10,853 passed / 4
|
||||
skipped / 0 failed** (baseline 10,844/4/0; first pass 10,848/4/0).
|
||||
|
||||
**Acceptance item 2 is NOT met — recorded gap, B2 (2026-08-03 round 2).**
|
||||
An earlier revision of this paragraph claimed R8 "added the App-layer
|
||||
double-write source pins the plan's own acceptance item required". That was
|
||||
a claim of coverage this changeset does not have, and it is corrected here
|
||||
rather than left as the citation a future session trusts (same rule that
|
||||
produced R7). The truth, per the adversarial review:
|
||||
- *First half — "the generic tail no longer double-writes the local
|
||||
player":* **source-pinned, not proven.** The pin is a regex/`Assert.Single`
|
||||
over `LiveEntityNetworkUpdateController`'s source text, so it would still
|
||||
pass if a second write were spelled differently, and **no test exercises
|
||||
the branch** at runtime.
|
||||
- *Second half — "the committed projection is what moves the render
|
||||
entity":* **uncovered at any layer.** No test drives a route-2
|
||||
ForcePosition through `RuntimePlacementPresentationSink` /
|
||||
`TryApplyRuntimePlacementPlace` and asserts the `WorldEntity` actually
|
||||
moved. Given R4 (the force-ack no longer consumes a declined `Place`),
|
||||
this is precisely the seam whose failure mode is silent: the canonical
|
||||
body moves and the render entity stays put.
|
||||
Closing this gap needs an App-layer test that runs the accepted
|
||||
ForcePosition end to end and asserts the render entity's position/cell came
|
||||
from the committed placement receipt — carry it into C5's parity tests or
|
||||
file it before this sub-landing closes.
|
||||
**Not yet done:** both reviews must be RE-RUN on this fixed diff, and the
|
||||
connected (user-gated) acceptance gate this campaign's standing
|
||||
discipline requires, before this sub-landing is considered closed — those,
|
||||
and the commit itself, are next. May land as more than one commit if a
|
||||
route proves large; each sub-landing keeps the full review discipline.
|
||||
- **C5 — legacy deletion + closeout gates — OPEN.** Delete every superseded legacy
|
||||
path; parity tests; exact lifecycle/reconnect + canonical nine-stop
|
||||
connected routes; two-client observation; **user visual matrix** (the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue