acdream/docs/research/2026-08-05-c5-scoping.md
Erik 0991182198 research: scope C5 — the deletion is smaller than the plan implies, and half the rows are blocked on a behaviour change
Verified by symbol at HEAD 52175aa1, behind two exhaustive caller censuses
(legacy placement writers; the probe family).

DELETION INVENTORY: 6 deletable symbol groups (~540 production lines), 2 more
deletable only via #275, and 5 that the plan or handoff names as deletable but
are NOT. Deletable now, all with zero production callers: PhysicsEngine.Resolve
(~360 lines — this IS AD-1's legacy demote/lift body) plus its private
HasCellSurface; PhysicsEngine.ResolvePlacement; PlayerMovementController
.SetPosition (both overloads); CommitPreparedPosition; the BeginAcceptedPlacement
/BeginAuthoredPlacement test wrappers. Hazard recorded: production
IsSpawnCellReady sits physically inside the Resolve block.

THE PLAN'S FRAMING IS WRONG FOR HALF THE ROWS. AP-1 and AD-1 are provably
retirable in the deletion commit — both rows' "production still routes through
the legacy resolver" sentences are already false at HEAD. But AP-131 and
AD-60's legacy half are blocked on #275, which is a BEHAVIOUR CHANGE
(classify-then-merge on every steady-state Position, retail Gate A, wire-cell
withhold across a 45+ site residency-predicate blast radius) — not a deletion.
"C5 deletes the paths and the rows retire" holds for two rows and not the
other two.

SEVEN TEST-ONLY-CALLER CASES, each with a disposition, because this is where a
deletion slice silently removes coverage: Resolve's unit tests (behaviour gone
— delete); Issue133DungeonTeleportPrefixTests (behaviour MOVED — re-point, it
is a named-bug regression pin); InitialPlacementOverlapTests and
TransitionScratchDifferentialTests (verify-then-delete / re-point the
differential arm); ~19 files using PlayerMovementController.SetPosition as
fixture setup (mechanical re-point); CommitPreparedPosition's tests (re-point
at the leash-arm replacement); the Begin* wrappers' ~40 sites (keep as a seam).
Also flagged: the #316-preserving test pins a defect verbatim and inverts by
design once measured.

THE CELL-LESS TRIGGER, ESTABLISHED BY READING rather than deferred to a live
session: pickup-then-drop. TryApplyPickup leaves an ACTIVE cell-0 record, and
ACE's drop sends that guid a bare UpdatePosition (Player_Inventory.cs:1443)
with no client-side delete. What remains unknown is only whether a CreateObject
from NotifyPlayers (Landblock.cs:900) precedes it on the ordered stream, which
depends on when ACE populates the fresh item's known-players set — a two-minute
probe run settles it, with route 5's "the gate cannot exist, record that" as
the honest fallback.

SEQUENCING: run the owed gates FIRST as one cheap user session on the current
binary (route-7 thickening, the cell-less falsification, #316's measurement),
then C5a (deletions + #318 + route-2's B2 parity test + retire AP-1/AD-1), then
C5b (#275 + AP-131/AD-60, own contract and dual review), then C5c (closeout
gates after #280 per the plan's own ordering, the ~1,340-line probe strip, the
ledger). The probe strip goes LAST because the owed gates depend on the probes,
with a REMOTE_LANDING carve-out if #316 stays unmeasured.

Seven false or stale plan claims found and listed, including AP-1/AD-1's own
row texts, ILocalPlayerTeleportPlacement being called deletable when it is a
live post-commit suffix, and the 4b "to delete" list being overtaken — the
pre-op ConstrainTo and duplicate constants are already gone and the remainder
is now canonical post-collapse.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 09:26:11 +02:00

30 KiB
Raw Blame History

C5 scoping — legacy deletion + closeout gates (2026-08-05)

Scoped at HEAD 52175aa1 (branch claude/acdream-physics-divergence-5aa784, identical to main — C4 merged and pushed today). Every claim below was verified by symbol at this HEAD, not inherited from a planning document; where a planning document turned out wrong, §8 says so explicitly. Inputs: the C4 closeout handoff (2026-08-05-c4-closeout-handoff.md), the campaign plan (2026-08-02-placement-cutover.md), register rows AP-1 / AD-1 / AP-131 / AD-60 / AP-141..145, and two exhaustive caller censuses run against this worktree (legacy placement writers; the temporary probe family).

One-paragraph verdict: C5 is three different kinds of work wearing one slice name. (1) A deletion sweep that is smaller than the plan implies — most of the big deletions already landed inside C4's routes; what is left is ~500 production lines, all with test-only callers, plus large mechanical test churn. (2) #275, which is NOT a deletion: it is a behaviour change on the steady-state inbound-Position merge (the hottest wire path), and it is the only thing that can retire AP-131 and AD-60's legacy half. (3) The closeout gates + probe strip + ledger. These must not ride in one landing; the split is drawn in §7.


1. The deletion inventory, verified at HEAD by symbol

1a. Deletable now — zero production callers, test callers only

Every entry below was grepped across all of src/; the caller lists are exhaustive at 52175aa1.

# Symbol Where Production callers Test callers ~Lines
D1 PhysicsEngine.Resolve src/AcDream.Core/Physics/PhysicsEngine.cs:1863-2222 ZERO (the live per-tick method is ResolveWithTransition, a different member) tests/AcDream.Core.Tests/Physics/PhysicsEngineTests.cs (12 sites) + Issue133DungeonTeleportPrefixTests.cs:58 ~360
D2 PhysicsEngine.HasCellSurface PhysicsEngine.cs:1767-1783 only Resolve itself (:1887) — deletes with D1 none ~17
D3 PhysicsEngine.ResolvePlacement PhysicsEngine.cs:2748-~2810 ZERO (its retirement is already recorded as done in HeadlessSessionWorldProjection.cs:794) InitialPlacementOverlapTests.cs:42; TransitionScratchDifferentialTests.cs:185,194,208,217 ~65
D4 PlayerMovementController.SetPosition (both overloads) src/AcDream.Runtime/Gameplay/PlayerMovementController.cs:1746,1760 ZERO (only stale doc-comment mentions at ConstraintManager.cs:25, PhysicsBody.cs:442 — both also still name the long-deleted BlipPosition) ~19 test files, used as fixture setup, not as subject (see §3) ~30
D5 PlayerMovementController.CommitPreparedPosition PlayerMovementController.cs:1789 ZERO (production replacement is ArmConstraintLeashAtCommittedPlacement:1815, called from RuntimeLocalPlayerPhysicsPublicationState.cs:774) 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, which production reaches via Apply (:1304) and the authored sequence (:1466) ~40 call sites across 10 Runtime test files ~25
D7 Doc hygiene tombstone comment LiveEntityNetworkUpdateController.cs:2469-2478; stale BlipPosition refs above; stale TryApplyPickup (:1116) citation in RuntimeAcceptedPositionDriveControllerTests.cs:221 (the method is now at RuntimeEntityObjectLifetime.cs:1258) ~20

Deletion hazard inside D1/D2's block: IsSpawnCellReady (PhysicsEngine.cs:1807) sits physically BETWEEN HasCellSurface and Resolve and is production (RuntimeSetPositionState.cs:2169,4378; SessionPlayerComposition.cs:374). A region-wise delete takes it by accident.

Production-line total for 1a: ~540 lines, all compile-loud (see §3 for why that matters).

1b. Deleted only BY #275 — cannot be deleted standalone

These two sites are the code bodies of AP-131 and AD-60's legacy half. They have a live production caller — the steady-state Position merge — so deleting them is not a sweep item; it is the #275 behaviour change (§2).

  • InboundPhysicsStateController.TryApplyPosition (the simple overload, src/AcDream.Runtime/Entities/InboundPhysicsStateController.cs:610, which hardcodes installPlacementFrame: true, clearParent: true at :662-663). Production path at HEAD: LiveEntityInboundAuthorityGate.cs:161LiveEntityRuntime.cs:2550RuntimeEntityObjectLifetime.TryApplyPosition :1781RuntimeEntityDirectory.cs:654 → this overload. The route-flag-threaded overload (:681) already exists and is what the continuation executor uses (RuntimeInitialCreateContinuationExecutor.cs:1991-1992).
  • RuntimeEntityObjectLifetime.cs:1918RefreshSnapshot(canonical, snapshot, refreshPosition: acceptedPosition): the wire-acceptance-derived FullCellId write AD-60 names as "part of the AP-1 divergence this campaign is removing". The executor's equivalent already withholds it (refreshPosition: false, executor :1996).

1c. Named by the plan/handoff as sweep material but NOT deletable — with reasons

This is where the plan is most wrong at HEAD. Five items:

  1. ILocalPlayerTeleportPlacement / LocalPlayerTeleportPlacement.Place (src/AcDream.App/Streaming/LocalPlayerTeleportController.cs:176,207-284). The handoff lists it as a "sweep candidate (now a thin acknowledge seam)". It IS thin, but it is live production: route 3 kept it as the post-commit presentation suffix (target-watcher NotifyTeleported, camera reset, spatial reconcile) and the A10 review fix explicitly decided its redundant WorldEntity pose writes are "kept because they cost nothing". Disposition: no deletion. Optional zero-risk simplification (inline the interface) is not worth review cost; recommend leaving it and striking it from the sweep list.
  2. PlayerMovementController.SetPositionCore (:1845-1920) — STILL PRODUCTION via PreparePositionForCommit (:1782) ← RuntimeLocalPlayerPhysicsPublicationState.cs:219 (the first-entry publication candidate build). The AD-61 force-seed (Contact|OnWalkable|Active at :1860-1863) is still inside it — filed, argued inert (overwritten by the faithful activation commit + settle), and not a C5 retirement target. Note the tension the census surfaced: the route-3 comment at PlayerMovementController.cs:1975-1985 repudiates this exact overwrite for the teleport path while first-entry still runs it — that is AD-61's documented state, not new debt.
  3. The route-4 leftovers the 4b plan listed as "4b's to delete" — the plan text is overtaken. At HEAD: the pre-operation unconditional ConstrainTo is already deleted (tombstone at LiveEntityNetworkUpdateController.cs:2469-2478; the only arming site is the post-operation one at :2853); the duplicate 96f/4f constants are already deleted (zero hits in the file); the !update.IsGrounded early return (:2622-2627) is no longer a player-arm carve-out — since the OnPosition collapse it is the guid-blind D2 wire-airborne return-0 shape (retail MoveOrTeleport @0x0051636D), i.e. canonical, not legacy; and ApplyRemoteContactRouting (:1175-1348) is the canonical five-arm post-collapse routing with exactly one production caller. None of these is C5 deletion material.
  4. AP-135's bookkeeping (RemoteMotion.CellId writers at :1392,:1622; LastServerPos/LastServerPosTime at :1393-1394,:2895-2896,:786) — deliberately retained per the row's own retirement condition (retires with the free-fall sweep gate, which C5 does not touch). Keep.
  5. RuntimeLiveEntitySessionController.cs:141 — the content-less headless host's pre-flip direct RegisterEntity path, the one forward-looking "C4/C5 revisit" comment in all of src/. Its stated unblock condition ("once the direct-host conductor drive no longer requires prepared content") is not met at HEAD. Carry, do not delete.

Also checked and confirmed already-deleted (comment references only): RemoteTeleportController, RemoteTeleportPlacement, LocalForcePositionTransaction, HeadlessSessionWorldProjection.BlipLocalPlayer, ResynchronizeLocalPlayerForPortalArrival, LocalPlayerTeleportPlacement's old duplicate Place authority, the ClassifyLeaveWorld family, BlipPosition. LiveEntityRuntime has no legacy CreateObject position seed; HeadlessSessionWorldProjection has no remaining direct placement writer; PhysicsEngine.SetPosition (Core) is called only from RuntimeSetPositionState (:2028,:3125,:4789) in production. PhysicsBody.SnapToCell's eleven src callers are three canonical-owner sites, two per-tick simulation sites, and five projectile/static/first-entry body seeds that are live by design (AP-141 pinned the projectile no-machinery decision) — none deletable.


2. Register rows: provable retirements vs blocked

Row Verdict Evidence
AD-1 RETIRABLE in the deletion commit. Its divergence sentence — "Production authoritative placement still routes through the legacy recoverable outdoor demote and outdoor-restore max(terrainZ, z) lift" — is already false at HEAD: that code is PhysicsEngine.Resolve (:1863-2222; demote at :1906, lift at :2168-2173) and it has zero production callers. Its listed prerequisites (authored-mover, rebucketing, prefix-quiescence, body-publication, route cutover) all landed C0C4. Deleting D1/D2 makes the retirement structural rather than merely observational. census §2; PhysicsEngine.cs:1863
AP-1 RETIRABLE in the deletion commit. "Production zero-delta routes deliberately remain on the legacy resolver until 4B2" is stale: every production placement writer now reaches Core SetPosition only through RuntimeSetPositionState; the local controller's body adoption / sealed setter landed at C3c; deleting D1/D3/D4/D5 removes the last resolver-shaped entry points (all already caller-free). 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. census §§2,3,4,7
AP-131 BLOCKED — retires only with #275, which is a behaviour change, not a deletion. The legacy caller AP-131 says "is deleted at the production cutover" is still the ONLY steady-state production Position merge at HEAD (chain in §1b), still passing unconditional true/true. Retiring it means computing the classified ApplyPlacementFrameBeforeRouting / UnparentBeforeRouting flags (plus retail's FORCE_POSITION Gate A skip) BEFORE the merge on the steady-state path, exactly as the executor already does for residence-pending entities. That changes what every ordinary inbound Position does to animated and parented entities — retail-correcting, but a live-path behaviour change requiring the full contract + dual-review discipline. InboundPhysicsStateController.cs:662-663; executor :1991-1992
AD-60 legacy half BLOCKED — same gate, #275. RuntimeEntityObjectLifetime.cs:1918 still derives FullCellId from bare wire acceptance on every accepted steady-state Position (the 4b-3 D1 comment at :1886-1899 states it plainly). Withholding it (executor's refreshPosition: false rule) moves residency changes onto placement/simulation commits only — the retail rule — but FullCellId is the residency predicate at 45+ sites (AP-142's own count), so the blast radius is real and this must not be smuggled into a sweep. RuntimeEntityObjectLifetime.cs:1915-1918

Rows adjacent but NOT C5 targets: AD-61 (force-seed, deliberate), AD-62 (non-commit force outcomes), AP-135 (retained bookkeeping), AP-141145 (all filed with their own conditions; AP-145 retires with #318's fix, not its test — the test only makes the asymmetry assertable).


3. THE DANGEROUS CASE — every test-only-caller path, with disposition

First a sharpening the scoping question deserves: deleting a method whose callers are tests is compile-loud (the test project breaks), same as a production caller. The genuinely silent case is the disposition of the tests afterward: deleting a test that pinned behaviour which MOVED (instead of re-pointing it) is the silent coverage loss. Per item:

Path Test callers Behaviour gone or moved? Disposition
PhysicsEngine.Resolve PhysicsEngineTests.cs ×12 Gone — the outdoor-demote/floor-snap/terrain-lift semantics are legacy-only; the canonical replacements have their own suites (PhysicsSetPositionTests, RuntimeSetPositionStateTests) Delete tests with the path, after a one-pass audit that no individual assertion pins something canonical-owned (e.g. AdjustPosition behaviour, which survives — tests touching it re-point at AdjustPosition directly)
PhysicsEngine.Resolve Issue133DungeonTeleportPrefixTests.cs:58 Moved — #133's invariant (landblock-prefix correctness on dungeon teleport claims) is now the canonical portal/teleport placement's job Re-point at the canonical path (drive a teleport-classified RuntimeSetPositionState placement with a prefixed dungeon claim and assert the committed cell), or prove an existing canonical test already pins it and record that in the deleting commit. Do NOT silently delete — this is a regression pin for a named historical bug
PhysicsEngine.ResolvePlacement InitialPlacementOverlapTests.cs:42 Moved — placement-ring occupancy search is ported inside canonical SetPosition (find_placement_position, covered by PhysicsSetPositionTests) Verify-then-delete: confirm PhysicsSetPositionTests covers the occupied-spawn ring-search case this test pins; if not, re-point it at Transition.FindPlacementPos via the canonical entry before deleting
PhysicsEngine.ResolvePlacement TransitionScratchDifferentialTests.cs:185-217 Moved — these are Slice-I zero-alloc differential arms Re-point or drop the arm explicitly: if ResolvePlacement was one of the differential's measured entry points, replace with the surviving canonical entry; a silent drop would shrink the I-slice differential's coverage without anyone deciding it
PlayerMovementController.SetPosition ~19 test files (~45 sites in PlayerMovementControllerTests alone) Neither — these tests USE it as fixture setup (position the player), they do not test it Mechanical re-point through one shared test helper that drives the production seeding path (publication PreparePositionForCommit → activation), or — cheaper and honest — keep ONE explicitly-named internal test-seed method with a doc comment stating it exists for fixtures only. Either way the fixture semantics (grounded start via the force-seed) must be reproduced deliberately, since dozens of movement tests assume a grounded, zero-velocity start
PlayerMovementController.CommitPreparedPosition 3 sites Moved — replaced by ArmConstraintLeashAtCommittedPlacement Re-point: the prepared-position transaction assertions should run against the production arm/commit pair; RuntimeLocalPlayerPhysicsPublicationStateTests:3007 likely already half-covers it — audit before deciding delete-vs-rewrite per test
BeginAcceptedPlacement/BeginAuthoredPlacement wrappers ~40 sites, 10 files Neither — the CORE (BeginAcceptedPlacementCore) is production; the wrappers are test conveniences Two defensible calls: (a) keep the wrappers, documented as test seams (zero churn, zero coverage change); (b) delete and mechanically re-point every site at Apply/the authored sequence. The ~40-site churn buys nothing behavioural; recommend (a) unless the campaign wants the sweep to be absolute — in which case budget the churn (it is mechanical but touches the Runtime suite broadly)
InboundPhysicsStateController.TryApplyPosition (true/true overload) — #275's deletion its unit tests assert the UNCONDITIONAL flags Deliberately changed — the old assertions describe the divergence being removed Rewrite, never delete-only: each unconditional-flag assertion becomes a classified-flag assertion (animated entity keeps its placement frame; ForcePosition on a parented entity does NOT unparent — retail Gate A), sabotage-verified both directions per process rule (e)

One more test-shaped landmine, inherited not created: the #316-preserving pair (LandingPacket_PlayerGuid_QueueClearedNoShadowPublish_316Preserved / ..._CreatureGuid_ShadowPublishedQueueNotCleared) pins a defect preserved verbatim. If C5's connected session measures #316 and the fix lands, that test inverts by design — do not "fix" the test without the measurement.


4. The C4 inheritance, sized

Item What it is Size / shape
#318 composition test Drive a real portal arrival through the canonical drive controller + REAL RuntimePlacementPresentationSink + REAL PhysicsEngine; assert PhysicsEngine.ShadowObjects holds a row at the destination (never just LocalPlayerShadowState's dedup cache — AP-145's TryPublishPlace.Set() bypass both skips the publish AND pre-seeds SyncPose's dedup so the next tick can skip too; a cache-only assertion is satisfied by the bug) plus the T8 write ordering Test-only, ~150300 lines, App.Tests. The wiring cost is composition (real sink + real engine in one fixture), not assertion count. Retires nothing by itself; it makes AP-145 falsifiable
AP-145 asymmetry The cache-without-publish seam itself NOT a C5 fix unless the composition test proves a live miss; the row argues why it self-heals in practice. Land the test first, decide after
Route-2 B2 parity test The plan's recorded acceptance gap (plan §C4 route 2): no test drives an accepted ForcePosition end-to-end through the presentation sink and asserts the render entity moved from the committed receipt ~100200 test lines. Flag: this deliverable is in the PLAN but absent from the handoff's "What C5 inherits" list — carry it explicitly so it doesn't fall between documents
#276 remainder SpawnPlacementSettler.TrySettle discards settle.CellId ~3080 production lines + a conformance test; NOT named a C5 deliverable by the plan — carry unless cheap to fold; does not block AP-1 (separately filed)
#277 far-Create service-window conversion Trigger-conditioned (no radius changed); zero C5 work
#313 DeclareValid selection transfer Selection UX, outside placement; carry
#316 player-arm landing never publishes the shadow — measure before fixing; the named instrument is ACDREAM_PROBE_REMOTE_LANDING The measurement is a two-client landing observation — fold it into the early gate session (§5); the fix (if the #184 class) is its own later commit
#317 velocity-chain retail audit Research-only (decomp), no code; carry unless the session has slack
#309 narrow half GotoLostCell hidden-until-reenter_visibility Carried, re-scope before running; not C5
#280 portal destination prefetch Its own slice with its own visual gate. Ordering note the handoff does not restate: the campaign plan's numbered order (items 3→4→5) puts #280 BEFORE C5's final-binary soak/matrix — sensible, since #280 changes reveal behaviour the matrix would exercise. C5's implementation slices are independent of it and can proceed; the final closeout gates should run after #280 lands, or the matrix will need re-running
Probe strip six TEMPORARY flags ~1,340 lines total: REMOTE_SLIDE ~700, REMOTE_LANDING ~300, LOCAL_TELEPORT ~215, REMOTE_TELEPORT ~50, PARK ~41, CHILD_CELL ~33. All env-only (no DebugPanel mirrors), zero test references — the strip is test-free. Hazards recorded by the census: keep ParseHexIdList (shared with permanent ACDREAM_DUMP_*), keep the DISTINCT older ACDREAM_PROBE_TELEPORT/LogTeleport block that sits adjacent to LogLocalTeleportArrival (two flags, documented distinct at RuntimeAcceptedPositionDriveController.cs:769-770), keep the probe-aliased production members (BodySnapThreshold, InterpolationManager queue state, bodyOnWalkableAtTickStart), and trim ResetForTest line-wise, not wholesale

5. Gate sequencing — and the cell-less trigger, answered by reading

State at HEAD: three of C4's four gates were run and user-passed on 2026-08-05 (route 3 unambiguous incl. the autorun-cancel line; route 6 visual; route 7 pass but THIN — one cause=propagate). Owed: (a) route 7 thickening (several equipped landblock crossings, expect double-digit propagate), and (b) gate 4, cause=cellless, whose recorded recipe route 7 invalidated.

The cell-less trigger — what reading establishes

The classifier's cell-less arm needs PreMergeCommittedCellId == 0, which requires an ACTIVE canonical record whose FullCellId is 0 at merge time (RuntimeEntityObjectLifetime.TryApplyPosition:1898 — a fully-withdrawn record yields null, "no opinion", not 0). The production population of such records exists and is well-defined:

  1. Post-pickup items: TryApplyPickup (:1258) commits SetFullCell(canonical, 0u, 0u) at :1314 and leaves the record ACTIVE (withdrawn presentation, suspended clock, but in the directory).
  2. Children zeroed by WithdrawCommittedChildrenToCellless on parent delete — but ACE deletes tracked equipment alongside its wielder (Player_Tracking.cs:108-112), so these tombstone rather than linger.
  3. Unwield — no longer cell-less (route 7 D1/D2: committed child carries the parent's cell), which is exactly why the old recipe died.

The one ACE message that delivers a bare UpdatePosition to population (1): dropping the item. HandleActionDropItem (references/ACE/.../Player_Inventory.cs:1437-1443) sends the dropper GameMessageUpdatePosition(item) directly after TryDropItem — and for RemoveFromInventoryAction.DropItem no client-side delete precedes it (no InventoryRemoveObject, no DeleteObject; :222-247), so the client-side record is still the active cell-0 one from the pickup.

The deciding nuance, which reading could not fully settle: inside TryDropItem, Landblock.AddWorldObjectInternal calls wo.NotifyPlayers() (Landblock.cs:900) which sends CreateObject to every player in the item's freshly-created PhysicsObj.ObjMaint known-players set — all enqueued on the same ordered reliable stream BEFORE the UpdatePosition at :1443. If the dropper is in that set at that instant (populated synchronously during AddPhysicsObj), the Create precedes the Position, the Position lands in the initial-create residence branch, and it classifies against the Create's own resolved cell — not cell-less. If known-player registration is deferred to the player's next visibility tick, the UpdatePosition arrives first and IS the live cell-less trigger.

Recommendation: a bounded two-minute falsification run, not an open investigation: with ACDREAM_PROBE_REMOTE_TELEPORT=1, pick up a ground item, drop it, repeat a few times (outdoors + once indoors), and read the [remote-teleport] lines. Either cause=cellless appears (gate 4 closes with a recorded recipe: pickup-then-drop), or it provably takes the Create path — in which case record the cell-less arm as synthetic-fixture-covered by design with no reachable live trigger against ACE, exactly the route-5 precedent ("when a gate cannot exist, record that, never a substitute"), process rule (g). Do not leave gate 4 on file as pending indefinitely.

Sequencing

Run the owed gates FIRST, before any C5 implementation, as one cheap user session on the current binary: route-7 thickening (PROBE_CHILD_CELL) + the cell-less falsification (PROBE_REMOTE_TELEPORT) + #316's measurement (PROBE_REMOTE_LANDING; a second client jumping/landing while observed). Rationale: (a) C4's ledger closes on evidence from the binary family its routes were gated on; (b) if thickening or the cell-less run surfaces a defect, C5's deletion inventory could change; (c) all three consume probes the strip will later delete. C5 absorbs the bookkeeping of these gates (ledger updates), not their execution debt.


6. Probe-strip ordering

The strip is last — after every connected gate in this campaign has consumed its probe evidence, as its own commit:

  1. Owed-gate session (§5) consumes CHILD_CELL, REMOTE_TELEPORT, REMOTE_LANDING.
  2. C5 implementation slices land (deletions, #275, tests) — probes untouched; they are env-gated and production-inert.
  3. C5 closeout gates run on the final implementation binary — the lifecycle/reconnect route re-exercises portals, so LOCAL_TELEPORT evidence remains readable if anything regresses; keep the probes through this.
  4. Strip commit (~1,340 lines, hazards per §4), then full Release suite + one short lifecycle route as the strip's own gate. The strip is behaviour-neutral by construction (env-gated emission only), so the final-binary soak from step 3 remains valid evidence; the post-strip re-run is the cheap proof of neutrality.

Conditional carve-out: if #316's measurement does NOT happen in the §5 session, ACDREAM_PROBE_REMOTE_LANDING must be excluded from the strip (it is the issue's named instrument) and the family stripped five-wide with a note — do not let a hygiene pass delete an open issue's measurement apparatus.


7. Size and split recommendation

Calibration: this campaign's landings ran ~127~500 production lines each (4a 364, 4b-2 350500, 4b-3 ~250, r5 ~131, r7 ~127, r3 ~418), each with full contract/dual-review discipline.

Piece Production lines Test lines Risk
Deletion sweep (§1a) ~540 deleted ~25 files touched; ~1,5002,000 test lines deleted/re-pointed Low (all compile-loud), but the §3 dispositions are judgment work
#275 unification (§1b/§2) ~150400 changed on the hottest inbound path ~300600 rewritten/added High — behaviour change on every steady-state Position; 45+ residency-predicate blast radius for the AD-60 half
#318 + route-2 B2 tests 0 ~250500 Low
Probe strip ~1,340 deleted 0 Low, hazard-listed
Gates + ledger/docs 0 0 User time

Combined this is far past the ~500-line calibration, and #275 is a different RISK CLASS from everything else. Split into three landings plus the pre-session:

  • C5-gate session (first, user-run): route-7 thickening + cell-less falsification + #316 measurement. Closes C4's ledger.
  • C5a — deletion sweep + tests: §1a deletions, §3 dispositions, #318 composition test, route-2 B2 parity test, retire AP-1 and AD-1 in the same commit as the deletions (register rule 1). Deletion-only + test-only; reviewable as one diff.
  • C5b — #275: pinned contract (the executor's classify-then-merge order is the template; retail Gate A is the FORCE_POSITION skip), single implementer, dual reviews, sabotage-verified flag tests both directions. Retires AP-131 and AD-60's legacy half in its own commit. This slice is where process finding (a) — "verify the load-bearing premise in code at implementation start" — applies to the classifier-flag semantics before building on them.
  • C5c — closeout: final-binary complete suite, lifecycle/reconnect route, canonical nine-stop soak, two-client observation, user visual matrix (sequenced after #280 per the plan's own ordering, §4), then the probe strip + its neutrality gate, then register/roadmap/milestones/memory + the successor handoff, close #275, close the campaign ledger.

8. Plan claims found false or stale at HEAD (the seventh-stale-doc guard)

  1. Campaign plan / handoff framing that C5's register retirements are a deletion product: AP-131 and AD-60's legacy half cannot retire by deletion — they require #275, a live-path behaviour change (the plan does name "close #275", so this is a framing error, not a missing item — but a session that read only "delete every superseded legacy path" would ship the sweep and wrongly retire the rows).
  2. AD-1's and AP-1's own row texts are stale in the conservative direction: the "production still routes through the legacy resolver" sentences are already false at HEAD (PhysicsEngine.Resolve / ResolvePlacement have zero production callers). They over-claim a divergence, not a completed port — retire with the deletion commit.
  3. The handoff's sweep candidate ILocalPlayerTeleportPlacement is not deletable — it is the live post-commit presentation suffix with a production caller (§1c item 1).
  4. The 4b plan's "every legacy fallback 4a left is 4b's to delete" list is overtaken: at HEAD the pre-op ConstrainTo and duplicate constants are already gone, and the !IsGrounded return + ApplyRemoteContactRouting are the canonical post-collapse retail shapes, not deletable debt (§1c item 3).
  5. Stale line citations: the handoff's TryApplyPickup (:1116) (now :1258; the same stale :1116 lives on in RuntimeAcceptedPositionDriveControllerTests.cs:221's doc comment); ConstraintManager.cs:25 and PhysicsBody.cs:442 still document the deleted BlipPosition/SetPosition pair.
  6. The handoff's "What C5 inherits" list omits the route-2 B2 parity test that the campaign plan itself records as "carry it into C5's parity tests" — carried here (§4).
  7. The cell-less trigger is no longer "UNESTABLISHED needing its own investigation" in the open-ended sense: reading pins the candidate population, the single candidate wire path (pickup-then-drop, Player_Inventory.cs:1443), and the one ACE ordering fact a two-minute probe run settles (§5). The honest outcomes are a recorded recipe or a recorded cannot-exist — not a standing investigation.