Retail re-cells children when their parent crosses a cell, recursively, to
unbounded depth. acdream did it from a RENDER tick, so headless parented
children were cell-less forever and the canonical cell had two writers. This
slice makes Runtime the sole authority and demotes App's tick to
presentation-only. Contract:
docs/research/2026-08-04-c4-route-7-contract.md; the research that unblocked
it is docs/research/2026-08-04-retail-parent-cell-propagation.md (ca96ea5e).
Retail: SetPositionInternal @0x00515330 branches on `this->cell == curr_cell`
@0x0051536d; the changed branch reaches change_cell @0x00513390, whose
delegates leave_cell @0x00510f50 and enter_cell @0x00510ed0 self-recurse over
children and write the FULL identity (add_object @0x00510ee2, objcell_id
@0x00510f1e, part-array cell id @0x00510f2b, cell pointer @0x00510f35).
change_cell itself has no child loop.
THE TRAP, recorded because it nearly shipped: the depth-1 loop
@0x0051539c-0x005153d8 is the SAME-CELL fast path (objcell_id and part-array
id only, deliberately not the cell pointer), NOT the propagation. An
implementer who finds it first concludes "depth-1, id-only" and strands every
equipped item at a landblock boundary — the #184 class. The clincher against
that reading: update_object @0x00515d10 early-returns on `parent != 0`
@0x00515d40, so a child never runs its own physics tick and parent
propagation is the ONLY mechanism maintaining its cell.
Route 7 performs NO placement (DoPickupEvent @0x00452240 = unset_parent +
leave_world; DoParentEvent @0x00452290 = set_parent + SetPlacementFrame), so
it arms ConstrainTo nowhere — the leash rule INVERTS relative to routes
2/4/5, and both reviewers confirmed nothing arms.
Propagation is an ITERATIVE WORKLIST, not recursion. The first implementation
recursed with a depth-64 cap; both reviews independently found the cap left a
truncated tail at a stale NON-ZERO cell — permanently unrecoverable, logged
only under a probe flag, and on the withdraw path exactly the #184 shape
AP-142 clause (a) exists to reject. Shipping a fresh #184 instance inside the
slice that fixes stranded children was not acceptable, so the cap was removed
rather than tuned. The worklist retires the cap, the constant, its register
clause, and the failure mode together. Termination: every record on the stack
is already at the target pair, so nothing can be pushed twice and a hostile
A->B->A cycle collapses without a visited set.
The child write deliberately bypasses the public RuntimeEntityDirectory
.SetFullCell and calls the record method directly. This is LOAD-BEARING:
the public method re-enters PropagateFullCellToChildren, which opens with
_propagationWorklist.Clear() — routing children through it mid-drain would
wipe the shared stack and silently drop every unprocessed sibling. Any future
side effect added to the public SetFullCell must be mirrored by hand at that
call site.
Deliberate divergence, recorded not disguised: retail's removal path leaves
children with a null cell pointer but a STALE nonzero objcell_id @0x005133c1.
acdream does not reproduce it, because FullCellId != 0 is the liveness
predicate at 45+ sites — faithful porting would mark dead children live.
AP-142 records this; clause (d) records that acdream cannot gate propagation
on HasPartArray the way enter_cell gates on part_array @0x00510ed8, because
the flag's only writers are graphical and headless never sets it — the reason
is Slice J LAYERING, not a semantic difference (retail's part_array is itself
a mesh-construction product, single assignment site makeAnimObject
@0x0050e930 -> CPartArray::CreateSetup @0x0050e93e).
D7 adopts retail's unset_parent-before-leave_world order @0x0045227f ->
@0x00452286, applied to BOTH pickup paths including the dormant executor
replay. Its inertness was verified by reverting it and finding all 12
propagation tests still green — reported honestly rather than papered over
with a manufactured test, and independently confirmed by both reviewers.
ClassifyLeaveWorld and its request/cause types are DELETED: retail has no
classification here, and method-per-cause IS the retail dispatch shape.
Wiring it would have forced a vacuous teleport-sequence predicate with the
#307 shape.
Two review rounds plus a coordinator-required third pass; 5 MAJORs. One was a
handoff failure worth recording: enter_cell's part_array guard was correctly
identified as load-bearing by the research, dropped by the contract when it
enumerated the writes, and inherited as an omission by the code — a right
finding that evaporated across two handoffs with nobody re-reading the source.
Another was a test that survived deleting the entire behaviour it claimed to
pin, because its assertion read a field written unconditionally one line
earlier.
NoProjection is structurally unreachable from TickChild (TryResolveExactAttachment
performs a strictly stronger form of the same guard one call earlier). Kept as
a fail-safe, unit-tested directly, and documented in two places rather than
wrapped in a fabricated end-to-end test.
Headless regression test — the direct gate for this defect, which FAILED
before this work because no code path existed:
RuntimeLiveEntitySessionControllerTests
.DirectSink_D5_StandaloneParentEventCommitsChildToParentsExactCell.
Probe: ACDREAM_PROBE_CHILD_CELL=1 emits [child-cell] lines at all four write
sites (attach / headless-attach / propagate / withdraw / delete). TEMPORARY.
Complete Release suite MEASURED at 11,079 passed / 4 skipped / 0 failed
(baseline 11,063 at cff52c44, +16). An allocation flake appeared once under
load and was proven NOT this slice by reachability — RuntimeCollisionReportingState
contains zero SetFullCell and zero ParentAttachments references.
STILL OWED: the two-client connected gate (equip/unequip, carry across
landblock boundaries, pickup, loot, reconnect) with ACDREAM_PROBE_CHILD_CELL=1,
and a session counts only if [child-cell] cause=propagate lines appear.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Route 6 needs no production change and this commit contains none: C3c
(529e0e9d) already flipped both hosts' Create paths onto the residence lease,
so a dropped item is byte-for-byte route 1's create classification
(RuntimePositionEntityKind.Remote, RuntimeCreateResidenceKind.TopLevel,
ClassifyCreate -> SetPosition with InitialCreateFlags = Placement | Slide).
Route 6 is a SOURCE of route-1 traffic, not a route of its own. Both drop
flavours converge on LiveEntityHydrationController.OnCreate ->
RegisterEntityWithInitialResidence — the whole-item drop through
ItemInteractionController's DropToWorld (no physics, no position; the server
decides), and split-to-world through TryRecoverUnknownPosition's call to the
identical entry point. Contract:
docs/research/2026-08-04-c4-route-6-contract.md.
Retires a FALSE PREMISE from the campaign plan (:97-100), which claimed
split-recovery creates "need an effect-replay suppression signal". Verified
against the decomp instead of assumed: play_default_script @0x005132B0 /
@0x00513300 has exactly three call sites in the entire pseudo-C dump —
DefaultScriptPartHook::Execute @0x00526c08, DefaultScriptHook::Execute
@0x00526c14, and ACCWeenieObject::DoCollision @0x0058c3b4 — and NONE from
set_description or CreateObject. Neither client plays a default script at
create, so there is nothing to suppress. acdream's only create-time replay is
the F754/F755 queue drain keyed by server GUID, which is retail's own
HandleCreateObject @0x00454C80 behaviour. The plan's other two clauses were
closed at C0 (TryCommitParent/CommitWithdrawal cancellation symmetry;
host-visible cancellation receipts); the list now states what actually
remains — route 7's child-cell two-writer split and the headless
parent-realize gap.
Retail split marking recorded for the record: UIAttemptSplitTo3D @0x0058D850
stores only splitStackSize/splitClassID/splitTime and performs no placement;
DeclareValid @0x0058E340's recovery action is SetSelectedObject @0x0058E481 —
a SELECTION transfer with a 10-second expiry, not effect suppression and not
placement. UIAttemptPutIn3D @0x0058D700 records no marker at all.
Seven tests over the now-flipped path (whole item, split stack, new-GUID
recovery, second drop, unavailable destination, newer Position after the
pending identity is consumed, plus the #314 repro), each sabotage-verified:
the production path was broken on purpose, the test was confirmed to fail,
and the sabotage reverted. R6-c is now settled by assertion rather than
argument — BuildSpawn's wholesale clone of Children/Movement/AnimationFrame/
SetupTableId is measured, not reasoned about.
FOUND WHILE TESTING — #314, filed not fixed (this route is zero-production by
contract). BuildSpawn resets top-level MovementSequence/ServerControlSequence
to 0 but its Timestamps `with` block overrides only Position/Teleport/
ForcePosition/Instance, leaving Physics.Timestamps.Movement and
.ServerControlledMove at the SOURCE item's values.
HasConsistentCreateIdentityAndParent requires the two projections to agree, so
a split whose source carries nonzero Movement timestamps — plausible for any
item dropped once, picked up, and split again — fails the predicate and throws
instead of completing the canonical transaction. Verified in source, not taken
on report. Note this is a crash in the exact mechanism the scoping cited as
EVIDENCE that drops already converge: code reading said the path converges,
driving it said it throws. Fixed in the immediately following commit.
Also filed: #313 (DeclareValid's SetSelectedObject port is missing and the
container-split flavour records no marker — selection UX, deliberately not
implemented inside a placement closure) and #315 (route 4b-3's per-packet
runTeleportHook Func<bool> closure at three RunRemoteArmTail call sites; the
network packet path, not Slice I's per-frame resolve path — filed now because
route 5 adds a fourth site). AP-124 stays open and registered.
Test lines are 410 against a 150-250 guidance, accepted: the excess is a real
ItemInteractionController harness plus the #314 repro, which is what found the
defect. A mock that proved nothing would have been shorter and worthless.
Complete Release suite MEASURED at 11,020 passed / 4 skipped / 0 failed
(baseline 11,013/4/0 at 6dc7ba51; +7 new). Neither known flake fired.
Connected gate (user-run) still owed: drop a whole item, split a stack to the
ground, drop a second within ~1 m, repeat indoors and after a portal recall,
then walk two landblocks away and back.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Route 4b-2 landed at 7f1c1f5a and the two-client far-snap walk passed
(2026-08-04): a remote crossing 96 m in both directions stays visible and
correctly positioned at range and resumes smooth interpolation on the way in,
with no freeze, Z pop, vanish, or invisible-but-solid.
Records that #309 is still outstanding and why we know it: the
ACDREAM_PROBE_PARK=1 capture from the accepting session shows 11 parks, every
one cause=unplaceable and zero cause=quiescence, so the shared-core park change
has not been exercised live. That probe was added precisely because the prior
#309 steps could pass while broken — without it the session would have been
recorded as a full pass.
Also records the corrected 10,968 baseline and the two process lessons: the
round-1 defect traces to the contract omitting "and still advance the pose",
and the park defect should have been split into its own slice when it surfaced
in round 2 rather than riding inside 4b-2 for three more review rounds.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Scoping at 44830a0e puts 4b at 1,300-2,200 production lines (centred ~1,700)
plus ~2,500-3,500 lines of test work — 4-6x route 4a and ~2x route 2, the two
largest landings in this campaign, which took 4 and 5 review rounds. Split into
4b-1 (infrastructure, no behaviour change), 4b-2 (far branch), 4b-3 (teleport
and cell-less, with the ~739-line class deletions). 4b-1 stays separate
regardless of appetite for landings.
Corrects two errors in documents from yesterday:
AP-135 does NOT retire with 4b. Its own condition is retirement with the
free-fall sweep gate, which 4b does not touch, and its sites are the airborne
no-op branches — 4a-owned dispositions. The trap is that its two writes sit
inside OnPosition, which 4b rewrites heavily.
Retail has exactly ONE ConstrainTo on the remote arm (@0x00454272); all three
nonzero-returning MoveOrTeleport branches funnel through it. My route-4 scoping
implied a distinct remote-teleport arming site. There is none, so 4b must not
add a second one — the post-operation arm 4a introduced becomes the only arm.
Records a new failure mode 4b must not create: a DeferredCell park WITHDRAWS the
entity (InWorld false, Active cleared, clock suspended, residency dropped), and
Forget-on-every-accepted-Position kills the park without restoring any of it. If
the next packet classifies Interpolate, no placement runs and the remote stays
withdrawn indefinitely — invisible AND intangible, the #184 class through a
third door. Direction: refuse rather than park; the next packet is the retry,
because remote Positions are a 5-10 Hz stream.
Two transfer errors named explicitly so they are not repeated: do not port route
2's re-issue funnel (re-issuing a superseded pose is wrong for a repeated
stream), and do not port its ack machinery (retail's remote arm has no
SendPositionEvent).
Also records that remotePlacementRequired fires for every non-visible remote on
the graphical host — a routine hot path, not a teleport rarity — and that
deleting the legacy blocks removes the only handler for null/Rejected*, which
during the login window is every remote packet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Routes the classifier's two NO-PLACEMENT remote branches — Interpolate
(contact, PlayerDistance < 96 m) and NoPositionOperation (no contact) — through
a Runtime-owned seam, and fixes the two divergences they carried. Teleport,
far-snap and cell-less stay on the legacy App path; 4b owns them.
Route 4 was split into 4a/4b after scoping put the whole route at 1,500-2,500
lines against a ~400 budget. 4a's branches perform no SetPosition, so this slice
carries no deferred-cell park, no service-window guard and no allocation
exposure — which is what made the split worth doing.
Divergences fixed, both previously unfiled:
* D1 — the NPC airborne branch hard-snapped Body.Position/Orientation and
branched on the client-tracked rmState.Airborne, never consulting the wire
IsGrounded bit. Retail's MoveOrTeleport @0x00516330 returns 0 at 0x0051636D
and writes nothing. Player remotes were already correct; NPCs were not.
* D2 — ConstrainTo was armed before the operation, unconditionally, so it fired
on the airborne no-op retail skips and anchored to the PRE-move position.
Retail arms it at 0x00454272, only when MoveOrTeleport returns nonzero,
anchored to &arg2->m_position read live, i.e. post-move.
AP-87 and TS-44 were carried deliberately, not delegated away. AP-87's three
conditions — including firstUp, which one round silently dropped — are preserved
as an explicit acdream policy layer applied AFTER the classifier commits to
Interpolate; the two previously separate player/NPC copies are now one. TS-44
stays an NPC-only caller gate; extending sticky suppression to player remotes has
no retail basis and no live evidence, so it was declined rather than absorbed.
Landing is explicitly carved out of 4a's ownership on both arms. A landing packet
classifies Interpolate, so an ordering slip would ENQUEUE a body that must PLANT
and a creature knocked off a ledge would glide down over a packet interval. The
carve-out is a named entry point returning AirborneSnap/SteadyStateInterpolate/
Legacy precisely so the PRECEDENCE is observable and testable rather than implied
by statement order — that is how the slip happened once and was caught.
The player/NPC asymmetry on landing is real and NOT resolved here: retail draws
no such distinction, but converging them is a behaviour decision needing its own
evidence. Filed into the 4b plan.
Register: AP-135 filed for the two bookkeeping writes the airborne branch
deliberately retains (rmState.CellId, LastServerPos/Time) — not retail's model,
but load-bearing for our catch-up sweep and staleness timer, and verified not to
be a canonical cell commit for ordinary remotes. AP-87 and TS-44 rewritten to
describe the code.
Honest remainder: App still owns branch selection, the airborne return, the cell
write, the entity write and the shadow publish, and headless satisfies "both
hosts drive the identical entry point" only vacuously since it returns early for
remotes. That is written into the 4b bullet rather than left implicit.
Cost: 364 non-comment production lines, 91% of the ~400 budget — the split did
isolate the cheap half, but not by much. Do not carry "well under" into 4b's
scoping.
Gates: complete Release solution 10,938 passed / 4 skipped / 0 failed (pre-4a
baseline 10,909). Four review rounds; the first three each introduced a new
behavioural defect while fixing another, and each left a comment asserting
behaviour that no longer matched — the final round's precedence matrix was
traced cell-by-cell against HEAD with only the D1-intended difference. App tests
call production entry points against a real WorldEntity and real classifier
output, closing route 2's #292 gap rather than repeating it.
Connected acceptance NOT run — needs a live second character.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
User-directed after scoping put whole-route 4 at 1,500-2,500 production lines
against a stated ~400 budget.
4a is the steady state: the classifier's Interpolate (contact, < 96 m) and
NoPositionOperation (no contact) branches. Neither performs a SetPosition, so 4a
carries no deferred-cell park, no service-window guard, no allocation exposure,
and no interaction with the Forget-on-every-accepted-Position behaviour that
dominated route 2's review rounds. It also fixes two of the three unfiled
divergences: the NPC airborne hard-snap that ignores the wire IsGrounded bit
(retail returns 0 and writes nothing, MoveOrTeleport @0x0051636D), and
ConstrainTo armed before the operation instead of after (retail arms it post-move
only on a nonzero return, @0x00454272).
4b takes the edges — teleport, far-snap, cell-less — where the parks, the
Position-time service-window guard, #277's broken bound, N3, and the third
divergence live.
The contract sanctions exactly one dual path: 4a routes its two classifications
through the new seam and leaves the other two on the legacy path until 4b. That
is a staged cutover rather than a duplicate authority ONLY because the
discriminator is the classifier itself and the classifications are mutually
exclusive; the contract says so explicitly and requires the fallback deleted in
4b.
Two carried acdream additions are called out as load-bearing rather than left to
be discovered: AP-87's 4 m / !willBeDrTicked snap conditions (which prevent the
#184 invisible-but-solid monster and are NOT in the classifier) and TS-44's
sticky suppression. Silently dropping them by delegating to the classifier is
named as the failure mode.
Acceptance requires a BEHAVIOURAL App test, not the source-text pin route 2
settled for (#292).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The user provoked a real ForcePosition via the @pklite entry-collision bump
and observed the visible slide off the overlapped character, correct
animation, no heading change, and no leash tethering afterwards. That accepts
both named behaviour changes live: the ack now fires after the canonical
commit, and the ForcePosition route no longer re-arms the constraint leash
(retail's force branch returns at 0x0045409D, ahead of all three ConstrainTo
sites).
Route 2 is complete and accepted at 9966b531. Routes 3-7 remain open.
Also records what shipping @pklite exposed, explicitly NOT a route 2
regression: PK Lite became reachable for the first time and melee/ranged
attacks refuse a PKLite target while spells on the same target work. Under
investigation, filed separately.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A local-player ForcePosition had TWO independent writers for one accepted
packet: LocalForcePositionTransaction snapped the physics body
(PlayerMovementController.BlipPosition, a raw SnapToCell with no collision
resolve), while LiveEntityNetworkUpdateController's generic tail separately
wrote position/cell/rotation to the render WorldEntity from the raw wire and
rebucketed it. Two stores, one packet — the divergence class 670f307c fixed on
the remote path. The outbound AutonomousPosition ack also fired BEFORE any
canonical commit existed: we told ACE "got it, I'm here" before deciding where
"here" was, and the trailing isCurrent() could only suppress the continuation,
never recall the packet.
RuntimeAcceptedPositionDriveController is now the one Runtime-owned seam. Both
hosts call the identical TryExecuteAcceptedLocalPosition; App and headless
project the committed result through the existing placement projection sink
(LiveEntityRuntime.TryApplyRuntimePlacementPlace already performed the same
four writes, from committed state rather than a wire guess).
Retail: SmartBox::HandleReceivedPosition @0x00453FD0's FORCE_POSITION branch is
get_heading -> Frame::set_heading -> SmartBox::BlipPlayer @0x00453940 -> stamp
POSITION_TS -> SendPositionEvent @0x00454091 -> return @0x0045409D. BlipPlayer
is CPhysicsObj::SetPositionSimple @0x005162B0 with flags 0x1012
(Teleport|Slide|SendPositionEvent) — a real collision-resolving SetPosition,
not a snap. The pinned classifier already encoded this exactly.
Named behaviour changes:
* The ack is now an OUTPUT of the committed route, fired strictly after the
canonical commit and exactly once per accepted force packet.
* The ForcePosition route no longer re-arms the constraint leash. The force
branch returns at 0x0045409D, ahead of all three ConstrainTo sites
(0x00454272, 0x0045418A, 0x004541EC); the old re-arm cited retail's "Player,
normal" branch, which BlipPlayer is not on. The teleport, CommitPreparedPosition
and first-entry callers legitimately still constrain and are untouched.
* A force correction that terminates WITHOUT committing still sends its
position event and is not retried — retail's BlipPlayer discards
SetPositionSimple's SetPositionError return and acks unconditionally.
A single _pending funnel owns the in-flight placement, deciding on the token's
PositionAuthorityVersion against the record's: equal -> clear; advanced with the
newest accepted event still a force -> re-issue, re-classified; advanced to an
ordinary Apply -> clear, since newer server truth owns that pose. This closes a
double-apply/double-ack and a silently-dropped correction that two earlier
iterations of this slice each introduced.
AD-62 records the residual: a ForcePosition our async collision publication
cannot carry to a committed placement is not re-applied. Retail has no park —
its world is fully resident and its placement synchronous — so the state is
unreachable there. AP-131 is NOT retired; its legacy Position caller is route 4.
Deleted: LocalForcePositionTransaction, PlayerMovementController.BlipPosition,
HeadlessSessionWorldProjection.BlipLocalPlayer.
Gates: complete Release solution 10,858 passed / 4 skipped / 0 failed (baseline
10,844/4/0). Two independent Opus reviews (retail-conformance and
architecture/adversarial) PASS on the final diff after three FAIL rounds; every
intermediate state was fully green, so the suite caught none of the four real
defects. Connected acceptance is NOT run: nothing a user can do makes ACE emit
a ForcePosition without retail's @pklite, which acdream does not implement — see
docs/research/2026-08-03-c4-route-2-visual-gate.md.
Known gap, recorded not claimed: the plan's acceptance item 2 is unmet. The App
double-write check is a source pin, and "the committed projection moves the
render entity" is uncovered at any layer (#292). Filed alongside: #286-#291,
#293-#296.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
S1 (#284), S2 (#282), and S3 (#283) are all landed and user-accepted. S3 is
recorded as measured-unreachable rather than restructured, so the plan's
"prove or disprove before moving ownership" step is what actually decided the
outcome.
Final complete Release solution: 10,844 passed / 4 skipped / 0 failed.
Next: the original campaign order, starting at C4 route 2.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three defects introduced by the 2026-08-02/03 stabilization batch, all found
while reconciling #281's 43 test failures. Each is an instance of the weakness
the placement campaign exists to remove - two owners of one fact with no single
writer keeping them agreed - so they are cleared before C4 stacks six more
placement routes on top of them.
#282: WorldEntity.EffectCellId documents itself as existing only for outdoor
dat stabs, whose null render parent still needs retail's outdoor landcell for
CObjCell::IsInView gating; live/interior entities were explicitly meant to use
ParentCellId. f24532ad began populating it for live entities, and because
EntityEffectPoseRegistry.UpdateRoot resolves EffectCellId ?? ParentCellId it
now wins - while 12+ sites still write ParentCellId alone. Retail carries one
cell per object (CPhysicsObj::set_cell_id @0x0050f4f0, change_cell @0x00513390,
ShouldDrawParticles @0x0050fe60).
#283: 670f307c gave Runtime a world frame that rebases on the accepted teleport
Position, while App's LiveWorldOriginState rebases only after old-window
retirement completes. Between those edges the two disagree by the landblock
delta. Not yet proven reachable; the plan proves or disproves it before
restructuring anything.
#284: a placement that cannot resolve returns RetrySetupUnavailable forever
with nothing counting it or naming its reason. The fix is observability plus
fail-fast on contradictory states, never a retry cap or timeout.
Plan sequences S1 (#284) first so the other two are observable rather than
archaeological, then S2 (#282), then S3 (#283) immediately before C4 route 3,
which shares its portal code. Also records the gating change that would have
caught all of this: the complete Release suite must be green before every
commit, not a focused subset.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
C3c COMPLETE at 529e0e9d in the placement-cutover plan (five fix slices,
R1 dual-review round, final gates). New closeout research note. ISSUES
#276 (settle-CellId discard), #277 (route-1 far-Create radius bound),
#278 (user-session triage bundle). Register AD-60/AD-61 numeric order.
The next slice before C5 is the 6b28ff99 O(changed) collision clone
(soak convergence); C4 resumes after.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
C3b landed at 0934a121 with dual review PASS. The plan records the
remote-entry mechanism and its verified retail anchors; the float-gates
doc gains the port note pinning the NaN dispositions (friction's
sanctioned skip; elasticity and translucency routed exactly as the
binary; ACE's elasticity NaN divergence recorded). Every dormant C3
prerequisite is now complete — C3c, the host flip with the connected
gates, is the sole remaining piece of C3.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
C3a landed at 960373df with dual review PASS. The plan records the
conductor's five-stage sequence (verified step-for-step against retail's
entry order, with the mover-shapes-first correction the tested
preconditions forced), the convergence/wiring closures, and the two
carried findings C3c must honor. Next: C3b remote body construction at
Create, whose float-gate oracle is committed at 874d94bf.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The first C3 implementation pass landed C3-1 (fe02c4f5) and correctly
stopped on two structural gaps no planning document captured: the local
player's first-entry circularity (the residence opens its placement at
Create, submission needs a body, and only the zero-caller publication
chain can attach one — resolvable by the campaign handoff's own route-1
order, but no driveable state machine exists) and the absence of any
remote-creature body construction at Create time (retail builds physics
in ACCObjectMaint::CreateObject; ours arrive with first motion). The
plan now records the C3a (first-entry conductor, dormant) / C3b
(retail-anchored remote body construction at Create, dormant — its
contract must first resolve set_description's three FPU-elided
friction/translucency gates from the PDB-paired binary) / C3c (the
actual host flips + connected gates) decomposition.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
C2 landed at 63c601ff with dual review PASS after two fix rounds. The
plan records the halved allocation result and tightened gate, the
class-wide token-based staleness rework the pooling forced, the
documented residual floor (Core-side ~520 B/op deferred to the C3
activation gate as a possible C2b), and the two review maintenance
notes. Next slice: C3, the spawn-frequency host cutover of routes 1+8.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The C1 body-writer research found the atomic controller/body transaction
already built and tested: RuntimeLocalPlayerPhysicsPublicationState plus
the dormant local-activation family implement the sanctioned
off-canonical-prepare + validated-atomic-commit shape end-to-end, with
zero production callers. The committed writer map records the six
canonical body writers, the two host escape hatches (the public
Controller setter both hosts write directly; App's object-clock facade
bypasses), the headless prepared-collision fragility, and both hosts'
construction divergences. C1 therefore collapses into C3's route-1 flip
— the remaining work is production wiring, not mechanism design — and
C2 (the placement allocation budget) becomes the next slice.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
C0 landed at 67f63e85 with dual review PASS; the plan now records its
delivered seam (acknowledge-only ExecutorCompleted receipts through the
one placement stream, retail-exact live-input derivation, the chained
authored-mover preparation, the cancellation-symmetry hardening) and the
three C3 prerequisites its reviews surfaced: the internal-only completion
receipt surface, the per-Execute distance-freshness deferral, and the
SendAutonomyLevelEvent obligation on any future autonomy-level host
exposure. Next slice: C1, the atomic controller/body publication.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The continuation executor (5db3de3c) completed the dormant residence
mechanism; the cutover is the campaign leg that makes it production truth.
The committed 8-route inventory maps every duplicate placement authority in
both hosts with exact call chains, confirms the placement-receipt observer
seam is fully built but unattached, and surfaces five pre-cutover gaps the
shipped mechanism cannot yet express (executor-to-channel bridge, atomic
controller/body publication, the 1,880 B/op activation budget, Runtime-side
live-input derivation, the portal-authority adapter). The plan decomposes
the cutover into C0-C5 bisectable slices under the campaign's standing
contract/dual-review/gate discipline, ending at the connected routes and
the user visual matrix that retire AP-1/AD-1.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Synchronize the architecture doc, milestones, roadmap, and ISSUES with the
continuation-executor behavior commit (5db3de3c): the residence system is
now a complete dormant mechanism, both independent reviews PASS, and the
next boundary is the all-host production cutover. The admission handoff
gains its superseded banner; the successor handoff records the executor's
ownership, the retail anchors proven during review (the wire-contact gate,
queue-by-parent-GUID relation replay, HasAnims semantics), the seven new
register rows, exact test totals, the rollback command, and the cutover
checklist. #275 filed for the post-cutover legacy-Position unification.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per docs/research/2026-07-30-ts4-116-oracle-plan.md, following the four
code commits that closed out TS-4 (5e2be19b), #116 shape-2 (01492205),
and AD-55 (252e8068), plus #116 shape-1's Path-6 fix (db2889af) and the
TransitionalInsert return-value fix (7e1be3de):
- ISSUES.md #116: shape-2 marked CLOSED (D4 un-skipped, structurally
confirmed, no cdb needed). Shape-1 narrowed, not closed: the Path-6
head-sphere fix is a real, independent improvement but the tick-22760
confirming replay showed it doesn't explain that specific symptom --
the mover is grounded there (Path 5, not Path 6) and the actual
no-normal-recorded mechanism (SpherePath.PrecipiceSlide's
find_crossed_edge-false fallback) is independently confirmed byte-exact
retail behavior too. Recorded the concrete next step (re-run against
the faithful Setup-based door registration instead of the simplified
fixture) rather than closing on an unmet acceptance criterion.
- ISSUES.md #166: noted TS-4 and AD-55 landed (the AP-7-family
completion this note was waiting on); closure still pends the visual-
matrix scenario-5 recheck against live retail.
- Campaign P plan (2026-07-29-physics-parity-campaign.md) P2 status
block: TS-4 outcome (retired, not deferred), #116 outcome (shape-2
closed / shape-1 narrowed), AD-55 outcome (retired).
Docs-only; no build/test change required for this commit.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PhysicsBody.IsFullyConstrained now reflects real ConstraintManager state
(pushed every tick by the same per-tick pumps commit 2 wired), so
jump_is_allowed's already-ported gate (WeenieError 0x47) actually fires
while an object is rubber-banding hard against a server position
correction, closing the last piece of #167.
Housekeeping:
- Delete register row TS-35 (retired: the write side is no longer stubbed).
- Rewrite the stale doc comments on PhysicsBody.IsFullyConstrained,
ConstraintManager (class + IsFullyConstrained), PositionManager.ConstrainTo,
EntityPhysicsHost.PositionManager, and PlayerMovementController.PositionManager
that described the leash as permanently unarmed/stubbed.
- Close#167 in ISSUES.md citing the research doc and commits e0629145 /
7719d25b.
- Add an "as-ported" addendum to
docs/research/2026-07-30-constraint-leash-constants.md naming the actual
current seam owners (the doc's own open question flagged this as
implementer-verify-required post-J-slices).
- Update docs/plans/2026-07-29-physics-parity-campaign.md's P5 status and
CLAUDE.md's Campaign P summary to reflect #167's closure (items #153/#72
remain open in P5).
Verification: complete solution suite green - 9,978 tests, 5 skips, 0
failures across all 9 test projects (Core.Tests, Runtime.Tests, App.Tests,
Headless.Tests, Core.Net.Tests, Content.Tests, UI.Abstractions.Tests,
Bake.Tests, Cli.Tests).
P4's original AP-71 landing shipped CanMoveInto deliberately unmodeled
(fail-closed default, AP-129). The review found this locks the entire
housing estate (103,766 of 729,888 installed EnvCells) for every player
including its own owner. Records the fix (7a0f836a) and the updated gate
totals.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both P4 items landed (d6c3f865 AP-71, cc8d57a2 AP-10). Records the slice-gate
complete solution suite totals: 9,946 total across 9 test projects, 9,941
passed, 5 skipped, 0 failed on a clean run. One flaky unrelated failure
(AcDream.Content.Tests parallel-cache-coalescing timing test, untouched by
this slice) observed on an earlier run in the same session; reproduces 0/2
in isolation and passed clean on immediate re-run, confirming full-suite
parallel-contention flakiness rather than a regression.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Campaign P Slice P2 step 2-3
(docs/research/2026-07-30-response-layer-edge-family-pseudocode.md §4, §6
Step 3). Per the research doc's own port order, TS-4's Path-6 steep-poly
shortcut may only be removed after a fixture reproduces the original
"stuck in falling animation on a steep roof" symptom cleanly with the
shortcut disabled. No surviving live-session fixture exists from the
2026-04-30 L.4 commit (b1af56e); this adds a dat-free multi-frame capture
(Ts4SteepRoofWedgeCaptureTests) using BSPStepUpFixtures.SlopedUnwalkable's
63.4 degree slope, replayed at 30 Hz with gravity integrated between
PhysicsEngine.ResolveWithTransition calls -- the same idiom as
Issue185OutdoorStairsSeamReplayTests.
Against today's baseline (shortcut active) the capture is green, as
expected (the shortcut's explicit AddOffsetToCheckPos keeps the body
moving every tick by construction).
Scratch-removed the shortcut (both BSPQuery.cs sphere0/sphere1 branches,
not committed -- reverted after capture) and re-ran the same test: the
body falls and lands cleanly on the steep polygon at tick 17 (InContact,
OnWalkable=false, via retail's own permissive CTransition::check_walkable
LandingZ gate, pc:273202), then freezes at that exact position for the
rest of the run -- the exact historical wedge shape, tripping the test's
own >0.5s-frozen threshold at tick 33.
Root-cause diagnosis via ACDREAM_DUMP_EDGE_SLIDE=1: the freeze is upstream
of EdgeSlideAfterStepDownFailed/CliffSlide entirely (none of that
dispatch's diagnostics fire). TransitionalInsert's Phase 2 object-collision
check returns Adjusted on every retry attempt because Path 6's retail-
faithful SetCollide returns ADJUSTED_TS without repositioning the sphere
(unlike the interim shortcut, which explicitly pushes the sphere off the
face) -- the same steep polygon re-triggers Path 6 on the immediate retry,
forever, and Phase 3 (the sp.Collide handling that contains DoCheckWalkable,
the Placement re-test, and the TS-1 CliffSlide chain) is gated on Phase 1
AND Phase 2 both returning OK, so it is structurally unreachable from this
state. TS-1's completeness is moot here -- the code path that would call
into it never runs.
Per the mission's explicit escape valve: STOP here, keep the shortcut, and
report -- do not improvise a third variant. Full diagnosis, the exact
capture, and the concrete next research question (does retail's own
transitional_insert loop check sphere_path.collide on every iteration
regardless of Phase 2's own return value, or only when Phase 2 returns OK?)
are recorded in the research doc's §7 item 6 and the doc's headline; the
campaign plan's P2 section gets a matching status note.
Physics test suite: 1841 passed, 1 skipped (D4, pre-existing/unrelated), 0
failed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The one user stop of the campaign: each scenario names its setup, the
retail-correct outcome, and the register rows / issues it closes,
including the stale #172-#175/#41 gate reconciliation via scenario 8 and
the #167 leash check riding scenario 12.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
User-directed pre-vendor detour from the 2026-07-29 physics audit. Goal:
Retail Movement Parity v1 - zero physics TS rows, no unargued
feel-affecting AP rows, issues #262/#165/#166/#116/#167/#72/#153 closed,
one batched connected visual matrix. Sonnet implements, Opus reviews at
slice boundaries. Roadmap gains the Campaign P entry and records Campaign
N's user-accepted closure; CLAUDE.md current-state pointer updated.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Slice 4 passed its two-client Coldeve visual gate and was user-accepted;
world-interaction program resumes at Slice 5 (vendor browsing) after the
physics parity campaign.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Slice 4 made a remote character's wielded weapon selectable, which made the
pickup chain reachable end to end for the first time: SelectionPickUp on
another player's weapon captured identity, passed ValidatePickupTarget (which
checked only the Stuck flag and the small-item mask, and a MeleeWeapon clears
both), installed a real non-autonomous approach through
PlayerInteractionMovementSink, and then sent a pickup request the server
rejects. Retail does none of that.
ItemHolder::AttemptToPlaceInContainer @ 0x00588140 runs
AttemptToPlaceInContainer_IsItemLegal @ 0x005870C0 first, at 0x00588173 --
ahead of container legality, auto-merge, the container walk, and the only
CM_Inventory::Event_PutItemInContainer emitter
(ACCWeenieObject::UIAttemptPutInContainer @ 0x0058D680). IsItemLegal's arm at
0x005872B7 rejects `!ACCWeenieObject::IsOwnedByPlayer(item) &&
item->pwd._location != 0` with one local
ECM_UI::SendNotice_DisplayStringInfo(0x1a, ...), and
CPlayerSystem::PlaceInBackpack @ 0x0055D8C0 then withdraws the waiting slot it
had published (SetWaitingState(obj, 0) + SendNotice_EndPendingInPlayer at
0x0055D918). No request, no movement. acdream had never ported that arm; it
was harmless while wielded children were unpickable and stopped being harmless
at f6db964f.
The notice is data_7e2228, "The %s is being wielded by someone else!" -- WITH
the exclamation mark. IsItemLegal's six strings occupy one contiguous literal
block, 0x007e21f0 through 0x007e234c, one per arm in reverse code order, and
the two neighbours already ported here (0x007e227c "The %s cannot be picked
up!" at 0x00587264, 0x007e22b4 "You cannot pick up creatures!" at 0x005871f4)
pin it. The punctuation-free 0x007cd350 variant belongs to the wield/wear
block and is emitted from a different function at 0x00560aef.
pwd._location is the PublicWeenieDesc CurrentWieldedLocation field
(acclient.h:37175), which acdream projects as
ClientObject.CurrentlyEquippedLocation, and ACCWeenieObject::IsOwnedByPlayer
@ 0x0058D160 is IsOwnedByObject(this, player_id) -- already ported as
ClientObjectTable.IsOwnedByObject @ 0x0058CEB0 and reached here through the
existing ItemInteractionController.IsOwnedByPlayer. The arm reads pwd._location
verbatim rather than adding a WielderId belt-and-braces test, because retail's
predicate is the thing being ported.
The player's OWN wielded item is IsOwnedByPlayer, so retail passes it and takes
a different route. ACCWeenieObject::DeterminePositionState @ 0x0058BE70 gives
it PositionState.WIELDED (acclient.h:6802) rather than IN_3D_VIEW, and
UIAttemptPutInContainer records IR_PICK_UP only for IN_3D_VIEW, treating
WIELDED and IN_CONTAINER alike as a plain IR_PUT_IN_CONTAINER transfer. So an
own-wielded item is unwielded in place: the request goes out immediately with
no approach, joining the existing current-ground-object shortcut. The shortcut
carries an ownership conjunct so it can never outrun the 0x005872B7 gate.
TryGetApproach now refuses attached children outright, for the same
IN_3D_VIEW reason. An Attached projection's bookkeeping WorldEntity.Position
carries the PARENT's composed root (EquippedChildRenderController
.ApplyParentWorldPose), not the child frame CPhysicsObj::UpdateChild @
0x00512D50 composes, so an approach built from it walked toward the wielder.
Slice 4 de-parented the marker anchor but left this one parent-derived; no
approach can anchor on a wielder now.
The pick predicates are deliberately untouched. Picking, selecting, examining,
lighting-pulse identity, and the vivid-marker anchor on a remote's wielded
weapon all behave exactly as Slice 4 shipped them -- retail's sr_Select and
sr_Examine branches of RecvNotice_SmartBoxObjectFound @ 0x004E5AD0 never
consult IsItemLegal. The gate is the transaction, not the pick.
f6db964f's message asserted the slice introduced no deviation and owed no
retail-divergence-register row. That was wrong: the unported 0x005872B7 arm
was a deviation it made reachable. This commit ports the arm in full, matches
retail on the own-wielded path, and removes the parent-derived approach
anchor, so the record is corrected here and no register row is owed.
Gates: dotnet build green; AcDream.App.Tests 3,960 passed / 3 skipped;
complete Release solution 9,792 passed / 5 skipped;
tools\run-connected-world-lifecycle-gate.ps1 -SkipBuild RESULT=PASS.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A click on a remote character's wielded weapon reported nothing. The picker
was already correct: RetailSelectionScene publishes every drawn part under its
own live-entity server GUID and RetailWorldPicker returns the weapon as the
polygon winner. The failure was downstream eligibility - WorldSelectionQuery
required TryGetInteractionEligibleRecord, whose _visible set admits
LiveEntityProjectionKind.World only, so the winning hit was discarded.
Retail has no such gate. Render::GfxObjUnderSelectionRay @ 0x0054C740
accumulates each hit under the drawn part's own physics-object id
(CPhysicsPart::get_physobj_id @ 0x0050D490), and CPhysicsPart::Draw @
0x0050D7A0 admits any drawn part whose physobj id is nonzero. An equipped item
is a first-class CPhysicsObj with its own id and part array
(CPhysicsObj::add_child @ 0x0050F870 via CSetup::GetHoldingLocation @
0x005213F0). There is no parent redirection and no wielded-specific rule, so a
click on a wielded weapon returns THE WEAPON'S GUID. PositionState.WIELDED is
distinct from IN_CONTAINER (acclient.h:6802), so container suppression never
hid a wielded selection either.
LiveEntityRuntime gains two scoped predicates: TryGetAttachedProjectedRecord
(a current Attached projection that is spatially projected) and
TryGetPickEligibleRecord (that arm plus today's World visible-set arm, with
the same WorldEntity.Id staleness recheck). TryGetInteractionEligibleRecord
and the _visible set are deliberately NOT widened - they feed radar,
auto-target, sticky/MoveTo establishment, and CombatAttackTargetSource, and
retail's radar has no wielded blips. A regression test asserts an attached
child stays out of that set while picking admits it.
Marker anchoring had the twin problem. SmartBox::GetObjectBoundingBox @
0x00452E20 pushes the picked object's OWN m_position - which for a child is
the frame CPhysicsObj::UpdateChild @ 0x00512D50 recomposes each tick as
Frame::combine(parent part frame, holding frame) - and
CPartArray::GetSelectionSphere @ 0x00518B80 scales the authored sphere by that
object's own part-array scale. acdream stores the PARENT's root in the child
projection's Position/Rotation because the child's MeshRefs are
parent-relative, which put the vivid brackets at the wielder's feet. The
composed child root is already published per frame to EntityEffectPoseRegistry
by EquippedChildRenderController.PublishChildPose, so selection now borrows it
through an injected Func<uint, Matrix4x4?> wired in LivePresentationComposition
beside the existing selection-sphere hook. There is no parent fallback: a child
with no published composed root has no live frame this tick and no sphere. Its
part-array scale comes from the spawn record, the same source
EquippedChildRenderController.TryRealize reads, because an Attached WorldEntity
carries the parent-derived pose rather than its own ObjScale.
The sr_Use branch of RecvNotice_SmartBoxObjectFound @ 0x004E5AD0 guards
ItemHolder::UseObject with `found->pwd._wielderID != SmartBox::player_id` at
0x004E5BE9 while still selecting and flashing. Equipped-child picking makes
that click reachable, so the gate ships with it as
IWorldSelectionQuery.IsWieldedByPlayer.
CPhysicsObj::SetLighting @ 0x00511A80 is non-recursive, so the pulse lights the
clicked object's own part array only - clicking a weapon never flashes its
wielder. That follows from routing the pulse identity through the same
predicate.
RetailWorldPicker, RetailSelectionScene, WbDrawDispatcher, and
EquippedChildRenderController are untouched, as are all wire and physics paths.
The slice REMOVES an undocumented deviation (Attached projections excluded
from pick eligibility versus retail's part-id pick) and introduces none, so no
retail-divergence-register row is owed in either direction.
Gates: dotnet build green; AcDream.App.Tests 3,951 passed / 3 skipped;
complete Release solution 9,783 passed / 5 skipped;
tools\run-connected-world-lifecycle-gate.ps1 RESULT=PASS.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The named-retail oracle settles the child-vs-parent question: retail's pick records part->physobj->id (CPhysicsPart::Draw 0x0050D7A0, GfxObjUnderSelectionRay 0x0054C740), equipped children are first-class CPhysicsObjs whose m_position IS the composed hold frame (add_child 0x0050F870, UpdateChild 0x00512D50), so a click on a wielded weapon returns the weapon's own guid with no parent redirection and no wielded-specific gate. Selection, the non-recursive click flash (SetLighting 0x00511A80), and the vivid brackets all anchor to the picked child; only sr_Use on your OWN wielded item is suppressed (0x004E5BE9).
The gap analysis found acdream's picker already correct - equipped children publish selection parts under their own guid and already win the ray test. The failure is downstream eligibility: PickAt requires the World-kind-only interaction set, so the winning hit is discarded. The slice is therefore a scoped pick-eligibility predicate plus a marker anchor sourced from the already-published child root pose - deliberately NOT widening the interaction/radar/auto-target set, which retail also keeps free of wielded items.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The acceptance session on Coldeve ran 20 portal transits with zero wedges and captured a real wire-loss recovery live (resend/s=1 nak-in=1 mid-session, converged net-final ledger, graceful logout) - the event class that permanently killed sessions before N1. #260 is closed on that evidence. The one unrelated observation (first-login run-on-the-spot until a recall reset, self-healed, not reproduced on relogin) is filed as #262 with hypotheses and the no-workaround rule restated. Campaign doc, roadmap, and CLAUDE.md pointers flipped to the closed record.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
All seven slices shipped and reviewed. The campaign doc status header and ISSUES.md #260 now record the implementation-complete state with every slice SHA; the campaign doc's double-encoded punctuation (one early PS5.1 ANSI round-trip) is repaired to clean UTF-8. Remaining acceptance: the user Coldeve endurance session.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The final slice review verified every retail address claim down to the
three distinct gate strictness masks (0x41 strict for NAK/handshake, no-ZF
>= for the 5 s sweep) and found no handshake, eviction, or ring defect.
This acceptance settles the campaign's remaining bookkeeping debt the
review surfaced: TS-58 (no TimeSync/Echo keepalive), TS-59 (no Flow
report), TS-60 (no 140 s dead-link/referral), TS-61 (send-failure burns
sequence+key), and AP-126 (one monotonic clock) are now real register
rows instead of dangling citations in shipped code. DropAll additionally
resets the completed-sequence ring (INFO-4's latent session-reset trap),
and the ledger corrects the post-acceptance retry-drop attribution to
NetworkManager's pre-route (INFO-5). N6 SHA f9c5e47e and its revert line
recorded. Core.Net 757/757 green after the ring-reset change.
Campaign N's implementation is complete: N0-N6 all shipped, all reviewed.
The remaining acceptance is the user Coldeve endurance session.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>