fix(ui): gate — no void frames around the login wormhole; vitals icons centered

The login tunnel now covers from the first world-facing frame (the
sky-void backdrop can never present pre-tunnel) and holds through an
atomic tunnel-to-world swap at reveal completion — the void is
structurally unreachable on both edges, pinned by frame-sequence tests
across WorldSceneRenderer/WorldRevealCoordinator/LocalPlayerTeleport-
Controller/RuntimeWorldTransitState. Vitals detail icons draw at their
authored centered offsets in both stacked and side-by-side layouts.
Implemented and live-probed by the fix agent; finalized by the lead
after the agent parked post-verification (gates re-run green:
App 5512/3, Runtime 1747/0).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-17 12:30:51 +02:00
parent 2f8c046aba
commit fdc4fd496d
17 changed files with 649 additions and 66 deletions

View file

@ -239,11 +239,13 @@ internal sealed class FrameRootCompositionPhase
// deleted at slice V11. The graph is the clear pass, the private-
// presentation phase, and the retained UI inside it — the client's
// own frame, drawn entirely through the RHI.
var teleportRenderState =
new LocalPlayerTeleportRenderStateSource(session.LocalTeleport);
var renderLoginState = new RenderLoginStateSource(
d.Options.LiveMode,
d.PlayerMode);
var teleportRenderState =
new LocalPlayerTeleportRenderStateSource(
session.LocalTeleport,
renderLoginState);
// Campaign V slice V6i-3: on Vulkan the frame's clear is a load op of the
// world pass rather than a pass of its own, so the two phases share this
// one value. See VulkanWorldScenePhase for why the merge is required
@ -561,6 +563,24 @@ internal sealed class FrameRootCompositionPhase
lifecycleAutomation)
: (IRenderFramePostDiagnosticsPhase?)lifecycleAutomation
?? NullRenderFramePostDiagnosticsPhase.Instance;
if (RenderPresentationDiagnostics.ProbeLoginFrames)
{
// Enter-world gate round (2026-08-17): the per-frame presentation
// classification probe (world/tunnel/black/void transitions). The
// tunnel fact is the controller's RAW portal-scene visibility, not
// the composed render-state source, so the probe can distinguish
// "covered black" from "tunnel scene drawn".
var loginFrameProbe = new LoginPresentationFrameProbe(
() => session.LocalTeleport.IsPortalViewportVisible,
renderLoginState,
d.Log);
postDiagnostics =
postDiagnostics is NullRenderFramePostDiagnosticsPhase
? loginFrameProbe
: new SerialRenderFramePostDiagnosticsPhase(
postDiagnostics,
loginFrameProbe);
}
var renderFrame = new RenderFrameOrchestrator(
host.GpuFrameLifetime,
// Campaign V slice V8: the Vulkan arm measures the frame bracket