RuntimePlacementPresentationSink.TryPublishPlace previously published the local player's collision-shadow pose with a direct LocalPlayerShadowState.Set call — a plain cache write that never touched PhysicsEngine.ShadowObjects. Because LocalPlayerShadowSynchronizer.SyncPose's own dedup check compares against that same cache, the direct write could pre-seed the cache with the destination pose and cause the next real SyncPose call to see "nothing changed" and skip its own ShadowObjects publish — leaving the real collision shadow at the pre-teleport position until an unrelated movement tick forced a real publish. Fix: TryPublishPlace now calls _localPlayerShadowSync.SyncPose(..., force: true), the same publisher ordinary per-tick movement uses, so Place always drives a real ShadowObjects write before the cache updates. TryPublishWithdrawal carried the exact mirror asymmetry (a bare LocalPlayerShadowState.Clear with no ShadowObjects.Suspend, leaving a live phantom shadow row at the park's source cell for the whole park window — the #184 shape) and is fixed in the same commit, same one-call shape: _localPlayerShadowSync.Suspend(entity). The sink no longer holds a direct LocalPlayerShadowState reference; both halves route exclusively through the one synchronizer, which owns the cache internally. The single LocalPlayerShadowSynchronizer instance is now constructed in LivePresentationComposition (before the sink) and threaded through LivePresentationResult to SessionPlayerComposition, which no longer builds its own — this guarantees the sink's Place/Withdraw edge and ordinary per-tick movement publish through the exact same publisher and cache rather than two independent instances that could drift out of sync with each other. TryPublishPlace's xmldoc now states the behavioural nuance directly: routing through SyncPose means Place inherits SyncPose's own admission guard (IsHidden, cellId == 0, not-current-visible-projection), which the old direct .Set() call never consulted. Under those conditions SyncPose now calls Suspend instead of publishing — correct and symmetric, but new behaviour worth flagging at the call site, not just in a test comment. RuntimePlacementShadowCompositionTests.cs (#318) proves four facts against the real ShadowObjects registry, not the cache: a bare Place publishes a real row at the destination cell with the source cell's row gone; a subsequent ordinary per-tick Sync is then a correct no-op; a Place for a registered non-local-player entity leaves its row at the source cell untouched and never touches the player's cache (route 7 P4 — the fix lives entirely inside the pre-existing player-only gate); and Withdraw suspends the real registry row, not just the cache, with the retained (suspendable) registration surviving for a later restore. All four were sabotage-verified in both directions. RuntimeForcePositionRenderCommitTests.cs (B2) drives a real end-to-end accepted ForcePosition through RuntimeEntityObjectLifetime.TryApplyPosition and RuntimeAcceptedPositionDriveController.TryExecuteAcceptedLocalPosition against a live HostFixture, asserting both the committed render position AND a cell change that deliberately crosses out of the spawn's outdoor grid cell, so the cell assertion is independently falsifiable rather than riding along with the position assertion. Retires AP-145 (this fix) in docs/architecture/retail-divergence-register.md. AP-1 and AD-1 are untouched by this commit — they retire separately in the deletion-sweep commit that follows. Evidence chain: docs/research/2026-08-05-c5a-contract.md (the governing C5a slice contract), docs/research/2026-08-05-c5a-architecture-review.md (round 1, FAIL — three MAJORs: vacuous route-7 P4 test, unfixed Withdraw-side mirror asymmetry, non-driving B2 test), docs/research/2026-08-05-c5a-architecture-review-round2.md (round 2, PASS with two MINORs — an unfalsifiable B2 cell assertion and the undocumented SyncPose guard nuance, both fixed here). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
470 lines
29 KiB
Markdown
470 lines
29 KiB
Markdown
# C5a contract — legacy deletion sweep + carried parity tests (pinned 2026-08-05)
|
||
|
||
Pinned at HEAD **`392c1e22`** (branch `claude/acdream-physics-divergence-5aa784`),
|
||
i.e. AFTER #319 landed. Every symbol, caller census, and line number below was
|
||
**re-verified against this HEAD by grep/read**, not inherited from the C5
|
||
scoping (`2026-08-05-c5-scoping.md`, written at `52175aa1`) — §9 lists every
|
||
place the scoping's picture moved. Baseline: complete Release suite
|
||
**11,112 passed / 4 skipped / 0 failed**, measured at `392c1e22` (the #319
|
||
commit message records the measurement; re-measure at implementation start,
|
||
never inherit — process rule (c)).
|
||
|
||
**Scope, stated negatively first:**
|
||
|
||
- **NOT #275.** The steady-state inbound-Position merge
|
||
(`InboundPhysicsStateController.TryApplyPosition`, the simple overload) and
|
||
`RuntimeEntityObjectLifetime`'s wire-derived `FullCellId` refresh (the
|
||
`refreshPosition: acceptedPosition` call, **now at `:1926`** post-#319) are
|
||
the C5b behaviour change with its own contract. C5a must not modify either
|
||
file's executable code (one test-file doc-comment correction is the only
|
||
permitted touch near this surface, §1 D7).
|
||
- **NOT the probe strip.** All six `ACDREAM_PROBE_*` temporary flags stay
|
||
(C5c); they are env-gated and inert to everything here.
|
||
- **NOT AP-131, NOT AD-60's legacy half, NOT AP-145's seam** (except the
|
||
pre-authorized red branch in §5.1). Those rows stay in the register
|
||
untouched.
|
||
|
||
**Scope, positively:** the six deletion groups in §1 (~490 production lines),
|
||
the seven test-caller dispositions in §3, retirement of register rows **AP-1**
|
||
and **AD-1** in the same commit as the deletions, and the two carried parity
|
||
tests in §5 (#318 composition; route-2 B2).
|
||
|
||
---
|
||
|
||
## 1. Deletion inventory — re-verified at `392c1e22` by symbol
|
||
|
||
Caller censuses below are exhaustive over `src/` (all `*.cs`). Method: for
|
||
`Resolve`, every `.Resolve(` receiver in `src/` was enumerated and typed — 38
|
||
distinct receiver/site classes, **none** a `PhysicsEngine` (see the grep-hygiene
|
||
note in §3.8: two of them are #319's NEW `ParentAttachmentState.Resolve`, a
|
||
name collision that did not exist when the scoping ran its census). For the
|
||
others, direct symbol grep over `src/` and `tests/`.
|
||
|
||
| # | Symbol | Location at HEAD | Production callers | Test callers | ~Lines |
|
||
|---|---|---|---|---|---|
|
||
| D1 | `PhysicsEngine.Resolve(Vector3, uint, Vector3, float)` | `src/AcDream.Core/Physics/PhysicsEngine.cs:1863`–`~2200` (body ends before `ResolveWithTransition`'s xmldoc; the live method at `:2223` is a **different member** and stays) | **ZERO** | `PhysicsEngineTests.cs` ×12 (`:41,:48,:66,:88,:111,:150,:186,:211,:391,:434,:446,:460`); `Issue133DungeonTeleportPrefixTests.cs:58` | ~360 |
|
||
| D2 | `PhysicsEngine.HasCellSurface` | `PhysicsEngine.cs:1767`–`~1789` | only `Resolve` itself (`:1887`) — deletes with D1 | none | ~23 |
|
||
| D3 | `PhysicsEngine.ResolvePlacement` | `PhysicsEngine.cs:2748`–`~2815` | **ZERO** (sole non-test mention is the already-recorded retirement comment at `HeadlessSessionWorldProjection.cs:794`) | `InitialPlacementOverlapTests.cs:42`; `TransitionScratchDifferentialTests.cs:185,:194,:208,:217` | ~70 |
|
||
| D4 | `PlayerMovementController.SetPosition` (both overloads) | `src/AcDream.Runtime/Gameplay/PlayerMovementController.cs:1746,:1760` | **ZERO** — every `.SetPosition(` in `src/` outside `PhysicsEngine.cs` is `WorldEntity.SetPosition` (receivers `entity.`/`child.`) or Core `PhysicsEngine.SetPosition` via `_physics.Engine.` from `RuntimeSetPositionState` (`:2028,:3125,:4789`), the canonical path | 19 test files, ~80 sites (44 in `PlayerMovementControllerTests.cs` alone) — **fixture setup**, not subject (§3.5) | ~30 gross; ~15 net after the retained seed (§3.5) |
|
||
| D5 | `PlayerMovementController.CommitPreparedPosition` | `PlayerMovementController.cs:1789` | **ZERO** — production replacement is `ArmConstraintLeashAtCommittedPlacement` (`:1815`), called from `RuntimeLocalPlayerPhysicsPublicationState.cs:774`; `PreparePositionForCommit` (`:1776`) remains production via `RuntimeLocalPlayerPhysicsPublicationState.cs:219` | `PlayerMovementPlacementTransactionTests.cs:42`; `PlayerMovementControllerTests.cs:1158`; `RuntimeLocalPlayerPhysicsPublicationStateTests.cs:3007` | ~25 |
|
||
| D6 | `RuntimeSetPositionState.BeginAcceptedPlacement` / `BeginAuthoredPlacement` | `src/AcDream.Runtime/Physics/RuntimeSetPositionState.cs:1321,:1333` | **ZERO** — pure pass-throughs to `BeginAcceptedPlacementCore`; production reaches the core via `Apply` (`:1304`) and the authored sequence | **39 sites across 9 Runtime test files** (scoping said ~40/10; re-censused) | **0 — KEEP as documented test seam** (§3.7) |
|
||
| D7 | Doc hygiene | stale `BlipPosition`/`PlayerMovementController.SetPosition` doc refs at `src/AcDream.Core/Physics/Motion/ConstraintManager.cs:25` (note: the scoping's path lacked `Motion/`) and `src/AcDream.Core/Physics/PhysicsBody.cs:442`; the stale `TryApplyPickup (:1116)` citation in `RuntimeAcceptedPositionDriveControllerTests.cs` (~`:221` region; the method lives in `RuntimeEntityObjectLifetime`, currently ~`:1258+`) — **cite by symbol, not line**, in the correction | — | — | ~20 comment lines |
|
||
|
||
Net production deletion: **~490 lines** (the scoping's ~540 minus D6's kept
|
||
~25 and D4's retained seed). All deletions are compile-loud.
|
||
|
||
**Confirmed unchanged from the scoping's §1c (NOT deletable, re-spot-checked):**
|
||
`ILocalPlayerTeleportPlacement`/`LocalPlayerTeleportPlacement.Place` (live
|
||
post-commit presentation suffix, `LocalPlayerTeleportController.cs:243` still
|
||
calls `entity.SetPosition(controller.Position)` inside it);
|
||
`PlayerMovementController.SetPositionCore` (`:1845`, production via
|
||
`PreparePositionForCommit`); the route-4 leftovers; AP-135's bookkeeping;
|
||
`RuntimeLiveEntitySessionController.cs:141`'s pre-flip path (its file WAS
|
||
touched by #319, but the C4/C5-revisit comment's unblock condition is still
|
||
unmet).
|
||
|
||
---
|
||
|
||
## 2. The survivor hazard — TWO production members inside the deletion region, not one
|
||
|
||
The scoping named one. Re-verification at HEAD finds **two**:
|
||
|
||
1. **`IsSpawnCellReady` (`PhysicsEngine.cs:1807`)** — production callers
|
||
`RuntimeSetPositionState.cs:2169,:4378` and
|
||
`SessionPlayerComposition.cs:374`. Sits between `HasCellSurface` (delete)
|
||
and `Resolve` (delete).
|
||
2. **`AdjustPosition` (`PhysicsEngine.cs:1813`)** — **the scoping never
|
||
dispositioned it.** It is production: `PhysicsCameraCollisionProbe.cs:38,:100`
|
||
(the camera collision probe), plus six-plus test files
|
||
(`PhysicsEngineAdjustPositionTests`, the camera replay suites,
|
||
`Issue177StairDescentCameraFloodTests`, ...). It sits between
|
||
`IsSpawnCellReady` and `Resolve` — dead centre of the physical block.
|
||
|
||
**Pinned survival rule:** the deletion is **member-wise, never region-wise**.
|
||
Delete exactly the bodies of `HasCellSurface`, `Resolve`, and
|
||
`ResolvePlacement`; `IsSpawnCellReady` (`:1807`–`:1811`) and `AdjustPosition`
|
||
(`:1813`–`:1861`) remain byte-identical in executable code.
|
||
|
||
**Xmldoc fallout (same commit):** `IsSpawnCellReady`'s summary contains
|
||
`<see cref="Resolve"/>` (in the `:1791`–`:1806` block) and `HasCellSurface`'s
|
||
summary names "the Resolve safety net"; the second dies with its method, the
|
||
first must be rewritten (the "loud outdoor-demote safety net" sentence
|
||
describes machinery this commit deletes — rewrite the paragraph to describe
|
||
the canonical `SetPosition` reality, do not leave a cref to a deleted symbol).
|
||
Sweep `PhysicsEngine.cs` for any other `cref="Resolve"`/`cref="ResolvePlacement"`
|
||
after the deletion; the build must be warning-clean on missing crefs.
|
||
|
||
---
|
||
|
||
## 3. The seven test-only-caller dispositions — re-verified, each carried forward
|
||
|
||
Framing (from the scoping, still correct): deleting a method whose callers are
|
||
tests is compile-loud. The silent hazard is the **disposition of the tests
|
||
afterward** — deleting a test that pinned MOVED behaviour loses the pin with a
|
||
green build. Each case below is a binding disposition; any deviation must be
|
||
argued in the commit message.
|
||
|
||
### 3.1 `PhysicsEngine.Resolve` unit tests — behaviour GONE → DELETE, after a one-pass audit
|
||
|
||
`PhysicsEngineTests.cs` (565 lines, 12 `engine.Resolve(` sites). The
|
||
outdoor-demote / legacy floor-snap / terrain-lift semantics die with the
|
||
method; canonical replacements have their own suites (`PhysicsSetPositionTests`,
|
||
`RuntimeSetPositionStateTests`). **Audit before deleting:** any individual
|
||
assertion that actually pins canonical-owned behaviour — specifically
|
||
`AdjustPosition` semantics, which SURVIVE — is re-pointed at `AdjustPosition`
|
||
directly (note `PhysicsEngineAdjustPositionTests.cs` already exists; a
|
||
re-point may land there). The audit's outcome (N assertions re-pointed, M
|
||
deleted) goes in the commit message.
|
||
|
||
### 3.2 `Issue133DungeonTeleportPrefixTests` — behaviour MOVED → RE-POINT (named-bug regression pin)
|
||
|
||
Verified at HEAD: the defect mechanism this test pins — the `lbPrefix`
|
||
resident-landblock scan that re-stamped a validated dungeon claim with a
|
||
neighbour's prefix — lives **entirely inside `Resolve`'s body**
|
||
(`PhysicsEngine.cs:1928,:1937,:1984-1995,:2193`). The canonical
|
||
`PhysicsSetPosition` path has **no lbPrefix scan** — the defect class cannot
|
||
recur there by construction. That is precisely why the pin must be
|
||
**re-pointed, not deleted**: #133 is a closed named bug, and the invariant
|
||
(a validated dungeon claim's landblock prefix is authoritative; a committed
|
||
cell never gets re-stamped from a neighbouring resident block) must stay
|
||
assertable against whatever path owns placement now.
|
||
|
||
**Re-point spec:** drive a teleport-classified canonical placement
|
||
(`RuntimeSetPositionState`, or Core `PhysicsEngine.SetPosition` if the fixture
|
||
cost is lower) with the test's exact geometry — dungeon claim `0x00070143`,
|
||
dungeon block world-offset so its local Y is negative, resident neighbour
|
||
block at origin containing the same XY — and assert the committed cell keeps
|
||
the `0x0007` prefix. Alternative accepted by this contract: PROVE an existing
|
||
canonical test already pins prefix authority for an off-bounds dungeon claim
|
||
and record the proof (test name + assertion) in the deleting commit. Silent
|
||
deletion is a contract violation.
|
||
|
||
### 3.3 `InitialPlacementOverlapTests` — behaviour MOVED → VERIFY-THEN-DELETE
|
||
|
||
The ring-search half of enter-world placement is ported inside canonical
|
||
`SetPosition` (`TransitionTypes.cs:1596` `FindPlacementPosition`, retail
|
||
0x0050C170; `PhysicsSetPositionTests.cs` header cites it and has
|
||
placement-probe scenarios, e.g. the `placementPasses >= 2` retry arm at
|
||
`:1152-1194`). **The audit criterion:** confirm `PhysicsSetPositionTests`
|
||
covers the **other-entity-occupancy** ring search this test pins (a relogging
|
||
player overlapping a registered creature sphere searches outward to the
|
||
nearest clear ring) — occupancy-driven, not merely BSP-failure-driven. If
|
||
covered: delete, citing the covering test by name. If not: re-point this
|
||
test's scenario through canonical `SetPosition` with a placement class that
|
||
reaches `FindPlacementPosition` (~50–100 lines), then delete the
|
||
`ResolvePlacement` call.
|
||
|
||
### 3.4 `TransitionScratchDifferentialTests` — differential arm → RE-POINT OR DROP EXPLICITLY
|
||
|
||
Verified at HEAD: the spec-based sequence arms (`ResolveSpec.Resolve` at
|
||
`:618`) call `ResolveWithTransition` — **untouched by this slice**. Only
|
||
`ReusedScratch_MatchesFreshPlacementSearch` (`:180`–`:227`, four
|
||
`ResolvePlacement` sites) is affected. It is a Slice-I zero-alloc scratch-reuse
|
||
differential over the placement search (including the hostile-identity
|
||
leak check). **Disposition:** re-point the arm at the canonical entry that
|
||
reaches `FindPlacementPos` (Core `SetPosition` with the appropriate placement
|
||
class), preserving both the bitwise fresh-vs-reused comparison and the
|
||
second-identity leak check. If re-pointing is disproportionate, the arm may be
|
||
dropped ONLY with an explicit commit-message decision naming what coverage the
|
||
I-slice differential loses — never silently.
|
||
|
||
### 3.5 `PlayerMovementController.SetPosition` fixture usage — NEITHER gone nor moved → RETAINED SEED + mechanical re-point
|
||
|
||
Census at HEAD: **19 test files** reference `PlayerMovementController` and
|
||
call `.SetPosition(`; ~80 sites total; 44 in `PlayerMovementControllerTests.cs`,
|
||
8 in `LocalPlayerTeleportControllerTests.cs`, 5 in `HeadlessSessionHostTests.cs`,
|
||
the rest 1–4 each. (Per-site care: a file can reference the controller and
|
||
still call `WorldEntity.SetPosition` — type each site during the re-point,
|
||
don't regex-replace blind.)
|
||
|
||
**Pinned design (the scoping's "cheaper and honest" option, adopted):** keep
|
||
**ONE** internal, explicitly-named test seed on the controller — rename the
|
||
3-arg overload to `SeedPlacementForTest(Vector3 pos, uint cellId, Vector3 cellLocal)`
|
||
(internal; xmldoc states it exists ONLY to seed fixtures and that production
|
||
placement flows through `PreparePositionForCommit` →
|
||
`ArmConstraintLeashAtCommittedPlacement`), delete the 2-arg overload, and
|
||
mechanically re-point all ~80 sites. Semantics are reproduced by construction:
|
||
the seed calls the SAME `SetPositionCore` (which stays production), so the
|
||
grounded, zero-velocity start (the AD-61 force-seed) that dozens of movement
|
||
tests assume is unchanged. This is the one production-file signature change
|
||
in the slice; its body is untouched.
|
||
|
||
### 3.6 `CommitPreparedPosition` tests — behaviour MOVED → RE-POINT at the arm/commit replacement
|
||
|
||
Three sites, each audited individually:
|
||
|
||
- `PlayerMovementPlacementTransactionTests.cs:42` (100-line file): the
|
||
prepared-position transaction assertions run against the production pair
|
||
(`PreparePositionForCommit` + `ArmConstraintLeashAtCommittedPlacement`) —
|
||
rewrite the test against that pair, or delete it if
|
||
`RuntimeLocalPlayerPhysicsPublicationStateTests` provably covers the same
|
||
transaction shape (cite which test).
|
||
- `PlayerMovementControllerTests.cs:1158` and
|
||
`RuntimeLocalPlayerPhysicsPublicationStateTests.cs:3007` both assert
|
||
`Throws<InvalidOperationException>` on the uncommitted/displaced state.
|
||
Audit whether the replacement arm carries an equivalent guard; if yes,
|
||
re-point the throw assertion at it; if the guard died with the method,
|
||
delete the assertion WITH a commit-message note (guard semantics gone, not
|
||
overlooked).
|
||
|
||
### 3.7 `BeginAcceptedPlacement`/`BeginAuthoredPlacement` — NEITHER → KEEP AS DOCUMENTED SEAM
|
||
|
||
39 sites across 9 Runtime test files at HEAD. The wrappers are pure
|
||
pass-throughs to the production core (`BeginAcceptedPlacementCore`); deleting
|
||
them buys zero behaviour and costs broad mechanical churn across the Runtime
|
||
suite. **Disposition: keep, with an xmldoc sentence on each wrapper naming it
|
||
a test seam** (so a future sweep does not re-litigate this). This is a
|
||
recorded deliberate exception to "delete every superseded legacy path": the
|
||
wrappers are not a legacy PATH — the core they call IS the canonical path.
|
||
|
||
### 3.8 Landmines and grep hygiene
|
||
|
||
- **The #316-preserving pair** (`LiveEntityNetworkOnPositionCollapseMatrixTests.cs:131,:180`)
|
||
pins a defect **preserved verbatim**. C5a must not touch it; it inverts only
|
||
with #316's measured fix (C5-gate session / later).
|
||
- **#319 introduced `ParentAttachmentState.Resolve`** (`ParentAttachmentState.cs:432`),
|
||
called at `EquippedChildRenderController.cs:920` and
|
||
`RuntimeLiveEntitySessionController.cs:390`. A mechanical grep for
|
||
`.Resolve(` now hits relation resolution — neither site is `PhysicsEngine`.
|
||
Any "prove zero callers" re-run during implementation must type receivers,
|
||
not count matches.
|
||
- The affected-file overlap between #319 and this slice is **empty**: #319
|
||
touched `EquippedChildRenderController`, `LiveEntityHydrationController`,
|
||
`LiveEntityPresentationController`, `ParentAttachmentState`,
|
||
`RuntimeEntityObjectLifetime`, `RuntimeLiveEntitySessionController` — none
|
||
contains a C5a deletion target. Verified.
|
||
|
||
---
|
||
|
||
## 4. Register retirements — AP-1 and AD-1, with the code evidence; four rows explicitly untouchable
|
||
|
||
A row retires because the code proves its condition met. Both retirements ride
|
||
**in the same commit as the D1–D5 deletions** (register rule 1).
|
||
|
||
### AP-1 — RETIRE. Evidence at `392c1e22`:
|
||
|
||
Row text: "Production zero-delta routes deliberately remain on the legacy
|
||
resolver until 4B2..." — **false at HEAD**:
|
||
|
||
1. The "legacy resolver" is `PhysicsEngine.Resolve`/`ResolvePlacement`. The
|
||
exhaustive receiver census (§1) shows **zero** `PhysicsEngine.Resolve` or
|
||
`.ResolvePlacement` call sites in `src/`.
|
||
2. Every production placement writer reaches Core `PhysicsEngine.SetPosition`
|
||
**only** through `RuntimeSetPositionState` (`:2028,:3125,:4789` — the only
|
||
three `_physics.Engine.SetPosition` sites in `src/`).
|
||
3. The row's named prerequisites (authored mover, rebucketing,
|
||
prefix-quiescence, body publication, atomic route cutover) landed across
|
||
C0–C4; the local controller's body adoption landed at C3c.
|
||
4. Deleting D1–D5 makes the retirement **structural**: the resolver-shaped
|
||
entry points cease to exist, so no future caller can re-open the row's
|
||
condition.
|
||
|
||
The narrower survivors (#276 settle-cell discard, AD-61 force-seed, AD-62
|
||
non-commit outcomes) are separately filed rows/issues and do not block AP-1's
|
||
own condition — deleting AP-1 does not orphan them.
|
||
|
||
### AD-1 — RETIRE. Evidence at `392c1e22`:
|
||
|
||
Row text: "Production authoritative placement still routes through the legacy
|
||
recoverable outdoor demote and outdoor-restore `max(terrainZ, z)` lift" —
|
||
**false at HEAD**: that code is `Resolve`'s body (demote at
|
||
`PhysicsEngine.cs:~1890-1910`, the outdoor `max(terrain, z)` lift inside the
|
||
snap block ~`:2160-2175`) and `Resolve` has zero production callers. The
|
||
lost-cell stand-in the row describes is unreachable from production. Deleting
|
||
D1/D2 removes the divergent mechanism outright.
|
||
|
||
### Must NOT be touched (each blocked on work outside this slice):
|
||
|
||
- **AP-131** — retires only with **#275** (C5b): the legacy
|
||
`TryApplyPosition` unconditional `installPlacementFrame: true, clearParent: true`
|
||
is still the ONLY steady-state production Position merge at HEAD.
|
||
- **AD-60's legacy half** — same gate (#275): the
|
||
`RefreshSnapshot(..., refreshPosition: acceptedPosition)` site — **`:1926`
|
||
at HEAD** (the scoping's `:1918` and the register's `:1338` are both stale;
|
||
cite the symbol) — still derives `FullCellId` from bare wire acceptance.
|
||
- **AP-145** — retires with **#318's fix**, never with its test. The §5.1
|
||
composition test makes the asymmetry falsifiable; only the pre-authorized
|
||
red branch may touch the seam, and then AP-145 retires in THAT commit.
|
||
- **AD-61 / AD-62 / AP-135 / AP-141–146** — all carry their own retirement
|
||
conditions; none is met by anything in this slice. (AP-146 and the AP-132
|
||
amendment are #319's, three days old — do not disturb.)
|
||
|
||
---
|
||
|
||
## 5. The two carried parity tests
|
||
|
||
Both are test-only against HEAD's production code, land BEFORE the deletion
|
||
commit (they are independent of it and de-risk the slice's review), and both
|
||
follow process rule (e): sabotage-verified, with the WHICH-assertion-fails
|
||
check, both directions for dual-layer assertions.
|
||
|
||
### 5.1 #318 composition test (~150–300 lines, App.Tests)
|
||
|
||
Drive a real portal arrival through the canonical drive controller + the
|
||
**REAL** `RuntimePlacementPresentationSink` + the **REAL** `PhysicsEngine`
|
||
(fixture patterns exist: `RuntimePlacementPresentationSinkTests.cs`,
|
||
`RuntimeFirstEntryHostIntegrationTests.cs`). The discriminating assertion:
|
||
|
||
> **`PhysicsEngine.ShadowObjects` (`PhysicsEngine.cs:147`) holds a row at the
|
||
> destination cell/position** — NEVER merely `LocalPlayerShadowState`'s dedup
|
||
> cache. AP-145's bypass (`RuntimePlacementPresentationSink.cs:243`
|
||
> `_localPlayerShadow.Set(...)` skipping `LocalPlayerShadowSynchronizer.SyncPose`'s
|
||
> publish) both skips the publish AND pre-seeds `SyncPose`'s dedup — a
|
||
> cache-only assertion is satisfied by the bug.
|
||
|
||
Plus the T8 write-ordering assertion from route 3 §8. Sabotage: perturb
|
||
`TryPublishPlace` to the cache-only shape and confirm the `ShadowObjects`
|
||
assertion (not an incidental one) fails; separately confirm a cache-only
|
||
assertion would pass under the same sabotage — proving the discriminator
|
||
discriminates.
|
||
|
||
**Pre-authorized red branch:** this test may legitimately FAIL at HEAD — the
|
||
composition drives placement with no subsequent movement tick, which is
|
||
exactly the window AP-145 says is unpublished. If red: **C5a's deletion work
|
||
does not absorb the fix.** The seam fix (routing the placement's shadow update
|
||
through the real publish) is a production behaviour change on a
|
||
narrow, low-frequency path; it lands as its **own reviewed commit** together
|
||
with the now-green test, retires **AP-145**, and closes **#318** — and the
|
||
composition test itself is its designed gate (the C4 handoff explicitly ruled
|
||
the connected route out as #318 coverage). If green: land as-is; #318 closes;
|
||
AP-145's row is then re-argued (its "why not observed" column may become its
|
||
retirement argument) — but only with the green evidence cited.
|
||
|
||
### 5.2 Route-2 B2 parity test (~100–200 lines, App.Tests)
|
||
|
||
The campaign plan's recorded acceptance gap (plan §C4 route 2, recorded unmet
|
||
since 2026-08-03): an App-layer test driving an **accepted ForcePosition end
|
||
to end** through `RuntimePlacementPresentationSink` /
|
||
`TryApplyRuntimePlacementPlace` and asserting **the render entity's
|
||
position/cell came from the committed placement receipt** — closing the
|
||
"canonical body moves, render entity stays put" silent seam. Expected green
|
||
at HEAD (route 2 landed; the seam is merely uncovered). If red, the same
|
||
stop-and-report protocol as 5.1: a red parity test is a found defect, not a
|
||
test problem; it gets its own investigation before any deletion lands.
|
||
|
||
Sabotage: sever the receipt→render write and confirm the position/cell
|
||
assertion is the one that fails.
|
||
|
||
---
|
||
|
||
## 6. What must REMAIN true — the slice's invariants
|
||
|
||
1. **Zero production behaviour change.** The production diff consists of:
|
||
member deletions with zero callers (D1–D5), comment/xmldoc edits (D7, §2),
|
||
and exactly one signature change with an untouched body (§3.5's seed
|
||
rename). No executable production statement is added or modified —
|
||
**except** in the pre-authorized 5.1 red-branch commit, which is its own
|
||
reviewed landing with its own register action.
|
||
2. **The two survivors survive.** `IsSpawnCellReady` and `AdjustPosition`
|
||
keep their exact executable bodies and all production callers
|
||
(`RuntimeSetPositionState.cs:2169,:4378`; `SessionPlayerComposition.cs:374`;
|
||
`PhysicsCameraCollisionProbe.cs:38,:100`).
|
||
3. **Every deleted symbol's absence is proven** by the compiler (all deletions
|
||
are compile-loud) AND every test caller has an explicit §3 disposition
|
||
executed in the same commit — no test deleted whose pinned behaviour moved
|
||
without its re-point landing alongside.
|
||
4. **AP-1 and AD-1 retire in the SAME commit as the D1–D5 deletions** — never
|
||
before (the code proof is the deletion), never after (register rule 1).
|
||
5. **The DO-NOT-TOUCH set holds:** AP-131, AD-60, AP-145 (modulo 5.1 red
|
||
branch), AD-61/62, AP-135, AP-141–146; the #275 surface files' executable
|
||
code; the six probe flags; the #316-preserving test pair.
|
||
6. **No skips.** The suite ends at 0 failed with the same 4 skips as
|
||
baseline — a new skip is a contract violation (process rule (d)).
|
||
7. **Counts are measured and reconciled.** The final suite total will move
|
||
(deleted legacy tests down, re-points and two parity tests up); the commit
|
||
message reconciles the net against baseline 11,112 explicitly (N deleted,
|
||
M added, expected total), never hand-waves it.
|
||
|
||
---
|
||
|
||
## 7. Gates
|
||
|
||
- **Complete Release suite** (`dotnet test AcDream.slnx -c Release -m:1` with
|
||
`ACDREAM_PAK_PATH` set), baseline **11,112 passed / 4 skipped / 0 failed**
|
||
at `392c1e22` — re-measured at slice start AND at each commit. Known flakes,
|
||
never conflated (they have been conflated twice): **#302**
|
||
(`PortalProjectionTests.ClipToRegion_FrameOwnedStore_…`, GC-allocation,
|
||
App.Tests — the `WarmedSteadyContactRefreshDoesNotAllocate` look-alike is
|
||
this class) and **#308** (`NakEmissionTests.LossSoak_…`, wall-clock,
|
||
Core.Net.Tests, full-suite load only).
|
||
- **NO connected gate for C5a — argued, not assumed.** Process rule (g): a
|
||
gate must be able to see the defect it gates. C5a's reachable defect
|
||
classes are (i) a compile break — seen by the build; (ii) silent coverage
|
||
loss — seen only by §3's dispositions and the review, invisible to any live
|
||
session; (iii) a behavioural regression — **structurally excluded** by
|
||
invariant 1: the production binary's reachable code is byte-equivalent, so
|
||
a connected session would exercise identical behaviour and measure nothing.
|
||
Precedents: route 5 recorded "no live gate can exist" rather than inventing
|
||
one; route 6 was a zero-production-line closure. The 5.1 red-branch commit,
|
||
if taken, ALSO needs no connected gate: #318's evidence channel is the
|
||
composition test **by design** — the C4 handoff explicitly refused to score
|
||
the connected route against it.
|
||
- **The review IS the coverage gate.** One dual review over the combined
|
||
slice diff (deletions + dispositions + parity tests), reviewers on Opus per
|
||
the standing audit rule, with §3's table as the review checklist: for each
|
||
of the seven cases, the reviewer confirms the disposition was executed as
|
||
pinned or the deviation argued.
|
||
|
||
---
|
||
|
||
## 8. Size, commit plan, and the split call
|
||
|
||
Calibration: campaign landings ran ~127 (route 7) to ~418 (route 3) to
|
||
~500 (4b-2) production lines each under full discipline.
|
||
|
||
| Piece | Production lines | Test lines | Risk |
|
||
|---|---|---|---|
|
||
| D1–D5+D7 deletions + §2 xmldoc | ~490 deleted, ~0 added | ~1,500–2,000 deleted/re-pointed across ~25 files | Low — compile-loud; the §3 dispositions are the judgment work |
|
||
| §5.1 #318 composition test | 0 (green) / ~10–40 (red branch, own commit) | ~150–300 | Low; red branch is a decision point, pre-planned |
|
||
| §5.2 route-2 B2 test | 0 | ~100–200 | Low |
|
||
|
||
**Call: C5a HOLDS as one slice, in two (possibly three) ordered commits under
|
||
this single contract:**
|
||
|
||
1. **Commit 1 — the two parity tests** (test-only). Lands first: independent
|
||
of the deletions, de-risks review, and settles 5.1's green/red question
|
||
before the sweep. If 5.1 is red, its fix is **commit 1b** (own review
|
||
round, retires AP-145, closes #318) before proceeding.
|
||
2. **Commit 2 — the deletion sweep**: D1–D5, D7, §2 xmldoc, all §3
|
||
dispositions, AP-1 + AD-1 row deletions. One diff, reviewable as one unit.
|
||
|
||
The ~490-line figure is at the top of the campaign's calibration band, but a
|
||
deletion of caller-free code is a different risk class from route 3's ~418
|
||
changed lines — the compiler proves most of it. What justifies keeping it
|
||
whole rather than splitting D1/D2/D3 (Core) from D4/D5 (Runtime): AP-1's
|
||
retirement evidence spans BOTH groups ("the last resolver-shaped entry points"
|
||
includes D4/D5), so splitting would either retire AP-1 on a half-proof or
|
||
leave the register straddling two commits — both worse than one larger
|
||
reviewable deletion. Do NOT fold in: #276's remainder, #317's audit, any
|
||
probe change (including the gate-4 `cause=` label improvement — C5c), or any
|
||
#275-adjacent edit.
|
||
|
||
---
|
||
|
||
## 9. What moved between the scoping (`09911821`, at `52175aa1`) and this contract (`392c1e22`)
|
||
|
||
1. **`AdjustPosition` is a second production survivor inside the deletion
|
||
region** (`PhysicsCameraCollisionProbe.cs:38,:100`) — the scoping's hazard
|
||
note named only `IsSpawnCellReady`. A region-wise delete would have taken
|
||
the camera collision probe's cell resolver with it. §2 pins member-wise
|
||
deletion.
|
||
2. **#319 created a `.Resolve(` name collision**: `ParentAttachmentState.Resolve`
|
||
(`:432`), called from two files. The scoping's census predates it. Callers
|
||
must be typed, not counted (§3.8).
|
||
3. **AD-60's legacy-half site moved to `RuntimeEntityObjectLifetime.cs:1926`**
|
||
(scoping said `:1918`; the register row still says `:1338`). C5a doesn't
|
||
touch it, but C5b's contract must cite by symbol.
|
||
4. **Begin* wrapper census: 39 sites / 9 files** (scoping: ~40 / 10).
|
||
Immaterial to the disposition.
|
||
5. **The scoping's D7 path `ConstraintManager.cs` is actually
|
||
`Motion/ConstraintManager.cs`** (`src/AcDream.Core/Physics/Motion/`).
|
||
6. **Everything else in the scoping's §1a/§1c/§2/§3 holds exactly at
|
||
`392c1e22`**: zero production callers re-proven for all six groups; #319
|
||
added no caller to any deletable symbol; the #319-touched file set is
|
||
disjoint from every deletion target; the seven dispositions carry forward
|
||
unchanged in substance.
|
||
7. **New since the scoping, absorbed here:** gate 4 closed 2026-08-05 as a
|
||
probe-label artifact (`af828a8a`) — the cell-less falsification is no
|
||
longer C5-gate-session work; and route 7's gate criterion was corrected to
|
||
the positive child-cell-equals-parent assertion (`2687d893`), whose
|
||
still-owed connected run belongs to the C4/#319 ledger, not to C5a.
|