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>
This commit is contained in:
Erik 2026-08-05 09:26:11 +02:00
parent 52175aa14a
commit 0991182198

View file

@ -0,0 +1,357 @@
# 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:161`
`LiveEntityRuntime.cs:2550``RuntimeEntityObjectLifetime.TryApplyPosition`
`:1781``RuntimeEntityDirectory.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:1918` — `RefreshSnapshot(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.