20 KiB
Remaining physics-divergence campaign handoff — 2026-07-31
Checkpoint 2 update: Slice 4B2 prerequisite A, Runtime SetPosition collision-report ownership, is implemented in the next checkpoint. Continue with the dedicated
runtime SetPosition collision-reporting handoff, not the prerequisite-A instructions preserved below as historical context.
Purpose and stopping point
This is the deliberate handoff boundary requested after placement Slice 4B2 checkpoint 1. The repository is stopped before any production graphical or headless route submits to the canonical Runtime SetPosition owner.
The completed foundation is useful and tested, but the overall campaign is not complete. AP-1 and AD-1 remain narrowed/open. AP-22 and AD-10 remain open. Do not retire those rows until their exact automated and connected gates pass.
Exact workspace
- Worktree:
C:\Users\erikn\.codex\worktrees\af5e\acdream - Branch:
codex/port-claude-agents - Handoff code checkpoint:
270f5154(feat(runtime): expose dormant placement receipts) - Immediately preceding residence-owner checkpoint:
4c02ac42(feat(runtime): own deferred set-position residence) - Pure Core SetPosition checkpoint:
e84a388e(feat(physics): port canonical retail set-position core) - No upstream is configured for this worktree branch.
- Remotes:
origin:https://git.snakedesert.se/erik/acdream.gitgithub:git@github.com:eriknihlen/acdream.git
The handoff was written in this same worktree. The next agent should continue there rather than creating a different worktree unless the user explicitly requests it.
Worktree hygiene
The worktree intentionally reports unrelated modifications. Preserve them.
Never use git add -A, git reset --hard, or checkout/revert commands against
these paths.
At the checkpoint, AGENTS.md has a real unrelated content diff. The following
paths report modified due to existing line-ending/stat noise but have no
content diff against the index:
src/AcDream.App/Input/PlayerModeController.cssrc/AcDream.App/Interaction/PlayerInteractionMovementSink.cssrc/AcDream.App/Rendering/LiveAnimationPresentationContext.cssrc/AcDream.App/World/LiveEntityRuntime.cssrc/AcDream.Core/Physics/CellArray.cssrc/AcDream.Core/Physics/PhysicsBody.cssrc/AcDream.Headless/Hosting/HeadlessSessionWorldProjection.cssrc/AcDream.Runtime/Gameplay/PlayerMovementController.cssrc/AcDream.Runtime/Physics/RuntimePhysicsState.cssrc/AcDream.Runtime/Physics/RuntimeRemotePhysicsUpdater.cstests/AcDream.App.Tests/World/LiveEntityRuntimeTests.cstests/AcDream.Core.Tests/Physics/CellTransitTests.cstests/AcDream.Core.Tests/Physics/Issue133DungeonTeleportPrefixTests.cstests/AcDream.Runtime.Tests/Gameplay/PlayerMovementControllerTests.cstools/A8CellAudit/A8CellAudit.csproj
Before every commit, stage exact paths and inspect:
git diff --check
git diff --cached --check
git diff --cached --stat
git status --short
What is complete
Campaign baseline and issue #273
c24bc571— retail StepDown support-radius behavior for tight gaps.10b55d74— tight-gap controls and diagnostics.
Retail retry and edge/StepDown dispatcher
e5f855ac— nested per-cell collision retries.67d1e9b3— refreshed-cell retry state.4ca7230b— retained cell across inner retries.c559c48d— retail edge-response ordering.4fbd93ec— edge-slide stop semantics.1fd5da67— StepDown placement validation.acec33ec— StepDown probe state.75b6f6b6— Path-6 collision response.d3c0d9ec— TS-4 production chronology gate.
These retire AP-3, AP-4, AP-5, AD-53, AD-54, and TS-4 under the tests and research already recorded in the divergence register.
Exact cell availability and atomic collision generations
7716c2ee— retail cell-availability semantics.3e0f3b62— containment-root validation.be94bc9b— atomic collision-generation activation.d94145e6— seal before activation.6b28ff99— starvation-free activation.
These retire AD-3, AD-4, and AD-6. The active collision world remains visible until a complete replacement generation is atomically committed.
Canonical SetPosition Core and Runtime residence owner
e84a388eports the pure Core SetPosition transaction.4c02ac42addsRuntimeSetPositionState, including:- exact accepted operation ownership;
- canonical body/contact/cell/shadow/workset commit;
- authored mover retention;
- exact-cell and collision-generation wake;
- 25-second root/direct-child lost-cell lifetime;
- bounded indexed deadline structures;
- revisioned ordered Withdraw/Place/Discard host receipts;
- cancellation/GUID-reuse/reset/disposal convergence;
- structural cell/quaternion validation and bounded scatter work.
270f5154addsRuntimePlacementProjectionChannel, a public, generation-gated host seam over the one existing receipt owner:- subscribe to ordered immutable receipts;
- peek the exact FIFO head;
- retry pending receipts without recommitting Runtime state;
- acknowledge only the exact current FIFO-head token;
- observe pending receipt debt.
The channel owns no second queue and has no App or Headless production consumer. A source guard pins that dormancy. This is intentional.
Validation at the stopping point
The final channel-only checkpoint passed:
- Release solution build: 0 errors, 18 existing warnings.
- Complete Release suite: 10,279 passed, 4 skipped, 0 failed.
- Runtime SetPosition focused tests: 47/47.
- App dormancy/ownership guards: 3/3.
git diff --check: clean.- Retail-conformance re-review: clean.
- Architecture/adversarial re-review: clean.
Rejected prototype — do not resurrect it
A prototype made graphical and headless PlayerMovementController instances
borrow the canonical RuntimeEntityRecord.PhysicsBody immediately and used a
snapshot/rollback lease to recover from late construction failures. It was
fully removed before 270f5154.
The design was rejected because it was failure-atomic only without reentrancy. While the lease was open, a nested SetPosition, remote/projectile bind or update, deletion/GUID replacement, object-clock epoch change, or disposal could establish newer authority. The outer rollback could then erase that newer commit or detach a body already used by another canonical owner.
The next implementation must use one of these complete solutions:
- A Runtime-owned exclusive/versioned controller/body publication transaction respected by every canonical body writer, binding path, SetPosition operation, clock epoch transition, deletion, reset, and disposal; or
- Off-canonical preparation followed by one validated atomic Runtime commit that publishes the prepared controller/body relationship without copying stale state over newer authority.
Because every writer must participate, this belongs to the atomic production route cutover. Do not reintroduce a local snapshot lease or a Commit method that merely checks the body reference at the end.
What remains — required execution order
Slice 4B2 prerequisite A — real collision-report ownership
RuntimePhysicsState.HandleSetPositionCollisions still returns false.
Retail CPhysicsObj::SetPositionInternal (0x00515330) returns the real
per-object report/tracking result. Establish the Runtime owner for that result
and return it exactly. Do not restore the former environment/object-presence
guess.
Required tests:
- report/no-report objects;
- collided object set ordering and lifetime;
- reentrant deletion/reset;
- graphical/headless equality;
- no report state surviving GUID reuse.
Slice 4B2 prerequisite B — exact authored mover preparation
Every production preparation must pass the full SetPosition request using:
- Setup's exact ordered authored spheres;
- exact scale, including valid zero/presence semantics;
- exact StepUp and StepDown heights;
- exact flags;
- exact cell-local frame and orientation;
- current position/vector/state authority versions.
Do not reconstruct a cylinder from visual radius/height, clamp a positive
scale, use the projectile mover as a generic object fallback, or pre-mutate
FullCellId, PhysicsBody, WorldEntity, App buckets, or shadows.
Slice 4B2 prerequisite C — atomic local controller/body publication
Implement the complete transaction described in the rejected-prototype note. Graphical and no-window controllers must end with the exact same Runtime body, but construction cannot expose or mutate canonical state before the validated atomic commit.
Adversarial gates must include:
- nested construction;
- reentrant SetPosition;
- remote and projectile binding/update;
- deletion and same-GUID new incarnation;
- projection-owner replacement;
- object-clock epoch change;
- reset and disposal;
- commit and rollback after replacement;
- late graphical camera/shadow/host failure;
- late headless prepared-collision failure.
Slice 4B2 prerequisite D — presentation-only host projection
Add one graphical and one headless IRuntimePlacementObserver using
GameRuntime.Placements.
Host receipt rules:
Withdraw: remove render/spatial presentation, picking, radar, audio, and targeting while retaining logical Runtime ownership.Place: project only the immutable Runtime-committed frame, then acknowledge the exact token.Discard: discard the older projection revision, then acknowledge it.- A host exception or unavailable backend does not roll Runtime back; retry the same FIFO head.
LiveEntityRuntime.RebucketLiveEntity must become presentation-only. Its
current CommitRebucket call is a second spatial authority and must be removed
as part of the same cutover.
Slice 4B2 prerequisite E — collision-prefix quiescence
Before landblock collision demotion, removal, or replacement:
- quiesce the prefix;
- drain/ack the existing placement receipt prefix;
- call
RuntimeSetPositionState.ParkCollisionResidents; - commit/withdraw the collision generation atomically;
- wake only exact cell+generation residents.
Cover LandblockPhysicsPublisher.DemoteToTerrain, RemoveLandblock,
replacement commit, and the headless collision-retirement path. No partially
observable collision generation is allowed.
Production route cutover
Cut routes only after all prerequisites above are present. The canonical chain for every route is:
wire acceptance
-> BeginAcceptedPlacement exact token
-> exact DAT/Setup preparation
-> Runtime SetPosition canonical commit or deferred residence
-> immutable host projection receipt
-> exact host acknowledgement
1. Initial login and CreateObject
Current duplicate authority:
DatLiveEntityProjectionMaterializer.MaterializeProjectionimmediately positions/rebuckets the world entity.PlayerModeController.BuildControllerAndCamerabuilds its own body and runsResolve/ResolvePlacement.- Headless performs its own initial resolve/placement/body construction.
Required order:
- register identity cellless;
- begin initial/remote-create placement before hydration;
- load exact Setup mover;
- prepare the atomic Runtime controller/body relationship;
- submit canonical SetPosition;
- publish presentation only from
Place; - acknowledge, then enable player mode/simulation.
Tests: outdoor/indoor login, unavailable destination then exact-generation wake, malformed or delayed Setup, one body identity, one enter-world clock reset, no early visible entity, graphical/headless identical snapshots.
2. Local ForcePosition
Delete the placement authority in LocalForcePositionTransaction and the
direct BlipPosition/pre-commit acknowledgement in
LiveEntityNetworkUpdateController.OnPosition.
Required order: accept timestamp and preserve heading; begin
LocalAuthoritative; canonical SetPosition; host Place acknowledgement;
then send the outbound Position acknowledgement. A missing destination cell
must not acknowledge ACE early.
Tests: same/cross cell, preserved heading/velocity, missing-cell wake, reentrant newer Position, stale host ack, exactly one outbound ack.
3. Portal transit and materialization
Remove placement authority from LocalPlayerTeleportPlacement.Place and its
direct resolve/controller/world-entity/rebucket/spatial mutations.
Bind a Runtime portal-placement authority to the active
RuntimeWorldTransitState reveal generation, teleport sequence, exact
destination cell, and placement token. Readiness permits submission only.
Materialization and simulation release happen only after canonical commit,
host projection, and exact acknowledgement. Cancellation/replacement produces
Discard; a stale generation/sequence/cell/token can never reveal.
Tests: /ls, spell recall, ordinary portal, same-location revisit, missing
destination, cancelled/replaced reveal, host throw/retry, no early world reveal
or LoginComplete.
4. Remote CreateObject and Position
Delete RemoteTeleportController, RemoteTeleportPlacement, their pending
dictionary/rollback/lost-cell ownership, and pre-placement
WorldEntity.SetPosition/rebucket calls.
Preserve retail MoveOrTeleport classification:
- fresh Teleport timestamp or cellless body: teleport hook then SetPosition;
- ordinary nearby grounded update: interpolation remains;
- distant update: stop interpolation then SetPosition.
Accept the timestamp, begin the exact token before hydration/body/App changes, unparent first, run the retail teleport hook when required, submit the exact mover, project after Runtime commit, then re-arm constraints.
Tests: visible/hidden/parented CreateObject, first Position, Teleport timestamp, near interpolation, >96 m far placement, unloaded indoor destination, racing velocity, delete/GUID reuse during host callback.
5. Projectile authoritative create/corrections
Remove authoritative placement from App ProjectileController and the direct
SnapToCell/cell/shadow commit in RuntimeProjectilePhysicsUpdater.
Use ProjectileAuthoritative with the same Runtime body and exact projectile
Setup sphere for initial create and authoritative corrections. Preserve
prediction/component/effect identity. Do not route ordinary per-quantum
projectile integration through SetPosition.
Tests: arrow, bolt, spell projectile, mid-flight correction, unloaded cell, landblock crossing, delete during ack, no duplicate body/projectile/effect.
6. Drops and unparent-to-world
InventoryWorldDropProjectionController.TryRecoverUnknownPosition may create
the logical object, but it must enter the same canonical create-placement
transaction. Do not expose a stale source position or replay create-time
effects.
Tests: whole item, split stack, new GUID, second drop position, attached child becoming a world root, unavailable destination, newer Position while waiting.
7. Pickup, Parent, and Delete
Runtime hooks already exist in RuntimeEntityObjectLifetime, but the route
cutover must ensure pickup/parent/delete cancel the exact active
placement/lost-cell family first and publish Discard/Withdraw before the
later entity/inventory delta.
Tests: pickup during preparation/deferred residence, parent during pending withdrawal, delete during host callback, GUID reuse, reset/disposal ownership convergence.
8. Headless parity
Delete the independent resolve/placement/direct SetPosition and Blip logic in
HeadlessSessionWorldProjection. Headless must prepare/commit/ack through the
same Runtime operations as graphical presentation. Portal completion also
waits for the exact placement acknowledgement.
Tests: byte-identical login, ForcePosition, portal, missing-cell wake, reconnect, and teardown snapshots.
AP-22 — retail-authored collision shapes
After AP-1/AD-1 production cutover is stable:
- Make
ShadowShapeBuilderthe single Core authority for prepared Setup primitives. - Preserve authored cylinder order.
- If no cylinders exist, preserve authored spheres as spheres.
- Mixed data uses retail cylinder-first precedence.
- A truly shapeless Setup emits no world shadow.
- Remove
Setup.Radius/Heightcollision synthesis,Radius * 2height guesses, and sphere-to-cylinder coercion. - Cut graphical static, headless static, and live-entity publication over together.
- Do not alter transition dummy spheres, sticky/range radius, or projectile mover shapes.
Automated gates: raw/prepared parity, cylinder order, sphere-only, mixed, shapeless, scale, graphical/headless equality, representative installed DATs, and dropped/portal/sign/door behavior.
AD-10 — canonical remote slope projection
After AP-22:
- Prove remote movement uses the full
ResolveWithTransitionsweep. - Remove terrain-normal preprojection from
RemoteMotionCombiner. - Remove Runtime terrain-normal sampling calls and delete the sampler if no longer used.
- Let
CTransition::adjust_offsetproject against the retained actual contact plane. - Preserve interpolation queues, correction replacement, Hidden behavior, network cadence, and graphical/headless parity.
Tests must deliberately make terrain normals disagree with BSP/prop contact normals, then cover uphill/downhill motion, seams, stairs, jumping, landing, queue-empty/head-reached boundaries, and two-client observation.
Closeout gates
Do not mark the campaign complete from narrow tests alone.
Automated:
dotnet build AcDream.slnx -c Release
dotnet test AcDream.slnx -c Release --no-build --nologo
Also run every focused fixture named in the campaign plan: #273 tight gap, #271 stair side, #269 slope, #265 landing, #185 stairs, #137 sliding normal, #116 head collision, roof/cellar wedge, missing-cell, generation replacement, GUID reuse, graphical/headless parity, and allocation/quiescence gates.
Connected/visual:
- login and portal arrival at outdoor, indoor, dungeon, stair-lip, and world edge locations;
- repeated
/ls, spell recall, ordinary portals, same-location revisit, and reconnect; - no early world reveal, outdoor demotion, floor snap, terrain-Z lift, or void;
- tight gaps, stairs, steep roofs, ledges, doors, crowds, shallow water, and landblock seams;
- dropped objects, portals, signs, doors, and shapeless decorations;
- two-client uphill/downhill movement and sloped props;
- headless/graphical trace equality and graceful zero-residue teardown.
Only then retire AP-1, AD-1, AP-22, and AD-10, update the architecture,
divergence register, campaign/roadmap/milestones, research notes, durable
memory, CLAUDE.md, and AGENTS.md, and record final rollback SHAs.
Review procedure for every remaining behavior commit
- Implement one bisectable mechanism and run focused tests.
- Run a retail-conformance reviewer against named retail symbols/addresses.
- Run an architecture/adversarial reviewer against reentrancy, stale sequences, malformed data, GUID reuse, streaming replacement, host failure, reset, and disposal.
- Fix every confirmed finding at its root cause.
- Re-run the same reviewers until clean.
- Run Release build plus the complete Release test suite.
- Update the divergence/docs in the same behavior commit.
- Stage exact paths only and commit.
Rollback points
Newest first:
git revert 270f5154 # dormant public placement receipt channel
git revert 4c02ac42 # Runtime SetPosition/lost-cell residence owner
git revert e84a388e # pure Core retail SetPosition transaction
Earlier campaign commits are individually bisectable and listed in the completed sections above. Revert only the responsible mechanism; do not restore the rejected snapshot lease or revive legacy compensation elsewhere.
First action for the next agent
- Read this file completely.
- Read
docs/research/2026-07-31-canonical-set-position.mdand the AP-1/AD-1 rows indocs/architecture/retail-divergence-register.md. - Confirm
HEADcontains270f5154in the exact worktree above. - Confirm only
AGENTS.mdhas a real unrelated unstaged diff. - Implement prerequisite A (real Runtime collision-report ownership) as its own reviewed commit.
- Then design prerequisites B/C together so exact mover preparation and the atomic controller/body transaction cannot create another partial ownership state.