Ports retail's missile Position handling into the canonical Runtime
placement owner instead of the deleted ApplyAuthoritativePosition
short-circuit. The Create/residence-window halves of the projectile
pipeline (RuntimeProjectile binding, TryBind's adopted-body branch,
the collision/shadow registration) were already canonical from prior
slices; this closes the remaining gap — how an ACCEPTED Position for
an in-flight missile is classified, placed, and presented.
Byte-decode (Step 1 hard gate, before any code was written):
CPhysicsObj::MoveOrTeleport @0x00516330-0x00516438 disassembled from
the PDB-paired binary (Capstone, x86 32-bit thiscall). `ret 0x10`
establishes four stack args; [esp+0x7c] (arg5, the velocity pointer)
is never referenced in any of the three branches (teleport/near/far).
The retail reviewer independently reproduced this by searching the
whole function body for the `24 7c` mod/rm+disp8 encoding a
`[esp+0x7c]` read would require and found zero occurrences. This
retired a fabricated `?? Vector3.Zero` fallback in the deleted method
— retail's PositionPack::UnPack initializes an absent velocity to
zero and MoveOrTeleport never installs it; the projectile's Vector
channel (RuntimeProjectilePhysicsUpdater.ApplyAuthoritativeVector)
remains the sole velocity authority for a missile. D-P5 in the
contract; the Runtime seam commits no velocity from the Position
packet at all.
The unbound-missile fix: RuntimeEntityObjectLifetime's
ClassifyRemoteAcceptedPosition now derives ProjectileAuthoritative
from a CONJUNCTIVE predicate — the Missile bit AND a bound
RuntimeProjectile whose Body is the canonical PhysicsBody — never the
bit alone. Retail places every non-player CPhysicsObj unconditionally
(there is no missile-specific placement gate in MoveOrTeleport or its
callers), so an unbindable or not-yet-bound missile taking the
ordinary remote tail is retail-faithful, not a fallback: the earlier
bit-only discriminator would have silently frozen it instead.
AP-141 records this as a deliberate, recorded divergence, not
fidelity. Retail mechanically WOULD arm a missile's ConstrainTo leash
on any nonzero MoveOrTeleport return: HandleReceivedPosition
@0x00453FD0's only kind test is player-vs-not, ConstrainTo
@0x00454272 has no kind test of its own, and CPhysicsObj::ConstrainTo
@0x00510520 creates a PositionManager on demand via
MakePositionManager @0x00510523 if one doesn't exist. acdream
deliberately does not construct that EntityPhysicsHost/
PositionManager/InterpolationManager chain for a ballistic body — the
route-5b split the C4 route 5 contract rejected — so a live missile
never shows an armed leash and never catches up via the near/
UnroutedCatchUp policy. This divergence is safe specifically because
ACE never sends UpdatePosition for a missile
(references/ACE/Source/ACE.Server/WorldObjects/WorldObject_Tick.cs:
333-334, SendUpdatePosition() commented out inside the
PhysicsState.Missile branch at :265) — every half of this row is
deterministic-test-gated only, never exercised against a real server.
AP-141 also records the surviving ConstrainTo re-anchor divergence
under clause (b): for the adopted-body case (TryBind's shared-body
branch — an ordinary remote whose Missile bit is set by a later
State packet, so it still carries a live RemoteMotion), acdream now
ports retail's teleport-branch and far-branch StopInterpolating
action (Interp.Clear()), but never re-arms or re-anchors the
inherited ConstrainTo leash the way retail's HandleReceivedPosition
@0x00454254/@0x00454272 does on every nonzero return. The risk
column's earlier wording — that a stale leash "would drag the body
toward a stale anchor" — was wrong and is retracted in this same
commit: ConstraintManager.ConstraintPos is write-only in both retail
and the port (never read by AdjustOffset), and
ConstraintManager::adjust_offset @0x00556180 only tapers or zeroes an
already-composed per-tick offset while InContact — a leash brakes
motion the interp/sticky chain already produced, it cannot pull
anything toward the anchor. The real residual is one tick of un-reset
brake accumulator, contact-gated, and it cannot move an airborne
far-snapped missile at all (the clamp branch does not run while
airborne).
NO CONNECTED GATE EXISTS for this route, by design: ACE never sends a
missile UpdatePosition (see above), so retail's own server never
exercises this code path in play. Every proof obligation here is
test-gated only — Runtime and App-level fixtures constructing the
packet directly — never a live client/server capture.
Three review rounds closed 8 MAJOR findings before this landed:
round 1 (A1 App discarded the seam's status; A2/R1 silent swallow on
an unbound missile; A3/R2 the adopted-body teleport_hook never
wired; A4/A5 zero Runtime/App test coverage); round 2 (a
ParentCellId regression introduced by round 1's own R6 finding,
which the retail reviewer retracted the following round as factually
wrong — the fix here is the REVERT to record.FullCellId, not the
relocation round 1 shipped; B2 the far-branch StopInterpolating skip
never extended to the adopted-body case; residual App/Runtime store-
path coverage; a per-packet closure contradicting the file's own
#315 cached-delegate pattern). Round 3 closed on coverage alone (no
defect): the Advance() retry arm's projectile branch — added at
round 2, semantically reordered at round 2's B5 fix (skip prediction
invalidation on a re-parked Contention, since it writes nothing) —
had never been executed by any test; two new tests drive it directly
and are sabotage-verified against both the reordering and the
retry-arm's own SyncProjectilePresentation call site. The one
recorded defect this campaign produced (the ParentCellId regression)
was caused by complying with a review finding that its own author
later retracted — the standing lesson recorded for future rounds is
that review findings are evidence to re-verify against the code, not
commands to obey unconditionally.
Complete Release suite: 11,063 passed / 4 skipped / 0 failed
(baseline 11,036 at 30d3d114, +27 new tests across this campaign).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
188 lines
12 KiB
Markdown
188 lines
12 KiB
Markdown
# C4 route 5 — architecture / adversarial DELTA review, round 3 (2026-08-04)
|
|
|
|
**Verdict: FAIL — one finding (C1), coverage-only, no identified defect.**
|
|
|
|
Every behavioural finding from rounds 1 and 2 is now closed and independently
|
|
verified. The single remaining item is that the `Advance()` retry arm's
|
|
projectile branch — added in round 2 (R3) and semantically modified in round 3
|
|
(B5, a prediction-invalidation reordering) — is executed by **no test at any
|
|
layer**. I traced the reordering and believe it is safe; but "safe by reading"
|
|
is the standard this project rejects, and it is the standard I applied to the
|
|
implementer twice (A4, A5). The templates to close it are already in the same
|
|
file. **The bar for round 4 is one test.**
|
|
|
|
Gates run for this review: `dotnet build AcDream.slnx -c Release --no-incremental`
|
|
— **0 errors, 21 warnings, all pre-existing and none in any route-5 file** (they
|
|
are in `AcDream.App.Tests/Composition`, `AcDream.Core.Tests` nullability and
|
|
xUnit-analyzer nits; my earlier "0 warnings" readings were incremental builds
|
|
that skipped those projects — no regression). Focused Runtime suites 59/59;
|
|
focused App suites including `UpdateFrameOrchestratorTests` 94/94.
|
|
|
|
---
|
|
|
|
## 1. Finding
|
|
|
|
### C1 — the `Advance()` retry arm's projectile branch has never been executed by a test, and round 3 changed its prediction-invalidation semantics
|
|
|
|
**Severity: MAJOR (coverage). No defect identified — the code reads correct.**
|
|
|
|
`RuntimeRemotePlacementDriveController.Advance:1221-1305` contains the R3
|
|
projectile branch (kind/identity re-validation, `pendingWasInWorld` capture,
|
|
prediction invalidation, `SyncProjectilePresentation`) and the B5 reordering.
|
|
All six `drive.Advance()` call sites in
|
|
`RuntimeRemotePlacementDriveControllerTests.cs` (`:533`, `:549`, `:592`,
|
|
`:1015`, `:1021`, `:1755`) belong to **remote**-kind tests. No test parks a
|
|
`ProjectileAuthoritative` retry and pumps `Advance()`.
|
|
|
|
What is therefore unexercised:
|
|
|
|
- prediction invalidation on a retried projectile placement (trap T3 — a
|
|
straddling quantum clobbering a committed placement is exactly what this
|
|
guards);
|
|
- the B5 semantic change: invalidation **skipped** when `SubmitAndResolve`
|
|
returns `Contention`;
|
|
- `SyncProjectilePresentation` on the retry arm (invariant 2 on a second call
|
|
site);
|
|
- the stale-route guard (`pending.Route.OperationKind` + body/component
|
|
re-validation) that exists precisely because a retry can outlive its
|
|
binding.
|
|
|
|
The R3 comment states the intent plainly — *"Neither invariant this route pins
|
|
… may hold on the direct arm only"* — and both invariants are, in fact,
|
|
asserted only on the direct arm.
|
|
|
|
Reachability: `_pending` receives a projectile entry whenever
|
|
`SubmitAndResolve` returns a retryable preparation status
|
|
(`RetrySetupUnavailable`/`RetryWorldFrameUnavailable`) for a
|
|
`ProjectileAuthoritative` route — an ordinary streaming-edge condition — and
|
|
`Advance()` is the host cadence pump. Standing caveat: like the whole route,
|
|
ACE-unreachable in play.
|
|
|
|
**Fix direction (cheap — both templates are in the same file):** rebuild
|
|
`FarSnap_RetryablePreparation_StoresThePoseAndStillRetainsTheRetry` (`:985`)
|
|
and `Advance_DestinationLeavesTheWindow_StoresTheNewestDestinationPose`
|
|
(`:1707`) against `CreateProjectileRecord` + a `ProjectileAuthoritative` route
|
|
— the same borrowing the projectile ledger test already did from
|
|
`Teleport_LedgerConverges_…`. Assert, on the retry: the pose advanced, the
|
|
prediction version moved on the storing outcomes and **did not** move on a
|
|
re-parked `Contention`, and the shadow row followed the body.
|
|
|
|
---
|
|
|
|
## 2. The B5 reordering — safety verified
|
|
|
|
Asked to scrutinise this specifically. **It is safe**, and for a reason
|
|
stronger than the comment's "single-threaded and synchronous".
|
|
|
|
The invalidation exists solely so a straddling quantum aborts at `Complete`.
|
|
`PredictionAuthorityVersion` has exactly four consumers, enumerated across
|
|
`src/`: `RuntimeProjectilePhysicsUpdater.TryBegin`/`Complete`/`IsIdentityCurrent`,
|
|
`RuntimePhysicsState.CommitProjectileCell`'s `IsExactOwner`, and
|
|
`ProjectileController.IsCurrentQuantumIdentity` (`:837`). The only callers of
|
|
`CompleteQuantum` are `ProjectileController.AdvanceQuantum` (`:760`) and
|
|
`LiveEntityAnimationScheduler` (`:419`) — both frame-loop driven. Nothing in
|
|
the placement pipeline's synchronous publish chain (placement projection sink,
|
|
`RebucketLiveEntity`'s visibility callbacks, collision-report observers) can
|
|
reach either. Notably, the one projection callback that *does* re-enter the
|
|
projectile controller — `OnProjectionVisibilityChanged` → `TryBind` — returns
|
|
early on a retained runtime and never touches a quantum. So no `Complete` can
|
|
interleave between the write inside `SubmitAndResolve` and the invalidate on
|
|
the next statement.
|
|
|
|
The skip on `Contention` is also correct: `Contention` from `SubmitAndResolve`
|
|
means preparation returned a retryable status, so `PhysicsEngine.SetPosition`
|
|
never ran and nothing wrote the body — verified by walking
|
|
`TryPrepareAndSubmitAuthoredPlacement`'s non-`Prepared` path, which touches
|
|
only `operation.*` fields and reads (`body.InContact`/`OnWalkable`), never the
|
|
body's pose.
|
|
|
|
Two ordering details I checked and found sound: the `Refused` branch still
|
|
invalidates **before** `StoreAcceptedDestinationPose` (the one body write on
|
|
that arm); and `CancelToken`'s synchronous cancellation receipt — which the
|
|
class doc warns can delete or replace the incarnation — is harmless here
|
|
because `pendingProjectile` is a captured strong reference (invalidating a
|
|
displaced component is inert, and a displaced component's quantum already
|
|
fails `IsIdentityCurrent`'s `ReferenceEquals(record.Projectile, projectile)`),
|
|
while both `StoreAcceptedDestinationPose` and `SyncProjectilePresentation`
|
|
re-validate currency at entry.
|
|
|
|
Honest note on the invariant's status: contract §5 item 4 says invalidation is
|
|
"before the write". On this arm it is now after, resting on a reachability
|
|
argument rather than structural ordering. The argument holds today; it is
|
|
recorded here so a future change that lets a projection callback drive a
|
|
quantum knows it broke something. The comment states the reasoning openly,
|
|
which is the right handling.
|
|
|
|
---
|
|
|
|
## 3. The indoor staging in the B2 regression test — legitimate, and it corrects my round-2 finding
|
|
|
|
**Judgment: the staging is legitimate, the test genuinely pins production
|
|
behaviour, and the diagnosis behind it is correct.**
|
|
|
|
I verified the mechanism at source. `PhysicsBody.Position`'s setter
|
|
(`PhysicsBody.cs:153-162`) calls `SyncCellPositionDelta(delta)` on every write,
|
|
including `StoreAcceptedDestinationPose`'s. That method (`:287-308`) has two
|
|
branches:
|
|
|
|
- **outdoor** (low word `1..0x40`): `LandDefs.AdjustToOutside` re-derives the
|
|
cell index from the shifted local origin *and* bumps the landblock on a
|
|
192 m crossing — so after a store to a cross-landblock destination the body's
|
|
own cell id **self-corrects to the destination cell**;
|
|
- **indoor** (low word outside that range — an EnvCell id): the local origin
|
|
shifts but the cell id is kept verbatim, so it stays pinned at the source
|
|
EnvCell.
|
|
|
|
So the implementer's finding is right, and it **corrects my round-2 B2**: the
|
|
defect I reported was real but its blast radius was narrower than I stated. My
|
|
stated scenario ("a missile crosses a landblock boundary at the streaming
|
|
edge") is an *outdoor* case, which self-heals through `AdjustToOutside`; the
|
|
genuinely divergent case is a body in an EnvCell — a bolt fired inside a
|
|
dungeon. That correction is recorded here rather than smoothed over.
|
|
|
|
The staging is not a fixture artefact:
|
|
`IndoorSourceCell = SourceLandblock | 0x0100` is a well-formed EnvCell id (the
|
|
first EnvCell index), and `SnapToCell` seeds indoor claims verbatim by design
|
|
(`PhysicsBody.cs:190-205`, "Indoor EnvCell claims (low word >= 0x100) … seeded
|
|
verbatim"). The scenario — indoor body, outdoor wire destination, service
|
|
window refuses — is production-reachable.
|
|
|
|
One asymmetry worth naming, which does not affect the pin: the test stages the
|
|
indoor cell on the *body* while leaving `record.FullCellId` at the outdoor
|
|
`SourceCell`, a pairing production would not produce. It is inert here because
|
|
the merge overwrites `record.FullCellId` with the wire cell before the ack
|
|
runs, so the pre-packet value is never read. The assertion depends only on the
|
|
two expressions diverging, which they genuinely do.
|
|
|
|
Discrimination confirmed by construction: the test asserts
|
|
`body.CellPosition.ObjCellId == IndoorSourceCell` (the divergence actually
|
|
occurred) and then `entity.ParentCellId == DestinationCell`. Restoring round
|
|
1's `body.CellPosition.ObjCellId` read fails the second assertion.
|
|
|
|
---
|
|
|
|
## 4. Round-2 findings — disposition
|
|
|
|
| # | status | verification |
|
|
|---|---|---|
|
|
| **B1** (adopted-body far snap leaves `Interp` armed; AP-141 asserts the opposite) | **CLOSED, well** | The clear landed in the Runtime seam at `ApplyAcceptedProjectilePosition`'s `SetPositionSimple` case, **before** the placement — matching retail's @0x005163CB-before-@0x005163D9 ordering — guarded on `route.StopInterpolating && record.RemoteMotion is RemoteMotion`, the exact shape `ApplyAcceptedRemoteFarSnap` ports. Correctly scoped to one action, not the full hook. `MissileAdoptedBody_FarCommit_ClearsInterpQueueButLeavesConstraintArmed` is discriminating in both directions: it pins the queue clear **and** pins that `UnConstrain` did *not* run, proving the fix did not over-apply the teleport hook to the far branch. AP-141 is now narrowed to "faithful ONLY for a BARE missile" and additionally names the surviving `ConstrainTo` re-anchor divergence for the adopted case — a more accurate row than before the finding. |
|
|
| **B2** (`ParentCellId` regression) | **CLOSED, well** | Reverted to `record.FullCellId`; the A1 gate kept. The doc now carries three independent justifications, of which two I verified directly (the sibling remote arm pairs wire pose with wire cell; Runtime's own `SyncProjectilePresentation` publishes the shadow at `record.FullCellId` in the same call, so the body-cell read would have disagreed with the shadow for one body in one packet) and the third is retail's `store_position` @0x00515CE2 writing `objcell_id`. Regression-tested — see §3. |
|
|
| **B3** (store path unasserted at both layers) | **CLOSED at both layers** | Runtime: `Refused_StillAdvancesPoseNoParkPredictionInvalidated` now asserts `shadowEntry.Position == body.Position`; on the `Refused` path nothing but `SyncProjectilePresentation` publishes the shadow, so it discriminates. App: `MissileFarRefused_StorePathStillMovesEntityToDestinationParentCellIdAgreesWithWireCell` asserts entity position and `ParentCellId`; deleting the ack leaves the entity at its spawn pose. What remains untested from B3's neighbourhood is only the A1 gate's own two statuses (`Deferred`/`RejectedByPlacement`), which round 2 already accepted as an unconstructible carve-out, and the retry arm (C1). |
|
|
| **B4** (per-packet closure) | **CLOSED, correctly** | `IsCurrentProjectilePositionOwner` is now a third cached delegate inside `RemoteArmCallbacks`, bound to `IsCurrentProjectileArmPositionOwner`, reading `_projectileArmPosition*` scratch fields stamped immediately before use — byte-for-byte the same shape as the existing `_remoteArmPosition*` / `IsCurrentRemoteArmPositionOwner` pair (`:92-93`, `:1467-1468`, `:1502-1506`). Same reentrancy exposure as the incumbent #315 pattern, therefore parity rather than a new hazard. The `UpdateFrameOrchestratorTests` zero-`Delegate`-field guard holds — it ran green inside the 94/94 App suite, and the cache stayed inside the named type rather than becoming bare fields. |
|
|
| **B5** (retry arm invalidating with no write) | **CLOSED — see §2** | Correct, and the safety of the reordering verified independently. Untested (C1). |
|
|
| Round-1 **MINOR (c)** (4a velocity comment with no retail basis) | **FILED as #317** | `docs/ISSUES.md` — accurate description, correct scoping rationale, and an acceptance criterion that names the actual work (audit the whole accepted-Position velocity chain, then cite or remove with a register row). Better handling than a silent comment fix. |
|
|
|
|
No round-1 or round-2 finding regressed, and nothing in this round's diff
|
|
introduced a new behavioural defect that I could identify.
|
|
|
|
---
|
|
|
|
## 5. What would make this PASS
|
|
|
|
One test: a `ProjectileAuthoritative` retained retry driven through
|
|
`drive.Advance()`, asserting the pose advanced, the prediction version moved on
|
|
a storing outcome and did **not** move on a re-parked `Contention`, and the
|
|
shadow row followed the body. Two existing remote tests in the same file are
|
|
the templates.
|
|
|
|
Nothing else is outstanding.
|