fix(world): #344 — a mid-teleport world-frame disagreement defers the projection instead of crashing
Some checks are pending
Headless portability / portable-headless (ubuntu-latest) (push) Waiting to run
Headless portability / portable-headless (windows-latest) (push) Waiting to run
Headless portability / linux-graphical (push) Waiting to run
Headless portability / linux-vulkan (push) Waiting to run

During a portal transit the two world-frame owners legitimately rebase
on different edges (Runtime at TeleportAdvanced, streaming only after
old-window retirement), and a spawn projection landing in that window
hit the #283 invariant as an unhandled render-path throw — the crash
the user hit entering a dungeon.

The guard's check is unchanged; only the disagreement RESPONSE is
discriminated on the canonical transit authority
(RuntimeWorldTransitState.IsTeleportActive, the same field the App
layer already reads for portal-in-flight): in transit -> the
materializer's existing "not yet" return, parking the projection on
its established retry rides (OnLandblockLoaded's re-attempt loop,
whose ordering guarantees agreement on retry because the recenter
coordinator adopts the new origin BEFORE unblocking new landblock
loads — verified at source; plus OnPosition recovery and
OnAppearance). Outside transit -> still throws: genuine corruption
stays loud. The implementer explicitly ruled out riding the Runtime
placement pump, which would have acknowledged-and-discarded the
completion receipt and silently dropped the entity forever.

Sabotage: removing the discriminator reddened the pre-existing #283
throw tests as well as the new not-in-transit test — the sabotage
defeats the original contract, not merely the new coverage. Four new
tests cover defer, defer-then-agree-then-succeed (projected exactly
once), throw-outside-transit, and the agreeing pass-through.

Clean-room suite: 11,261 passed / 6 skipped / 0 failed. #346 filed for
a sixth, distinct load-sensitive allocation flake observed during the
runs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-07 10:07:45 +02:00
parent 3f2b2dc3ed
commit 52bdf4df71
5 changed files with 205 additions and 12 deletions

View file

@ -168,4 +168,99 @@ public sealed class LiveWorldOriginStateTests
state.EnsureAgreesWithRuntimeFrame(0xF682FFFFu, 0xF6820033u);
}
// #344: the discriminated overload. During an in-flight portal/teleport
// transit, Runtime rebasing ahead of this owner is the ordinary shape of
// the race documented on EnsureAgreesWithRuntimeFrame's doc comment, not
// corruption — the caller gets back its existing "not yet" outcome (a
// false return, matching every other "not yet" case in
// DatLiveEntityProjectionMaterializer.TryMaterialize) instead of a thrown
// exception. Outside a transit the guard is unchanged: it still throws.
[Fact]
public void DisagreeingWorldFrameOwners_WhileTransitInFlight_DefersInsteadOfThrowing()
{
var state = new LiveWorldOriginState();
Assert.True(state.TryInitialize(0x09, 0x04));
bool agree = state.TryEnsureAgreesWithRuntimeFrame(
0xF682FFFFu,
0xF6820033u,
transitInFlight: true);
Assert.False(agree);
// The disagreement did not mutate the streamed origin — only
// Recenter (driven by StreamingOriginRecenterCoordinator) may do
// that.
Assert.Equal(0x09, state.CenterX);
Assert.Equal(0x04, state.CenterY);
}
/// <summary>
/// The full #344 round trip: a spawn arrives mid-portal (disagreement,
/// transit in flight) and parks; streaming then recentres to the same
/// destination Runtime already rebased to, and the exact same query that
/// deferred a moment ago now agrees — the caller's existing retry re-
/// attempts materialization and this is what lets it proceed. Passing
/// transitInFlight:false on the retry too shows agreement no longer
/// depends on the discriminator once the origins actually match.
/// </summary>
[Fact]
public void DisagreeingWorldFrameOwners_WhileTransitInFlight_ThenAgreeing_RetrySucceeds()
{
var state = new LiveWorldOriginState();
Assert.True(state.TryInitialize(0x09, 0x04));
bool firstAttempt = state.TryEnsureAgreesWithRuntimeFrame(
0xF682FFFFu,
0xF6820033u,
transitInFlight: true);
Assert.False(firstAttempt);
// Streaming recentres: old window fully retires, the streamed
// origin adopts the destination Runtime already rebased to.
state.Recenter(0xF6, 0x82);
bool retry = state.TryEnsureAgreesWithRuntimeFrame(
0xF682FFFFu,
0xF6820033u,
transitInFlight: false);
Assert.True(retry);
}
[Fact]
public void DisagreeingWorldFrameOwners_NotInTransit_StillThrowsWithTheOffsetInMetres()
{
var state = new LiveWorldOriginState();
Assert.True(state.TryInitialize(0x09, 0x04));
InvalidOperationException error =
Assert.Throws<InvalidOperationException>(() =>
state.TryEnsureAgreesWithRuntimeFrame(
0xF682FFFFu,
0xF6820033u,
transitInFlight: false));
Assert.Contains("World-frame owners disagree", error.Message);
Assert.Contains("45504m", error.Message);
Assert.Contains("0xF6820033", error.Message);
}
[Fact]
public void AgreeingWorldFrameOwners_TryVariantPassesRegardlessOfTransitFlag()
{
var state = new LiveWorldOriginState();
Assert.True(state.TryInitialize(0xA9, 0xB6));
Assert.True(
state.TryEnsureAgreesWithRuntimeFrame(
0xA9B6FFFFu,
0xA9B60001u,
transitInFlight: true));
Assert.True(
state.TryEnsureAgreesWithRuntimeFrame(
0xA9B6FFFFu,
0xA9B60001u,
transitInFlight: false));
}
}