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>
29 KiB
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) andRuntimeEntityObjectLifetime's wire-derivedFullCellIdrefresh (therefreshPosition: acceptedPositioncall, now at:1926post-#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:
IsSpawnCellReady(PhysicsEngine.cs:1807) — production callersRuntimeSetPositionState.cs:2169,:4378andSessionPlayerComposition.cs:374. Sits betweenHasCellSurface(delete) andResolve(delete).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 betweenIsSpawnCellReadyandResolve— 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 ifRuntimeLocalPlayerPhysicsPublicationStateTestsprovably covers the same transaction shape (cite which test).PlayerMovementControllerTests.cs:1158andRuntimeLocalPlayerPhysicsPublicationStateTests.cs:3007both assertThrows<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 atEquippedChildRenderController.cs:920andRuntimeLiveEntitySessionController.cs:390. A mechanical grep for.Resolve(now hits relation resolution — neither site isPhysicsEngine. 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:
- The "legacy resolver" is
PhysicsEngine.Resolve/ResolvePlacement. The exhaustive receiver census (§1) shows zeroPhysicsEngine.Resolveor.ResolvePlacementcall sites insrc/. - Every production placement writer reaches Core
PhysicsEngine.SetPositiononly throughRuntimeSetPositionState(:2028,:3125,:4789— the only three_physics.Engine.SetPositionsites insrc/). - 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.
- 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
TryApplyPositionunconditionalinstallPlacementFrame: true, clearParent: trueis still the ONLY steady-state production Position merge at HEAD. - AD-60's legacy half — same gate (#275): the
RefreshSnapshot(..., refreshPosition: acceptedPosition)site —:1926at HEAD (the scoping's:1918and the register's:1338are both stale; cite the symbol) — still derivesFullCellIdfrom 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 merelyLocalPlayerShadowState's dedup cache. AP-145's bypass (RuntimePlacementPresentationSink.cs:243_localPlayerShadow.Set(...)skippingLocalPlayerShadowSynchronizer.SyncPose's publish) both skips the publish AND pre-seedsSyncPose'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
- 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.
- The two survivors survive.
IsSpawnCellReadyandAdjustPositionkeep their exact executable bodies and all production callers (RuntimeSetPositionState.cs:2169,:4378;SessionPlayerComposition.cs:374;PhysicsCameraCollisionProbe.cs:38,:100). - 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.
- 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).
- 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.
- 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)).
- 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:1withACDREAM_PAK_PATHset), baseline 11,112 passed / 4 skipped / 0 failed at392c1e22— 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 — theWarmedSteadyContactRefreshDoesNotAllocatelook-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:
- 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.
- 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)
AdjustPositionis a second production survivor inside the deletion region (PhysicsCameraCollisionProbe.cs:38,:100) — the scoping's hazard note named onlyIsSpawnCellReady. A region-wise delete would have taken the camera collision probe's cell resolver with it. §2 pins member-wise deletion.- #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). - 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. - Begin wrapper census: 39 sites / 9 files* (scoping: ~40 / 10). Immaterial to the disposition.
- The scoping's D7 path
ConstraintManager.csis actuallyMotion/ConstraintManager.cs(src/AcDream.Core/Physics/Motion/). - 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. - 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.