fix(review): close the C5b architecture-review findings (D2/D3/D4, L1-L5, S1)

Follow-up to C5b (735f0a72). The retail-conformance review passed, so no
production behaviour line moved: the flag truth table and the
refreshPosition:false withhold are untouched. This is blast radius, test
discrimination and documentation fidelity - plus two findings I could not
confirm and am rebutting rather than complying with.

D3 - THE PUBLISH-CONSERVATION TEST DID NOT DISCRIMINATE. The reviewer was
right and it was the worst finding here: proof obligation 3's test passed
identically with C5b reverted. Its only delta assertion FILTERED
(Assert.Single(deltas, Rebucketed && parentGuid)), so the pre-C5b stream
[Rebucketed] and the post-C5b stream [Updated, Rebucketed] both satisfied
it, and childSpatialBefore+1 held in both worlds because whichever site did
not move the cell propagated idempotently. It now asserts the complete
ordered parent stream plus each element's CellId and Position.ObjCellId.
Sabotage: restoring refreshPosition:acceptedPosition turns it red (it was
green before), together with the withhold test and the new L5 test.

That cardinality change was itself unfiled and is now AP-147: a
cell-changing accepted Position publishes TWO entity deltas where it
published one, and the intermediate Updated pairs the OLD CellId with the
NEW wire Position - a torn pair that did not exist pre-C5b, since both
halves used to move inside one publish. No production consumer reads a
delta's paired fields, but a recorder/plugin/bot event log would capture
it. The row states why suppressing the Updated is not available at that
layer (the merge cannot know whether its caller reaches W2).

D4 - THE PROJECTILE DOC COMMENT WAS FALSE AND ITS RETAIL ARGUMENT WAS
INVERTED. SyncPresentationFromResolvedBody claimed record.FullCellId is
"the WIRE cell ... stamped by the merge's RefreshDerivedState/SetFullCell,
before classification ever runs" and argued from retail's store_position
@0x00515CE2 that the destination cell is the right one. C5b falsified the
premise; the missile arm also returns before W2, so nothing stamps the wire
cell for a projectile at all. Rewritten. The honest conclusion, which the
old text would have called wrong: on a stored outcome presentation now
pairs the DESTINATION world position with the SOURCE cell. That is not a
choice this method can make differently - StoreAcceptedDestinationPose
writes only Position/Orientation, so record.FullCellId and
body.CellPosition.ObjCellId now hold the same source cell and reading
either yields the same value. The divergence is AP-138 item (1)'s
store-writes-pose-but-not-cell residual, retiring via #309, not a field
choice here. Projecting the wire cell instead would invent a residency the
placement declined - the AP-1 shape C5b closed.

L3/L4/L5 - PINNING GAPS, ALL THREE CONFIRMED AND CLOSED.
L3: the matrix's oracle passed HasAnimations as a literal, so the merge's
old.MotionTableId ?? old.Physics?.MotionTableId and
RuntimeAcceptedPositionRouteRequests.Build's canonical-snapshot twin were
textually identical and pinned by nothing. The oracle is now BUILT by the
production constructor.
L4: every fixture set both MotionTableId halves to the same value, so
deleting either operand of the ?? was undetectable while the production
comment said the mixed case is the real-world one. Six mixed rows added,
including the explicit-zero row (a present-but-zero top half is not null,
so ?? never reaches the physics half).
L5: the retained Rebucketed ternary had zero coverage through
TryApplyPosition - every restoreCancelledPark test called Forget directly.
Now driven through the real merge, with the wire cell deliberately the
SOURCE while the park's committed body cell is the DESTINATION, so the
restored residency can only have come from the rollback.
Sabotage (each red, each restored): merge ?? -> top half only, 1 red;
-> physics half only, 2 red; Build's ?? -> physics half only, 2 red;
ternary -> constant Updated, exactly the L5 test red.

L1/L2 - THE MISSING TEST IS ADDED; THE DEFECT IS NOT THERE. The reviewer
was right that C5b's "no fixture covers pickup at that layer" was
inaccurate - LiveEntityNetworkOnPositionCollapseMatrixTests drives the real
OnPosition at ~26 sites - and the end-to-end test is added: withdraw ->
accepted Position -> IsSpatiallyProjected && FullCellId == wireCell, both
guid classes.

But ChildUnparentDisposition.Pending is NOT a live defect, because it is
production-unreachable. The sole production _withdrawProjection binding
(LivePresentationComposition.cs:599) is
LiveEntityProjectionWithdrawalController.WithdrawExact, whose only Pending
mint is inside its catch block and therefore always carries a non-null
Failure - and AdvanceUnparentTransition rethrows at
EquippedChildRenderController.cs:1307 BEFORE the return Pending at :1309.
The named drop scenario does not reach it anyway (BeginDetachedRemoval has
already emptied the capture list) and would be correct if it did: a
previously-equipped child is LegacyImmediate, so the FullCellId != 0u gate
at DatLiveEntityProjectionMaterializer.cs:767 is never consulted and
re-projection uses the wire cell at LiveEntityRuntime.cs:824.

Measured while building that test, and NOT what C5b assumed: W2 and W3 are
REDUNDANT on the remote tail. Sabotaging W2 alone - adopting the committed
cell instead of the wire cell, OR skipping the rebucket outright - leaves
the whole file green, because W3's RemoteMotion.CellId write reads through
to canonical FullCellId via CommitCanonicalCell, whose CellCommitted
recovery re-installs the bucket. Only removing BOTH goes red, and then the
new test is the only red in the file. So it is named for what it pins, and
AD-60 is amended with the measurement: neither channel is individually
load-bearing, so a future retirement of one is caught by nothing else.

D2 - REBUTTED, WITH THE REAL GAP FILED INSTEAD. The reviewer's hypothesis
was that TryApplyInitialCreateCompletionPresentation's staleness guard lost
its ability to detect an intervening steady-state Position when C5b stopped
the merge stamping the wire cell, and asked for a PositionAuthorityVersion
term. I do not think that is right and did not add it.

The receipt's facts are the canonical BODY's pose and cell at publish
(PublishExecutorCompletion builds both from the record). Exactly two owners
can move them: a Runtime SetPosition commit/withdrawal, every one of which
calls AdvancePlacementCommit - the only caller family is
RuntimeSetPositionState - and a rebucket, which moves FullCellId. Both are
already covered by the two existing terms. An accepted steady-state
Position is neither, and C5b did not make it one: the merge refreshes the
snapshot and advances PositionAuthorityVersion but never wrote the body,
and the App generic tail writes the RENDER entity. The wire-cell half stays
covered because W2/W3 commit it in the same call; the paths that return
before them leave the record at the last committed cell, which IS the
receipt's own cell - correctly not a supersession.

Adding the term would decline receipts whose facts are still true, on the
entity's FIRST world-visible moment: the pose write and
RebucketLiveEntityPresentationOnly would be skipped while TryPublishPlace
still publishes, so a packet returning before the render write would leave
the sidecar visible at its materialized pose in a wrong bucket. That is the
handoff's own "removed the invariant failure while leaving the bug" shape.

There IS one supersession neither term covers, and it predates C5b:
RuntimeRemotePlacementDriveController.StoreAcceptedDestinationPose writes
body.Position/Orientation on the far-snap Refused/Contention arm with no
placement commit and no cell move. Filed as #323 with the FIFO-blocking
argument for why a receipt can still be pending when it lands, an explicit
"not established as reachable", and an explicit "do not fix it with
PositionAuthorityVersion". The guard's comment now carries the whole
argument instead of one sentence.

S1 - DANGLING POINTER CLOSED. InboundPhysicsStateController.cs:610 still
said the two-callers-one-rule debt was "tracked for the eventual cutover
unification ... See docs/ISSUES.md", which pointed at nothing after C5b
closed #275 without a successor. Filed #322, cited from both the comment
and #275's closure, including why widening TryApplyPosition's signature to
take a route would be the wrong unification.

AP-138 amended: C5b staled its round-3 measurement that "both
accepted-Position callers commit the accepted wire cell to
record.FullCellId before submitting". Route 2 submits from
TryExecuteAcceptedLocalPosition ahead of W2, so on a first submit
PlacementTouchesPrefix's CurrentCellId arm now names the SOURCE landblock,
not the destination. Confined to which prefix the quiescence pre-flight
matches, which that row already established is not the correctness
mechanism.

GATES. Release build 0 errors. Complete suite 11,134 passed / 4 skipped /
0 failed, from the 11,125 / 4 baseline at ed806997: net +9, all new tests,
no test deleted or weakened, no new skip. Runtime.Tests 1195 -> 1202 (+6
mixed-motion-table rows, +1 park-rollback fact); App.Tests 4132 -> 4134
(+2 guid rows). None of #302/#308/#321 appeared. Not connected-gated -
nothing here changes runtime behaviour.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-05 22:08:46 +02:00
parent ed8069976c
commit 23aa62f292
9 changed files with 637 additions and 62 deletions

View file

@ -1698,6 +1698,12 @@ which uses the production classifier as the oracle. Wiring the continuation
executor into the steady-state path is a separate structural decision that no
longer has any behavioural motivation behind it.
**Successor filed 2026-08-05 at the C5b review (finding S1): #322.** This
closure originally left "the remaining structural item is tracked below" with
no ID, and the production comment on
`InboundPhysicsStateController.TryApplyPosition` pointed at `docs/ISSUES.md`
for a follow-up that did not exist. Both now cite #322.
---
## #274 — Restricted/barred-house entry needs a connected retail comparison
@ -13521,3 +13527,110 @@ under repetition, it is a real race and should be escalated out of LOW.
Do not add a retry, a `Skip`, or a delay to make it green — this campaign's
standing rule is no workarounds without explicit approval, and a masked race is
strictly worse than a red test.
---
## #322 — Two callers compute the same two pre-placement flags from the same two inputs
**Status:** OPEN
**Severity:** LOW (internal refactor debt; NOT a retail divergence)
**Filed:** 2026-08-05 (C5b review, finding S1 — the successor #275 closed
without filing)
**Component:** Runtime / inbound Position
**Description.** Since C5b (`735f0a72`) both accepted-Position callers derive
retail's two pre-placement writes — `installPlacementFrame` and `clearParent`
from the identical pair `(disposition, hasAnimations)`, in two separate places:
- `InboundPhysicsStateController.TryApplyPosition` computes them inline,
pre-merge, from the retained snapshot `old`.
- `RuntimeInitialCreateContinuationExecutor.ApplyPositionAction` reads them off
`RuntimeAuthoritativePositionRouteClassifier`'s
`ApplyPlacementFrameBeforeRouting`/`UnparentBeforeRouting`, built through
`RuntimeAcceptedPositionRouteRequests.Build`.
The two are pinned EQUAL by test rather than by a shared code path
(`InboundPhysicsStateControllerTests.MergedPrePlacementFieldsMatchTheClassifiedRouteFlags`,
whose oracle is the production classifier fed through the production `Build`
since the C5b review's L3 fix). That is deliberate: it keeps each computation
separately sabotage-verifiable. It is still two copies of one rule.
**Why this is not #275.** #275 was the BEHAVIOURAL unification and is closed:
the merge no longer passes unconditional `true/true`, and it no longer derives
`FullCellId` from bare wire acceptance. What remains is structural only. There
is no behavioural motivation left behind it, which is precisely why it needs
its own ID rather than an open-ended "eventual cutover" pointer.
**Acceptance (either is fine, pick at the time):** (a) wire the continuation
executor into the steady-state path so there is one caller, or (b) extract the
two-flag derivation into one function both callers call — but if (b), the pin
test must be re-argued, since a shared path makes the current
merge-vs-classifier sabotage discrimination vacuous.
**Do not** widen `TryApplyPosition`'s signature to take a full
`RuntimeAuthoritativePositionRoute` as a shortcut: the whole point of C5b's
finding is that these two flags need no route, no `playerDistance` and no
`CommittedCellId`, because retail decides both ahead of `MoveOrTeleport`
(`SmartBox::HandleReceivedPosition` @0x00453FD0 — Gate A @0x0045400C returns
@0x0045409D before `unset_parent` @0x00454129 and before the `HasAnims`
`SetPlacementFrame` gate @0x00454137).
---
## #323 — A far-snap store can silently stale a pending initial-create completion receipt
**Status:** OPEN
**Severity:** LOW (narrow, self-healing within one broadcast interval; no
observed live symptom)
**Filed:** 2026-08-05 (C5b review, finding D2 — found while establishing that
C5b did NOT weaken the guard; this gap predates C5b)
**Component:** App / placement projection
**Description.** `LiveEntityRuntime.TryApplyInitialCreateCompletionPresentation`
declines a stale `ExecutorCompleted` receipt on two terms:
`record.FullCellId != token.ExactCellId` and
`record.Canonical.PlacementCommitVersion != token.PlacementCommitVersion`.
Together those cover every owner that can move the receipt's facts — the
receipt carries the canonical body's pose and cell at publish
(`RuntimeSetPositionState.PublishExecutorCompletion`), and only a Runtime
SetPosition commit/withdrawal (every one of which calls
`AdvancePlacementCommit`; the only caller family is `RuntimeSetPositionState`)
or a rebucket can move them.
**Except one.** `RuntimeRemotePlacementDriveController.StoreAcceptedDestinationPose`
writes `body.Position` and `body.Orientation` directly on the far-snap
`Refused`/`Contention`/`RejectedPreparation` arm (AP-138 item (1)'s
"store, because the resolve never ran"). It bumps no placement commit and
moves no cell. If an `ExecutorCompleted` receipt for that entity is still
sitting behind an unacknowledged receipt on the shared placement FIFO when
that store lands, the receipt drains with a pose that is now older than the
body's, and `entity.SetPosition(projection.WorldPosition)` snaps the render
entity back.
**Why the FIFO can be non-empty at that moment.** `PublishExecutorCompletion`
dispatches synchronously, but `RuntimePlacementProjectionSubscription.OnPlacement`
applies a receipt only when it is the FIFO head, and
`RuntimePlacementPresentationSink.TryApply` deliberately refuses (leaves at the
head) any `Place`/`Withdraw` for an entity still holding an initial-create
residence, for the drive controller's per-frame pump to consume. So one
entity's conductor-owned receipt can hold another entity's `ExecutorCompleted`
behind it across network packets.
**Not established:** whether the combination is actually reachable in play — it
needs a ≥96 m far-snap classification for an entity whose initial-create
completion is still queued, and the far arm's refusal reasons are themselves
narrow. Reported rather than fixed for exactly that reason.
**Do NOT fix it by adding `PositionAuthorityVersion` to the guard.** That was
the C5b reviewer's proposed shape and it is wrong: the merge bumps that version
on every accepted Position including ones that move nothing, so the guard would
decline receipts whose facts are still true, on the entity's FIRST
world-visible moment — skipping the pose write and
`RebucketLiveEntityPresentationOnly` while `TryPublishPlace` still publishes.
The correct shape, if this is ever confirmed reachable, is to make the store
arm advertise itself (a body-pose authority version, or routing the store
through a commit-versioned seam) so the receipt can see it.
**Superseded text, for the record.** The comment at the guard used to say only
"A newer move superseded this receipt's facts after the drain." It now carries
the full argument and cites this issue.