Retained home after member switch — correction

Investigated 2026-09-22 at 0cc67515d24994ef439252453caac37f51d876a0 in imp/blazor-hybrid-service-layer-decoupling. The user subsequently approved implementing the Presentation correction and validating it before resuming acceptance. Historical device evidence is in SERVICE-LAYER-REVIEW-VALIDATION.html.

Confirmed cause

Authentication.NotifyState raises LoggedInChanged only when the boolean flag changes. A successful UAL-to-DAL sign-in preserves IsLoggedIn == true, but does raise ISessionState.Changed after publishing DAL. The real-service regression confirms this contract, including the published epoch and DAL identity.

AlpaIHomePage.OnInitializedAsync subscribes only to LoggedInChanged. Consequently, the retained home never starts a DAL feed load. Its _rendered page, components, and pending refresh state can remain associated with UAL. The new mounted-home test reproduces this without native UI automation: one outgoing feed load, DAL state publication, no second load, and the outgoing page model still retained.

The _feedInitialized guard is an additional obstacle, not the initiating cause: removing that guard alone cannot help when the subscribed event never fires. Changing the session service to fabricate a logged-in transition would violate its documented event contract and affect unrelated consumers.

Implemented correction

The correction uses the existing ISessionState.Changed port in Presentation. Authentication, Application, and Infrastructure behavior are unchanged. A shared immutable presentation snapshot compares the published username (case-insensitively), MEC, LEC, user type, and logged-in flag. An ordinary token refresh for the same context preserves the UI.

Regressions cover mounted-home switching, a new member in the same MEC, the same member changing MEC, delayed incoming content, late outgoing success/failure, late document-ready refresh, same-member token refresh, disposal, contract invalidation, menu invalidation, and the real authentication service's event contract. Older pilot-card/orchestrator stubs now provide the session state that contract ownership reads; their assertions remain intact.

External notifications must use the renderer dispatcher, and state must remain valid across awaits, consistent with Microsoft's Blazor synchronization guidance.

Red regression evidence before correction

Test Result before correction
MountedHome_MemberSwitchWithoutLogout_ReplacesPreviousMembersFeed Fails: expected 2 feed loads, observed 1; outgoing page model remains
MemberSwitchWithoutLogout_PublishesChangedWithoutLoggedInChanged Passes: actual Authentication publishes Changed and does not fabricate LoggedInChanged

The home test runs the production page lifecycle and subscriptions on a Blazor renderer, with deterministic completed feed responses. It omits unrelated child markup and asserts retained state and the feed boundary. It does not replace device acceptance.

Focused result: 7 passed, 1 failed, 0 skipped. Build: 0 errors, 5 warnings. Full Debug result: 2,906 passed, 1 failed, 0 skipped out of 2,907; the new retained-home regression is the sole failure. Full TRX: home-member-full-debug.trx, SHA-256: fbacf506b76505198de1776479bbde6b962fbd89105f1d8118afeba152ed25b3. Full logs and separate TRX files are retained under /private/tmp/AB2081-home-member-regression/; failed harness iterations are preserved too. The definitive focused TRX is home-member-investigation02.trx, SHA-256: bc3b2177f27046d94a6ec00860ba878937fe981172b35805d4694298204073a1.

Validation after correction

The full matrix passed on staged source tree 56952313552ca638d883a5da5b68ccee4b608005 based on 0cc67515d24994ef439252453caac37f51d876a0. The commit carrying this record contains the same application and test sources; the difference from that staged tree is this document. The correction adds 15 tests to the earlier 2,905 / 2,899 baselines.

Check Result
Debug unit + architecture 2,920 passed, 0 failed, 0 skipped
Release unit + architecture 2,914 passed, 0 failed, 0 skipped
DocsTests 4 passed, 0 failed, 0 skipped
Navigation freshness Current
Gateway catalog 75 routes, 0 gaps; catalog regression passed
ios-debug 0 errors, 650 warnings; 7.56 seconds
android-debug 0 errors, 676 warnings; 37.91 seconds
ios-release 0 errors, 653 warnings; 16.38 seconds
android-release 0 errors, 679 warnings; 79.88 seconds
TRX SHA-256
correction-full-debug02.trx 2246b49df381e0b541bf34acb7263ef31185a0651acf3b4c774649b2ce341484
member-correction-matrix-release.trx d5d69b3502b63f9f2bb831bf8a4b4f569928767d67535b0a354422d3260912fc
member-correction-matrix-docs.trx f2a9fc8232b4aad69009827ec173a2a8cc9c7384cef6fb94ab66388106eea7b9

All matrix commands follow the validation record's reproduction commands, with fresh TRX names. Raw output and build metadata are retained under /private/tmp/AB2081-home-member-regression/ and its matrix/ subdirectory. Initial failing iterations remain available: 80 focused tests passed before adding the last document-ready case; the first full run found 12 unimplemented getters in older stubs, which were corrected before the definitive full run above.

No hook bypass is used for the code commit. The compilation matrix's iOS signing-disabled artifacts are not deployable acceptance builds. Device acceptance requires a fresh, normally signed --no-incremental build and installed bundle identity proof. Prior device results do not establish that this correction passed on either platform; the final integration PR remains gated on the complete device walkthroughs.

Corrected device reproduction

Correction commit: 6c729bb7f29a1143606eeb1faa3cc498302679fb. After the complete automated matrix and unbypassed code hooks passed, a fresh signed build was installed and verified on the dedicated iOS03 simulator. UAL-to-DAL sign-in without logout returned a DAL home, drawer, MEC page, and rendered Delta contract. The original persistent stale-home reproduction passed.

Full acceptance is still incomplete: a 2 ms underlying DOM overlap has unresolved visibility, and controlled failure scenarios, cookie/favorites isolation, FTDT correctness, and Android acceptance remain open. The current validation record contains exact installed-build identity, per-walkthrough limits, evidence hashes, and teardown.

Legacy native-path visibility verification follow-up — failed

At application-equivalent revision db19d810e094a9f8dc6d20a29f977afab8ea9048, a fresh signed simulator build and a real UAL-to-DAL sign-in reproduced the 2 ms mixed DOM interval. The first frame callback after native return still contained UAL alone. Simulator video frame 25 visibly exposed outgoing UAL home content and its PDR / SUBMIT tab along the native pop animation's left edge; frame 26 showed the skeleton. Thus absence of mixed-member frame callbacks did not establish visual isolation.

The native login view model returns to root immediately after authentication succeeds, while the home clears via a queued renderer callback. Recommended correction: coordinate that return with Presentation invalidation and retain a neutral native cover until the safe webview frame is ready. No additional code was changed. Acceptance stopped; exact timing, frame counts, hashes, and cleanup are in the latest section of the validation record.

Acceptance scope correction

The user clarified that the UI walkthrough must use Blazor-hybrid sign-in. Invoking native LoginPage to bypass the signed-in /login redirect was an inappropriate test-path substitution. The native pop failure and proposed native cover are not evidence of a Blazor transition failure or a required correction for this task. Blazor logout/login is supported; no-logout switching needs an agreed Blazor entry point or test harness. See the latest scope correction for the remaining acceptance gates. The shared Presentation regressions remain valid; no application code was reverted.