fix(ui): FA4 re-review REOPEN — re-declare 0x00A6 from the post-world seam, not the pre-world reset
The FA4 fix round's MUST-FIX 3 placed the 0x00A6 reconnect re-arm at the
wrong lifecycle point (re-review 8bbceff5): ResetSessionTransientUi runs
via the SessionDialogs reset stage BEFORE _inWorld=true, so SetPanelOpen
(world-gated, Validate requireWorld:true) returned Inactive and published
nothing — yet _pageVisible was latched true anyway, so no later hook
re-declared and fellow vitals stayed frozen for the whole new session.
The unit test passed only because the fake recorded unconditionally.
Two-part fix, both retail-faithful mechanisms not suppressions:
- SocialFellowshipPageController.SetPageVisible advances the edge-trigger
latch ONLY when the declaration is Accepted (published), so a dropped
pre-world send leaves the latch clear and a later attempt retries.
- ResetSessionDeclaration (pre-world) now ONLY clears the latch; the new
RedeclareAfterWorldEntry fires from the LiveSession EnteredWorld seam
(wired via RestoreLayout, idempotent if a persisted layout already
re-showed the page) so a still-open Fellowship page re-declares 0x00A6
in world and vitals resume.
Regression pins that actually catch it (the prior test could not):
- SetPageVisible_DoesNotLatch_WhenDeclarationDropped_SoItRetriesInWorld
(widget-level root, world-gated fake);
- Reconnect_ReDeclares0x00A6_AfterWorldEntry_NotDuringPreWorldReset +
Reconnect_StaysSilent_WhenFellowshipPageIsNotActuallyOpen (panel-level,
world-gated). RED-verified: reintroducing the pre-world declaration
fails the reconnect test.
Full Release suite: 13,286 passed / 4 skipped / 0 failed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
8bbceff594
commit
04161defd8
7 changed files with 148 additions and 42 deletions
|
|
@ -157,7 +157,17 @@ internal sealed class LiveSessionRuntimeFactory
|
|||
ClearCombat: _domain.Actions.Combat.Clear),
|
||||
EnteredWorld: new(
|
||||
SetActiveCharacter: _interaction.Settings.SetActiveCharacter,
|
||||
RestoreLayout: () => _ui.RetailUi?.RestoreLayout(),
|
||||
RestoreLayout: () =>
|
||||
{
|
||||
_ui.RetailUi?.RestoreLayout();
|
||||
// MUST-FIX 3 re-fix (FA4 re-review REOPEN): re-declare a
|
||||
// still-open Fellowship page's 0x00A6 now we are in world —
|
||||
// RestoreLayout is the post-world UI-restore moment, and
|
||||
// this is idempotent if RestoreLayout already re-showed the
|
||||
// page (the latch is already set). ResetSessionTransientUi
|
||||
// (pre-world) only cleared the latch.
|
||||
_ui.RetailUi?.RedeclareSocialPanelAfterWorldEntry();
|
||||
},
|
||||
SyncToolbar: () => _ui.RetailUi?.SyncToolbarWindowButtons(),
|
||||
LoadCharacterSettings: _interaction.Settings.LoadCharacterContext,
|
||||
ArmPlayerModeAutoEntry: _interaction.PlayerModeAutoEntry.Arm),
|
||||
|
|
|
|||
|
|
@ -533,8 +533,18 @@ public sealed class SocialFellowshipPageController
|
|||
public void SetPageVisible(bool visible)
|
||||
{
|
||||
if (_pageVisible == visible) return;
|
||||
_pageVisible = visible;
|
||||
_bindings.SetPanelOpen(visible);
|
||||
// MUST-FIX 3 re-fix (FA4 re-review REOPEN, 2026-08-12): advance the
|
||||
// edge-trigger latch ONLY when the 0x00A6 declaration was actually
|
||||
// published (Accepted). A generation reset runs BEFORE the
|
||||
// reconnected session is in world, so this command is world-gated and
|
||||
// returns Inactive there — publishing nothing. The original fix
|
||||
// latched unconditionally, leaving `_pageVisible = true` while the
|
||||
// fresh server was never told, so the intended post-world
|
||||
// re-declaration became a no-op (already latched) and fellow vitals
|
||||
// stayed frozen. Leaving the latch untouched on a dropped publish
|
||||
// lets RedeclareAfterWorldEntry retry exactly once, in world.
|
||||
if (_bindings.SetPanelOpen(visible).Status == RuntimeCommandStatus.Accepted)
|
||||
_pageVisible = visible;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -280,26 +280,34 @@ public sealed class SocialPanelController : IRetainedPanelController
|
|||
_fellowship?.SetPageVisible(_visible && IsShowingFellowship);
|
||||
|
||||
/// <summary>
|
||||
/// MUST-FIX 3 (FA4 fix round, 2026-08-12): called from
|
||||
/// <see cref="RetailUiRuntime.ResetSessionTransientUi"/> — a seam that
|
||||
/// already runs on every generation reset — to re-arm the Fellowship
|
||||
/// page's <c>0x00A6</c> edge-trigger latch. <see cref="SocialPanelController"/>
|
||||
/// is process-lifetime and the latch
|
||||
/// (<see cref="SocialFellowshipPageController.SetPageVisible"/>) is
|
||||
/// edge-triggered on a bool that survives a reconnect unchanged, so
|
||||
/// without this a still-open Fellowship page never re-declares
|
||||
/// <c>0x00A6</c> on the fresh session and every fellow's vitals freeze
|
||||
/// for its remaining lifetime. Clears the latch, then re-evaluates the
|
||||
/// SAME D4 conjunction <see cref="OnShown"/>/<see cref="OnHidden"/>/tab
|
||||
/// switches use — a still-open Fellowship page re-declares; a closed or
|
||||
/// other-tab page correctly stays silent (the conjunction is false, so
|
||||
/// the now-cleared latch is simply left at its default).
|
||||
/// MUST-FIX 3 (FA4 fix round, 2026-08-12) — the PRE-WORLD half. Called
|
||||
/// from <see cref="RetailUiRuntime.ResetSessionTransientUi"/>, a seam
|
||||
/// that runs on every generation reset BEFORE the new session is in
|
||||
/// world. <see cref="SocialPanelController"/> is process-lifetime and the
|
||||
/// Fellowship page's <c>0x00A6</c> latch survives a reconnect unchanged,
|
||||
/// so it must be re-armed for the fresh session — but ONLY CLEARED here.
|
||||
/// It must NOT re-declare from this seam: <c>SetPanelOpen</c> is
|
||||
/// world-gated, so a re-declaration attempted before world entry is
|
||||
/// dropped (Inactive). The actual re-declaration is
|
||||
/// <see cref="RedeclareAfterWorldEntry"/>, wired to the post-world
|
||||
/// <c>EnteredWorld</c> seam (FA4 re-review REOPEN, 2026-08-12).
|
||||
/// </summary>
|
||||
public void ResetSessionDeclaration()
|
||||
{
|
||||
_fellowship?.ResetPageVisibleLatch();
|
||||
UpdateFellowshipPageVisibility();
|
||||
}
|
||||
public void ResetSessionDeclaration() => _fellowship?.ResetPageVisibleLatch();
|
||||
|
||||
/// <summary>
|
||||
/// MUST-FIX 3 (FA4 fix round) — the POST-WORLD half. Wired to the
|
||||
/// LiveSession <c>EnteredWorld</c> seam, so it runs after a (re)connect
|
||||
/// has entered world and <c>SetPanelOpen</c> is Accepted. Re-evaluates
|
||||
/// the same D4 conjunction <see cref="OnShown"/>/<see cref="OnHidden"/>/
|
||||
/// tab switches use: a still-open Fellowship page re-declares
|
||||
/// <c>0x00A6</c> to the fresh server and fellow vitals resume; a closed
|
||||
/// or other-tab page stays silent (the conjunction is false, so the
|
||||
/// latch <see cref="ResetSessionDeclaration"/> cleared is left at its
|
||||
/// default). Idempotent — if a persisted layout already re-showed the
|
||||
/// page and <see cref="OnShown"/> re-declared, the latch is already set
|
||||
/// and this is a no-op.
|
||||
/// </summary>
|
||||
public void RedeclareAfterWorldEntry() => UpdateFellowshipPageVisibility();
|
||||
|
||||
/// <summary>
|
||||
/// Per-frame poll: the Fellowship/Allegiance empty-state gates (no
|
||||
|
|
|
|||
|
|
@ -749,6 +749,14 @@ public sealed class RetailUiRuntime : IDisposable
|
|||
SocialPanelController?.ResetSessionDeclaration();
|
||||
}
|
||||
|
||||
/// <summary>MUST-FIX 3 re-fix (FA4 re-review REOPEN, 2026-08-12): wired to
|
||||
/// the LiveSession <c>EnteredWorld</c> seam so a still-open Fellowship
|
||||
/// page re-declares its <c>0x00A6</c> panel-open state AFTER a (re)connect
|
||||
/// is in world — <see cref="ResetSessionTransientUi"/> runs pre-world and
|
||||
/// only cleared the latch, and <c>SetPanelOpen</c> is world-gated.</summary>
|
||||
public void RedeclareSocialPanelAfterWorldEntry() =>
|
||||
SocialPanelController?.RedeclareAfterWorldEntry();
|
||||
|
||||
public void UpdateCursor(IEnumerable<IMouse> mice)
|
||||
{
|
||||
CursorFeedback feedback = _bindings.Cursor.Feedback.Update(Host.Root);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue