wip(physics): collision O(changed) delta-commit (O1-O3) - ON HOLD, feel-test failed

Publication-throughput rework per the D2 design (docs/research/
2026-08-02-collision-throughput-handoff/design-note.md): O1 per-prefix
installed-key ledgers replacing the seal's full-map scans; O2 per-
landblock delta commit (LandblockReplacementApplyCursor against the
active root) replacing whole-world TransferTo; O3 empty staging root,
commit-time reflood (CObjCell::init_objects 0x0052B420 ->
recalc_cross_cells 0x00515A30), journal/peer-rebase machinery deleted
(~1,900 lines net).

Automated gates green: Runtime 999, Core physics 2,135, App 4,039/3,
Headless 79, complete solution 10,812/0/4; lifecycle gate PASS
(connected-world-gate-20260802-193029). Soak 194423: publication-side
acceptance fully met (37 -> 4 failures, all convergence dims zero,
loadedLandblocks baseline-identical, waitCue 6/9 -> 1/9).

COMMITTED AS WIP ON USER DIRECTION - NOT ACCEPTED. The user feel-test
FAILED on this tree: monsters still pop into existence at close range,
monsters spawned mid-air far ahead, static placements visibly wrong,
plus 243x "Landblock already has a full retirement receipt"
InvalidOperationException catch-retry loop during origin recenter
(launch-feeltest-oclone.log). The 4 remaining soak failures
(pendingLandblockRetirements 131/122 at the Caul->Sawato stops) and the
implementer's "exposed pre-existing" classification are under
re-judgment against that loop. Dual reviews were dispatched and then
stopped mid-flight on user direction; NO review has passed this commit.
Full problem inventory + next-agent instructions:
docs/research/2026-08-02-collision-throughput-handoff/.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-02 20:06:59 +02:00
parent c52ce14a07
commit 71604331cf
13 changed files with 6410 additions and 1894 deletions

View file

@ -1983,6 +1983,78 @@ public sealed class ShadowObjectRegistry
internal uint GetOwnerSlot(int index) => _ownerSlots[index];
/// <summary>
/// O3 (2026-08-02): commit-time owner application for one landblock
/// delta, replacing the deleted staged whole-world reflood context.
/// Retail hydrates a cell and refloods the objects associated with it —
/// <c>CObjCell::init_objects</c> (0x0052B420) →
/// <c>CPhysicsObj::recalc_cross_cells</c> (0x00515A30); the per-landblock
/// streaming analogue is: adopt each staged owner's registration/shape
/// payload and recalculate its cross-cells against the live post-delta
/// world, retire the outgoing generation's authored statics that were not
/// re-authored, and re-run the flood for every retained live owner
/// touching the replaced landblock.
/// </summary>
internal void ApplyCommittedOwnerReplacement(
ShadowObjectRegistry stagingSource,
uint ownerId,
uint landblockId)
{
ArgumentNullException.ThrowIfNull(stagingSource);
if (stagingSource.HasLogicalOwner(ownerId))
{
// Staged owner (authored target static, or an owner registered
// directly into the generation): adopt its payload, then
// recalc_cross_cells against the live world — the staged flood
// only saw the target-only staging root, so a seam footprint
// completes here.
MirrorOwnerFrom(stagingSource, ownerId);
RefloodOwnerForLandblock(ownerId, landblockId);
return;
}
if (IsStaticOwnerRootedIn(ownerId, landblockId))
{
// Outgoing generation's authored static, not re-authored by the
// replacement: it ends with its landblock (same lifetime rule as
// RetireOwnerFromLandblock's static branch).
DeregisterCore(ownerId, publishMutation: false);
RemoveOwnerPrefixMembership(ownerId);
_ownerVersions.Remove(ownerId);
AdvanceMutationRevision();
return;
}
// Retained live owner touching the replaced landblock: recalculate its
// cross-cells against the new topology. Suspended or since-removed
// owners no-op inside the reflood.
RefloodOwnerForLandblock(ownerId, landblockId);
}
/// <summary>
/// O3 (2026-08-02): retail <c>CObjCell::init_objects</c> refloods every
/// object associated with the hydrated cell at hydration time. Owners
/// that became associated with the replaced landblock after the sealed
/// capture (a mover entering the prefix mid-publication) are present in
/// the live prefix-owner slots but absent from the sealed owner list;
/// recalculate their cross-cells against the just-installed topology.
/// </summary>
internal void RefloodPrefixOwnersAfterReplacement(
uint landblockId,
IReadOnlyList<uint> sealedOwnerIds)
{
uint prefix = landblockId & 0xFFFF0000u;
if (!_prefixOwnerSlots.TryGetValue(prefix, out List<uint>? slots))
return;
var applied = new HashSet<uint>(sealedOwnerIds);
int limit = slots.Count;
for (int index = 0; index < limit; index++)
{
uint ownerId = slots[index];
if (ownerId == 0u || !applied.Add(ownerId))
continue;
RefloodOwnerForLandblock(ownerId, landblockId);
}
}
/// <summary>
/// Refreshes one staging owner from the exact active payload, then floods
/// it against the staging generation's complete cell graph. The returned
@ -2321,14 +2393,16 @@ public sealed class ShadowObjectRegistry
if (_stagingSlotIndex < _stagingSlotLimit)
{
uint ownerId = _stagingSlots![_stagingSlotIndex++];
if (_staging._entityReg.TryGetValue(
ownerId,
out RegistrationRecord? registration)
&& registration.IsStatic
&& (registration.SeedCellId & 0xFFFF0000u) == _prefix)
{
// O2 (2026-08-02): every staged logical owner in the
// target's prefix slots — authored statics AND owners
// registered directly into the staging generation —
// must reach the active world through the delta
// commit's owner installs; the old whole-root transfer
// carried them implicitly. Mirrored owners identical
// to their active state reinstall in place, so the
// wider filter stays exact and prefix-scoped.
if (_staging._entityReg.ContainsKey(ownerId))
AddOwner(ownerId);
}
WorkUnits++;
return false;
}