PR 2522 service-layer review resolution

This ledger responds to all 118 stable finding IDs in the original review. It covers the corrected cumulative implementation on codex/service-layer-review-fixes, based on Phase 7 6633bb59 and the latest integrated app branch snapshot 5152a5c4. Earlier phase PR history is retained; fixes target imp/blazor-hybrid-service-layer-decoupling. No final production-branch merge is authorized by this document.

Addressed means the code, test or documentation remedy exists at this cumulative tip. It does not mean native device acceptance passed. Findings refuted by the original review retain their recorded disposition and historical reasoning at the reviewed phase; those rows do not describe every later source change. F68 retains the guarded synchronous start because moving it outside the generation barrier would reopen an account-boundary race; no device contention measurement is claimed.

Validation

The final exact commands, tested code revision/tree, counts and native build results are recorded in review validation. Intermediate failures were read from uniquely named TRX files before rerunning. Historical phase counts remain in implementation evidence; they are not substituted for this review pass.

Native device walkthroughs were open at this review tip. They subsequently passed on reconciled application 8fb3181f1cefe66018305ac5dda917a0ec6fddfb: iOS run 16 followed by Android run 17. The current validation record contains per-walkthrough results, installed-artifact proofs, exact counts and evidence hashes. This closes device acceptance; the final integration still requires the target's human approval.

Finding dispositions

ID Status Remedy or retained disposition and evidence
F01 Addressed Fixed remaining tip behavior; earlier aggregate code is historical — MenuService now distinguishes explicit context invalidation from transport timeout and retains installed-menu fallback. MenuService_MenuCacheFallbackTests.RefreshTransportTimeout_ReturnsInstalledMenu; shared query timeout fallback covered under F18. Favorites has no persisted read fallback to restore at tip. Earlier P0 standalone warning remains historical.
F02 Superseded Already fixed in Phase 5: ordered logout participants continue after individual cleanup failures; Authentication_LogoutCacheContextTests and Authentication_PublicationTests retain the regression coverage.
F03 Addressed Fixed — CacheContextCoordinator.Generation uses Volatile.Read; writes remain under the atomic coordinator. CachedRemoteQueryTests.GenerationRead_DoesNotWaitForAnUnrelatedCacheCommit holds an actual commit while another reader obtains generation.
F04 Superseded Already fixed in Phase 5: epoch/publication guarding blocks refresh resurrection after logout. Authentication_PublicationTests.SilentResponseAfterLogout_CannotRestoreTokenUserOrFlags.
F05 No change — refuted Caller-side contract documented in ICacheStore.cs:9-13; every non-test writer at the tip wraps the write in TryExecute with a pre-fetch generation.
F06 Addressed Fixed — Dynamic home feed and main warmup explicitly consume ContextInvalidatedException before the error/not-found branch. Existing generation rejection tests continue to cover producer behavior.
F07 Addressed Fixed: Favorites mutation publication is best-effort after server success and cannot repopulate an outgoing memo. TopNav and AppShell consume expected context changes. The transport preserves confirmed successful favorite HTTP mutation results while retaining pre-send guards. CacheContextSafetyTests addition/removal cases prove server success without outgoing memo publication; LogoutAfterServerCommitsFavorite_PreservesConfirmedMutationOutcome covers the transport boundary.
F08 No change — refuted Plan checkboxes are all unchecked; inventory documents the prose contract and defers other families to later phases; codecs exist at the tip.
F09 No change — refuted Failure scenario unreachable: non-refresh reads return the memo or persisted cache; the old flag was never reset and was the latent bug.
F10 Addressed Fixed: parameterized late-result coverage spans all registered cache partitions, with zero persistence or publication after invalidation. CacheWriteBoundaryTests.Raw_Api_Cache_Writes_Stay_In_Guarded_Owners rejects production raw cache writes outside the recognized captured-generation publication paths.
F11 No change — refuted No new DataManager exists; DI registers IConnectivityService; fallback never taken and deleted with DataManager in P7.
F12 Addressed Fixed: MauiProgram restores native service-layer request/resource timeouts to 30/60 seconds. Native cancellation timing remains a device acceptance check.
F13 Addressed Fixed: AuthenticatedRequestProvider allows one auth recovery independently of MaxRetries, within the original deadline. ZeroTransientRetries_StillAllowsOneAuthRecovery, TransientAttemptsAlreadyExhausted_StillPermitsOneIndependentAuthRecovery.
F14 Addressed Fixed: MauiProgram explicitly resolves validated IOptionsMonitor.CurrentValue after Build and before startup consumers; it does not rely on Generic Host startup validation.
F15 Addressed Fixed: refresh is accepted only after rereading a nonempty token different from the rejected token. A fresh request can retry a completed failed recovery instead of retaining that failure for the token lifetime. SuccessfulRefreshWithUnchangedToken_DoesNotReplayRejectedCredential, FreshRequestAfterFailedRecovery_CanRecoverAnUnchangedToken.
F16 Addressed Fixed — Foreground refresh replaces older non-refresh/background producer; explicit per-key invalidation retires a forced producer after mutations. Subscriptions toggle invalidates its query key. Concurrent equivalent forced requests still share one producer. ExplicitRefresh_ReplacesAnOlderBackgroundFetch_AndOldResultCannotOverwriteIt, KeyInvalidation_PreventsPreMutationProducerFromPublishing.
F17 No change — refuted Every forced-expiry path runs logout (advancing generation) before another member can sign in; CachedRemoteQuery had no production consumer in P1; SignInContextReset added in P7.
F18 Addressed Fixed — Bare OCE/TaskCanceled without an actually canceled token now follows transient fallback; explicit context changes and real canceled tokens still propagate. TransportTimeout_WithoutCancellation_ReturnsPersistedFallback; existing caller-wait cancellation and context tests retained. Full-suite MEC compatibility cancellation now supplies an actually canceled token, with a paired token-transport timeout test verifying stale fallback.
F19 No change — refuted AuthRestClient consumes IRawRequestProvider at the tip and AuthRestClientTests covers both bodies; P1 has RawNoRetry_PreservesStatusAndBody.
F20 Addressed Fixed for the corrected cumulative implementation: checked-in validation record pins the tested code tree and exact commands/counts; historical phase snapshots are explicit. Earlier phase results are historical and cannot authorize shipping an intermediate phase alone.
F21 Addressed Fixed: InvalidatedContext_DoesNotReturnLateToken keeps IsLoggedIn true, so only the generation guard can reject the old token.
F22 Addressed Fixed: automatic and supplied bearer attachment requires HTTPS plus the exact configured gateway origin (including port); unknown Optional requests remain anonymous, unknown Required requests fail before sending. UntrustedOrigin_NeverAcquiresOrLeaksBearer. Native redirect handling remains device validation.
F23 Addressed Fixed: gateway readiness, mobile-content API and fault-injection docs use endpoint options/configurator and current client owners. Legacy incident descriptions are labeled historical.
F24 Addressed Fixed: final transport and JSON contract failures produce value-free method/operation/type diagnostics; transport and JSON Error messages no longer echo raw exception payloads. RequestProvider and AuthenticatedRequestProvider.
F25 No change — refuted Stale-snapshot scenario cannot happen (both Settings paths publish); other items are planned P1 work.
F26 No change — refuted HttpError/DeserializationFailed fallback and Sentry reporting restored in P3 (MecService.DefaultOptions, LegacyCatalogFailure); 401-no-fallback is the plan's documented rule (line 333, 740, 1823). Refuted 2 of 3.
F27 Addressed Fixed — MyPilotGroupPageViewModel marshals Groups lookup and both bound status updates through the existing BannerPageViewModel main-thread dispatcher. MyPilotGroupEventReadinessTests.EventsReadyWork_FromPoolThread_ChangesBoundCountsOnlyThroughUiDispatcher.
F28 Addressed Fixed: MecNullableDecimalJsonConverter preserves null, empty string, number and numeric-string hotel coordinates. Hotels_NullableCoordinatesPreserveLegacyStringInputs.
F29 Addressed Fixed — CachedRemoteQuery accepts the owning service's captured expectedGeneration; all delegate-based services pass it, including Content's outer banner/config operations. The helper rejects a stale delegate before installation. RevokedExpectedGeneration_DoesNotInstallAnOldDelegateInTheCurrentContext.
F30 Addressed Fixed remaining tip gaps — MEC corrupt installed row eviction plus online recovery tested end-to-end through MecService. Event-count UI dispatch tested under F27. Aggregate forwarding/clear-memo evidence is superseded by aggregate removal at P7.
F31 Addressed Fixed: named route constants replace inline routes in MecApiClient, NotificationsApiClient, SubscriptionsApiClient and ContentApiClient. MecService event readiness scopes use nameof on the MEC/LEC enum members.
F32 No change — refuted Endpoints are identity-independent app config; legacy sent no bearer pre-login; pinned by ConfigAndUpdate_ArePublicAndNeverAcquireABearer; plan line 796 lists app configuration as guest-safe. Refuted 2 of 3.
F33 Addressed Fixed — Background sign-out marks pending guest-banner refresh; the existing delayed/auth-gated resume path replays it after releasing the background latch. No early network herd introduced. RejectedSessionWhileBackgrounded_ReplaysGuestBannerRefreshAfterResume.
F34 Addressed Fixed — Waiters record the current per-wave attempt; after the active attempt completes they share its outcome even on failure. A later trigger can retry. Canceled/context-revoked waves do not suppress their replacement. ConcurrentFailedBulkTriggers_ShareOneAttempt_AndLaterTriggerCanRetry; existing resume replacement tests retained.
F35 Addressed Fixed: ContentService and default-returning ContentApiClient operations report through LegacyCatalogFailure/Report before forwarding the stamped exception to crash reporting. This restores the ALPA event and shared report marker without duplicate reports. Existing content HTTP/deserialization reporting tests retain the compatibility evidence.
F36 Addressed Fixed — Sign-out cancels outgoing auth/connectivity sources before generation-reset flags are cleared. Wave core carries its original generation into step runner and checks between dispatches and image sources. GenerationChangeBetweenSteps_StopsOutgoingWaveWithoutAnAuthEvent plus background/resume orchestration. A follow-up audit also guarded config/bulk/image/member await boundaries and ICA dispatch, passed the original generation to member steps and dynamic feed, and added AccountSwitchDuringBulkTail_DoesNotDispatchIntoTheIncomingContext at both bulk and member completion with an uncanceled caller token.
F37 Addressed Fixed — UI readiness uses volatile state flags and a double generation check without entering the cache coordinator; stale snapshot returns false. Existing readiness tests retained.
F38 Addressed Fixed: notif/list, flight notification list and createupdate upsert explicitly opt into three total attempts with two/four-second backoff plus jitter; read/upsert POST can recover one 401. Other mutation methods do not opt in. SafePost_TransientFailureRecoversWithoutDuplicatingOrdinaryMutations, updated exhaustion tests.
F39 No change — refuted Deliberate and tested (GetMemberAsync_UnauthorizedRetainsProtectedTypedError, AdditionalAllowlistCannotOverrideAuthorizationGuard); plan lines 795-796, 1074, 1823.
F40 Addressed Fixed: generic catalog failures include the operation and error code. ExceptionDispatchInfo.SetCurrentStackTrace preserves the reporting origin before Report with the actual operation caller. LegacyCatalogFailureTests verifies the operation, stack and one ALPA event on repeated reporting.
F41 Addressed Fixed: getalpano reads raw response text before decoding JSON strings; bare alphanumeric/numeric text preserves leading zeros. MemberNumber_ReadsLegacyTextWithoutLosingLeadingZeros.
F42 Addressed Fixed: Jumpseat/KCM JToken parsing explicitly uses DateParseHandling.None. KcmPublicMapCatalogue_NeverSendsMemberBearer_AndPreservesDateLookingNotes.
F43 No change — refuted App still resolves CacheContentService, which injects IDocumentsQueries (registered as DataManager), so the subscription exists before any sign-in path.
F44 Addressed Fixed: normalized flight audience identity participates in shared operation selection while the persisted key stays unchanged. Input changes retire the previous producer/memo, and producer/memo boundaries recheck identity. AudienceChange_DoesNotJoinOrPublishThePreviousAudienceFetch.
F45 Addressed Fixed — Actual registered scaffold routers resolved through RealServiceGraph with focused live ports; behavioral calls assert routing. Page-banner tester switch is changed between calls to verify live/mock/live. ServiceLayerReviewIntegrationTests.RegisteredScaffoldRouters_UseTheirLivePorts_AndBannerSwitchRemainsPerCall.
F46 Addressed Fixed — Real production ContentPrefetchCatalog executes every one of seven registered key-to-query mappings; exact invoked method checked per key. ProductionPrefetchCatalog_InvokesEveryRegisteredTargetOnItsOwningPort.
F47 Addressed Fixed: Notifications, Subscriptions and Jumpseat clients observe live endpoint snapshots, preserve v2 routes and use the same snapshot for bearer-origin validation. Behavioral endpoint-change coverage is in NotificationsApiClientTests and JumpseatAndSubscriptionsApiClientTests. Orchestration assertions now exercise background cancellation, failed-wave coalescing, guest resume, readiness and successful retry rather than weakened source-text checks.
F48 No change — refuted Infrastructure cannot reference the head model; production uses ToCriteria(); parity test is a deliberate migration guard.
F49 No change — refuted Plan's Result-plus-diagnostic contract is implemented at the port level; both crossings go through Application ports.
F50 Addressed Fixed: document download is an explicit read-only POST policy with bounded retries and one auth recovery. DownloadFailure_UsesBoundedReadPostRetryOrOneAuthRecovery. Ordinary POST replay guards remain covered.
F51 Addressed Fixed — Downloaded-file reads and catalog/disk enumeration occur outside coordinator, bracketed by generation checks. File publications/deletions remain atomic. Existing DocumentCacheServiceTests cover real disk/cache behavior.
F52 Addressed Fixed — Null document catalog evicts old persisted key instead of writing corrupt null. NullCatalogue_EvictsOldPayload_InsteadOfPersistingCorruptNull.
F53 No change — refuted No OCE source reaches the loop: token path swallows exceptions and downloads convert timeouts to Result failures.
F54 No change — refuted DocumentLogoutParticipant is in the real logout path in that test; the assertion fails if the generation does not advance.
F55 Addressed Fixed: AuthServiceCompositionTests resolves real SettingsService, PrefetchTagOptionsConfigurator and its IDocumentPrefetchConfiguration alias, options monitors, and document services with only platform leaves replaced.
F56 No change — refuted Doc discloses the device walkthrough as not run (P4-A5 row, P7-A4 open); awaited viewer behaviour is documented as deliberate.
F57 Addressed Fixed: AuthRestClient permits two transport attempts (one retry) for JWT exchange only — fewer than legacy's three because a reply lost after the server rejected a password still counts toward account lockout; HTTP status retry remains owned by Authentication to prevent nested status retry multiplication. Existing status tests still assert one raw status send. FirstTransportFailure_RecoversInsideExchangeBeforeCallerSeesFailure, NetworkFailure_ExhaustsTwoTransportAttempts_AndRemainsTransportClassifiable.
F58 Addressed Fixed — SilentLoginAsync suppresses per-attempt 500/502/504 dialogs just like 503; exhaustion retains the existing outage policy. Authentication_PublicationTests.SilentServerFailure_DoesNotShowAnInteractiveErrorPerAttempt.
F59 No change — refuted SecureMsalCache.Register is never called and UseMSAL is false, so no MSAL key exists; base never removed the MSAL account either.
F60 Addressed Fixed — SecureTokenStore and SecureCredentialStore use bounded native-write/gate waits. A timed-out write keeps exclusive ownership until its native task is observed and its late value removed; deferred clear queues behind it, so a subsequent sign-in cannot be overwritten or erased by old completion. Logout no longer waits forever on a pending token/credential publication. SecureTokenStore_ConcurrencyTests.HungWriteAndClear_TimeOutWithoutReleasingNativeIoGate_ThenEraseLateWrite; SecureCredentialStoreTests.HungPairWriteAndClear_AreBounded_AndLateCompletionCannotOverwriteNewPair. A platform keystore that never completes still cannot accept a new persisted session safely; subsequent calls fail boundedly rather than falsely claiming success.
F61 Addressed Fixed — FTDT syncing flag releases in finally after context change only when that operation acquired ownership. Existing late-success/failure tests now re-sync the same duty-period instance with a bounded wait.
F62 Addressed Fixed — ThemeCoordinator consumes its own context-invalidated resolutions as non-fatal while keeping caller cancellation unchanged; no stale theme writes. Updated ThemeContextTests assert non-fatal completion, current theme/cache preservation, and failed old secure-write rollback.
F63 No change — refuted Fixed in P6 (push started inside the same TryExecute); stuck flag sits on an outgoing in-memory instance.
F64 Addressed Fixed — New AfterCompletion logout phase starts observed anonymous push after session gates/revocation counter release. Notification anonymous registration explicitly avoids token acquisition/bearer attachment. Shared PushTokenRegistration serializes anonymous/member updates from both lifecycle effects and native callbacks and checks the queued epoch before starting, so old signed-out registration cannot overwrite the newer member's update. Authentication_PublicationTests.SlowPostLogoutPush_DoesNotBlockLogoutOrTheNextSignIn plus NotificationsService anonymous-registration test and PushTokenRegistration_OrderingTests.
F65 Addressed Fixed — App startup does not start a second logout after the auth session already rejected/revoked it. Missing credentials now queue the same guarded Session Expired recovery; locked accounts preserve AccountLocked recovery. Testable native dispatch seam verifies missing/rejected/locked recovery and suppression after a newer sign-in. Authentication_PublicationTests.MissingOrRejectedCredentials_QueuesOneRecoveryAfterRevocation, LockedAccount_QueuesAccountRecoveryAfterLogout, RejectedRecovery_DoesNotShowAfterNewSignIn.
F66 Addressed Fixed — Import self-heal preserves locally calculated Results when the server's sync response omits them. ImportSelfHeal_PreservesCalculatedResults_WhenServerOmitsThem exercises real temporary FTDT database/import/sync.
F67 Addressed Fixed — Legacy login VM shows inactive-attempt alert when publication returns false. Both success analytics/event now occur only after true publication; retained prior session no longer causes a false success event in finally. LoginPageViewModel_PublicationTests.SupersededPublication_ShowsRecoveryAndNeverReportsLoginSuccess.
F68 Retained guarded behavior Retained: StartCurrent intentionally starts synchronous effect entry under the generation barrier, with awaits outside. Moving Task.Run/Task.Yield outside would let old sign-in work capture a new member generation at FTDT/push entry. The synchronous-prefix cost remains unmeasured; a future performance change requires expected-generation effect contracts before moving that start.
F69 Addressed Fixed: AuthServiceCompositionTests builds the actual authentication, effects and logout graph and verifies all four narrow ports plus the compatibility alias resolve the same singleton. RealServiceGraph supplies isolated platform storage/preferences/files/navigation leaves.
F70 Superseded Superseded at the cumulative tip: Phase 6 removed the temporary bearer bridge. P5-A5 now explicitly records this disposition; current adapters have bearer propagation and 401 regression coverage. No intermediate bridge/device acceptance is claimed.
F71 Addressed Fixed: AuthRestClient restores the AuthenticateUser transaction through ITracer and finishes it in finally. AuthRestClientTests covers transaction completion for successful and rejected exchanges.
F72 Addressed Fixed: source and IL guards permit IAuthentication only in the concrete Authentication implementation/logger and the composition alias; new native consumers fail too.
F73 Addressed Fixed: per-record FTDT mapping reports a value-free error and continues valid records; pagination uses raw page count. Get_UnmappableRecord_DoesNotLoseValidRowsOrStopPagination.
F74 Addressed Fixed: GetMenuItemsAsync/GetFavoriteItemTypesAsync/GetFavoritesAsync ITracer spans restored; opt-in MobileMenu retry observer restores existing snackbar and ALPA debug event. ILogger retry messages identify the static operation.
F75 Addressed Fixed: MobileMenu adapter catches cancellation before Report; context invalidation derives from cancellation. CallerCancellation_IsNotStampedAsAReportedFailure.
F76 Addressed Fixed: ExistingToken bearer policy reads IAuthTokenProvider.GetCurrentToken without refresh or 401 recovery. Authentication supplies the accepted, unexpired published-session token; DynamicContent and LogApiClient select this policy. PublicFeed_NeverWaitsForTokenRefresh, BackgroundLogFlush_NeverStartsTokenAcquisition.
F77 No change — refuted Documented single-send (implementation doc lines 254, 272), plan bans write retry without a server idempotency contract; local queue retry budget is retained. Refuted 2 of 3.
F78 No change — refuted Menu_EmptyWireResponseRemainsAFailureForDomainFallback covers [], null, "" at the adapter.
F79 Addressed Fixed test gap: actual RequestProvider send boundary now tests managed, socket, timeout, canceled-native, disposed upload content and non-transient failures through production PlatformTransportErrorClassifier. Historical table remains historical, not evidence for production transport. ProductionTransport_ClassifiesFailuresAtTheActualSendBoundary.
F80 Addressed Fixed: architecture tests reject FTDT/MobileMenu generated type references in Domain, Application, Presentation and the head. Infrastructure owns their wire mapping.
F81 Addressed Fixed: removed the assertion on a snapshot captured before the failing reload. InvalidReload_ThrowsBeforeAnyClientReceivesAnInvalidSnapshot retains assertions on the current monitor validation failure and zero invalid-snapshot publications.
F82 Addressed Fixed: FlightSearchAirportLookup and MenuService_MenuItemFallbackTests replace obsolete names; runtime missing-service diagnostics name their actual ports. Historical inventory and incident references remain explicitly historical.
F83 Addressed Fixed: JumpseatResultsPage documents the actual FlightSearchService rethrow/null-result contract.
F84 Addressed Fixed — Cache/journal publication failures have a distinct classification and revoke-only logout, preserving stored credentials. Actual secure-store failures retain the AB#2220 erase-credentials policy. Authentication_PublicationTests.CleanupFailureAfterGenerationAdvance_RevokesPartialIncomingSecureState now verifies preserved credential pair.
F85 Addressed Fixed: FileSignInPublicationJournal durably flushes a non-secret pending marker before incoming JWT/MSAL identity writes; only successful final guarded cleanup/publication removes it. Startup recovery runs before token hydration and app consumers, purging persisted private state after an interrupted switch. Marker failures fail closed. SignInPublicationRecoveryTests.RestartWithPendingIdentityPublication_PurgesPersistedPrivateRowsBeforeCompletingJournal exercises persisted restart recovery.
F86 Addressed Fixed — Hydration preference scope reads lazy ISessionState.CurrentUser; FlightSearchPushNotificationService uses published session identity; unused PushNotificationHandler MemberJson read removed. No pending token-store identity reaches these consumers.
F87 Addressed Fixed — Removed abandoned explicit-epoch reset heuristic. Explicit credential publication and interactive MSAL force context reset directly; same-member silent refresh after an abandoned explicit attempt preserves cache generation. Authentication_PublicationTests.AbandonedExplicitAttempt_DoesNotPurgeTheRetainedMemberOnSilentRefresh.
F88 Addressed Fixed: ServiceLayerReviewIntegrationTests.RealSignInGraph_ClearsOutgoingRowsBeforePublishingIncomingMember_AndRejectsLateFetch composes real Authentication, AuthSessionEffects, SignInContextReset and persisted API/document stores. It waits on the document mutation gate, verifies outgoing identity remains visible while waiting, publishes incoming identity only after purge and rejects a late outgoing fetch. Only unrelated FTDT side effects are mocked.
F89 Addressed Fixed: AuthSessionEffects resets favorite hearts, hotel search state, unscoped member preferences, notification filters/sort and shortcut selection during context publication. Device environment, installation and remember-me preferences stay preserved. The real reset clears cached/PDF state; startup recovery invokes IAuthSessionEffects before identity hydration to clear preferences too. AuthMemberContextTests.PendingIdentity_UsesPublishedPreferenceScope_AndSwitchClearsUnscopedMemberChoices verifies the preference boundary.
F90 Addressed Fixed: the real DI graph test resolves each participant and checks its Phase/Order against registration metadata, including the new AfterCompletion push phase.
F91 Addressed Fixed: SharedActionsService and both Flight log-event paths resolve registered IALPALogService rather than the unregistered concrete type.
F92 Addressed Fixed — ICredentialStore.SetCredentialsAsync writes the pair behind one gate with no epoch early return between halves. Readers/clear cannot see a mixed pair. Failed second write removes both values. Timed-out pair drains and erases before releasing gate. SecureCredentialStoreTests.PairWrite_KeepsReadersAndClearBehindBothWrites and FailedSecondWrite_RemovesBothValues.
F93 Addressed Fixed: raw Installation serialization and APNS/FCM token logging are removed, including MauiProgram token/prefix output. AppProperties no longer dumps property values at initialization, omits token/JSON values in setters and uses value-free error logs.
F94 Addressed Fixed: source alias discovery normalizes global:: and recognizes aliases of nested target types. Mutation fixtures cover ordinary/global/native/nested aliases while excluding strings/comments.
F95 Addressed Fixed: the Phase 7 ratchet now requires zero active aggregate inventory entries independently of scanner equality; expanding the JSON allowlist cannot authorize a new aggregate consumer.
F96 Addressed Fixed: RemoteRawBoundaryTests inspects remote-source IL for generic JSON decoders targeting Domain models, including collection elements. Explicit embedded-asset providers are exempt because they read checked-in assets, not external responses.
F97 Addressed Fixed: SERVICE-LAYER-TEST-MIGRATION.html records removed method identifiers, current executable replacements and deliberate behavior changes; Phase 0 inventory is explicitly historical.
F98 Addressed Fixed: catalog_check scans Infrastructure/Remote plus Domain/Helpers/Constants.cs, fails on missing source targets/Postman collection, and catalog source entries name current clients. Offline regression test proves missing sources/new routes fail.
F99 Addressed Fixed: active gateway/FTDT upgrade/fault-injection instructions identify current owners; remaining historical aggregate examples carry visible migration notices with a current ownership link.
F100 Addressed Fixed: affected component specifications carry visible service-layer migration notes and current Updated timestamps. Audited stamps were preserved; this is not a layout audit.
F101 Addressed Fixed documentation milestone: the compatibility composite remains only for implementation/DI and legacy fixtures; its next removal requires migrating FakeAuthentication/legacy auth tests, then deleting compatibility methods and alias. The new ratchet forbids production growth.
F102 Addressed Fixed — Authentication's native iOS error classification calls LegacyRestNetworkErrorClassifier.IsTransientIosErrorCode; central four-code policy retained.
F103 Addressed Fixed: legacy public KCM airport host explicitly uses Bearer.None. Combined host/no-token/date-note regression test above.
F104 Addressed Fixed — Both native and Blazor environment switches await revoke-only logout before publishing changed hosts. Invalid publication attempts restore prior environment flag/endpoints. ApiEndpointOptionsTests.EnvironmentSwitch_DoesNotPublishNewEndpointsUntilLogoutCompletes exercises delayed logout before URL change.
F105 Addressed Fixed: Member/PAC mapping errors report only operation plus exception type, with no original exception attached; result messages contain no payload value. MalformedPacMember_NeverReportsPayloadValues.
F106 Addressed Fixed: DocumentLogoutParticipant acquires the document mutation gate, clears downloaded files before deleting document rows and continues catalog cleanup if file purge fails. AuthLifecycleEffectsTests.DocumentLogout_WaitsForOutgoingMutationBeforePurgingDownloadsAndCatalog guards late writes. ClearCache includes orphan category downloads; SignInContextReset atomically removes catalog-owned files before identity publication using the shared validated leaf-path helper. Tests cover orphan files, unrelated-file retention, member-switch purge and startup recovery with missing cache directories.
F107 No change — refuted Recorded as plan Q6 "Phase 3 refinement" and in implementation doc lines 130-135; routes and bodies pinned by tests. Refuted 3 of 3.
F108 Addressed Fixed: plan and Phase 0 policy matrix consistently list the explicit legacy auth-fallback exceptions, including subscriptions and airport reference data. Cancellation/context invalidation still never fall back.
F109 Addressed Fixed: Phase 5 evidence uses the exact P5-A1 through P5-A5 meanings. Device lifecycle/account-lock checks are Not run; removed bridge criterion is Superseded at the cumulative tip.
F110 No change — refuted Doc says the device walkthrough is pending and no simulator run is claimed; deadline and bearer behaviour are covered by transport tests. Refuted 2 of 3.
F111 No change — refuted Every device-gated row says "not run" or "remains pending"; P7-A4 and P7-A5 marked Open.
F112 Addressed Fixed: retry documentation lists GET defaults plus explicitly safe read/upsert POST and DELETE exceptions, the independent one-401 recovery allowance, and single-send business writes/log sink.
F113 No change — refuted Documented deviation attributed to the implementation request; plan line 964 already permits shared fixtures.
F114 No change — refuted Refuted 3 of 3: plan line 327 says to verify branch policy and use explicit review if none exists; PR 2505's body says auto-complete is disabled; CI runs on the final PR into imp/blazor-hybrid. The fact itself stands and is recorded in section 7.
F115 No change — refuted Plan line 966 mandates every phase PR opens into the integration branch; refuted 2 of 3.
F116 No change — refuted Plan requires explicit completion when no policy exists; each PR body names the merge order; refuted 2 of 3.
F117 Addressed Fixed base integration: merged app snapshots 4774e079 and 5152a5c4 into the isolated correction branch. Hunk resolutions preserve HotelDirectorySearchState DI/logout cleanup and theme placeholders while retaining captured-generation guards. Both shared stylesheet blobs match 5152a5c4 exactly. Automated validation belongs to the linked review validation record; device gates remain open.
F118 No change — refuted Workspace housekeeping, not a defect; phase5-saved holds no unpushed commits; .agents/ and .codex/ do not show as untracked.

Second-pass review of the correction (PR 2527 at 1eedae91)

An adversarial review of the correction itself, run 2026-09-21/22 against 1eedae91 with the hand-written fix isolated by git show --remerge-diff ee7e3417, produced eleven items. All are resolved on fix/AB2081-service-layer-review-fix-01 (stacked on codex/service-layer-review-fixes at 41ec2b28). IDs are R-prefixed to keep them distinct from the original 118. Pre-existing defects the review surfaced but that predate the correction are listed after the table and belong in separate work items.

ID Status Remedy and evidence
R01 Addressed Startup recovery ran RecoverAsync().GetAwaiter().GetResult() in the composition root with no catch while the session-publication.pending marker was retained by design, so one purge failure crashed every launch until reinstall. SignInPublicationRecovery.TryRecoverAsync returns the failure; MauiProgram reports it once AppServices is up and revokes the token store instead of hydrating (credentials kept, marker kept), so the launch credential check re-runs the switch as a context publication. The four early exits that left the marker pending now complete the journal. SignInPublicationRecoveryTests, Authentication_PublicationTests (journal injected). b1644573.
R02 Addressed MobileMenuApiClient.ObserveRetry called DisplaySnackbar, undoing AB#2715 (630548b3) which gated "Network issue detected. Retrying..." behind Diagnostic Mode; the test asserted the regressed call. Now DisplayDiagnosticSnackbar; MenuRetry_RestoresValueFreeNotificationAndFinishesTrace asserts zero plain snackbars. d36202df.
R03 Addressed One 30 s TotalTimeout covered every attempt, backoff and body read with no per-attempt limit; legacy gave each of three attempts its own 30 s, so a stalled socket consumed the deadline and the retry branch never ran. RequestOptions.AttemptTimeout (15 s) bounds each send through a linked token and fails as a transient transport error; TotalTimeout 60 s; MaxRetries 2 (three sends, as legacy; the prior default sent four); RetryDelay 2 s (2 s then 4 s, as legacy; the prior 200 ms default is too short for a mobile radio to recover). AuthRestClient alone caps at one retry (two sends): a reply lost after a rejected password still counts toward lockout. DocumentsApiClient.Policy keeps 30 s / 90 s because getdocuments is known to take up to 30 s. Values are to be tuned against p95/p99 latency telemetry. RequestProvider_RetryPolicyTests, DocumentsApiClientTests.Policy_*. 26d0c8e9, f967335a.
R04 Not reachable SilentLoginAsync re-captures the current generation after the MSAL step, which could send the previous member's stored credentials over a completed explicit sign-in sharing its epoch. Without MSAL nothing awaits between the generation check after the password read and the re-capture; UseMSAL is false and never set. A comment at the re-capture names the window and the guard required before enabling MSAL. 41ec2b28.
R05 Addressed CachedRemoteQuery cleared memo and in-flight work on an operationIdentity change but the persisted row is keyed by registry key alone; a failed fetch for flight audience {B} promoted {A}'s persisted list into memory as fresh. A sibling <key>#identity row records the producing identity for identity-bearing queries; a different marker discards the row on first use per generation or on change, including after restart. Rows without a marker stay compatible. NotificationsServiceTests.AudienceChange*. ac9dcf19.
R06 Addressed A refresh:true call retired an in-flight operation and every refresh:false peer received OperationCanceledException although its own token was never cancelled. SharedOperation.Successor lets the retired producer complete its waiters with the refresh result or failure. Invalidate and generation changes keep their pinned cancellation. CachedRemoteQueryTests.RefreshThatSupersedes*. 9c112512.
R07 Addressed CompleteJwtSignInAsync refusals from a secure-storage or cleanup failure showed "This sign-in attempt is no longer active", true only for supersession. Constants.MessageSignInNotCompleted replaces the literal duplicated in three view models with wording true for all three causes. 38ff64d2.
R08 Addressed (pre-existing) Sign-in never cleared web-auth cookies; only logout did (legacy 4774e079 had the same shape). Explicit sign-in over a live session bypasses logout and the auto-login WebView autofills the web form, so a surviving A-session cookie would show A's web page to B. AuthSessionEffects now clears cookies inside every context publication; RealSignInGraph_* asserts one clear per publication. 2da3bfba.
R09 Addressed Startup recovery purged caches but hydrated the secure store, so a kill after SetUserInfoJsonAsync(B) themed the logged-out UI with B's MEC and left B's token. TryRecoverAsync returns RecoveryOutcome(Interrupted, Failure); any interrupted switch revokes rather than hydrates, and the stored credentials complete the switch on the next silent refresh. a7df955c.
R10 Addressed A Retry-After beyond ~49.7 days made Task.Delay throw ArgumentOutOfRangeException out of SendRawAsync. A wait past the total deadline is now an infinite wait on the deadline token, ending as Timeout as documented. RequestProvider_RetryPolicyTests.RetryAfterBeyondTaskDelayCeiling_*. e78beb42.
R11 Addressed The real DI graph built without ValidateOnBuild/ValidateScopes; nine tests present at 6633bb59 were renamed or removed without a migration-map entry. RealServiceGraph now validates on build (five presentation/Blazor-dependent head adapters excluded by name; everything else validates clean) and the map lists the nine with their replacements. b31eb6b8.

Pre-existing defects surfaced by the same review, outside this correction's scope:

Test continuity and operational documentation

The test migration map names the focused successors of removed aggregate fixtures. Architecture tests now enforce zero aggregate consumers, narrow auth consumers, raw representation mapping, generated-client containment, and guarded raw cache writes. Active runbooks use endpoint options and the current FTDT adapter; historical service names in design/spec examples are visibly labeled.

Integration follow-up: immediate logout and navigation

Correction cd390aee51e92219ac693bf57cbc58c367b4f26a moves explicit Blazor logout to anonymous Home after synchronous identity revocation and before background cache cleanup. The publication barrier remains closed until cleanup ends. Sixteen unit regressions (including four captured-live-menu cases) and four browser regressions cover the transition and navigation isolation; focused iOS timing and tab checks are recorded at the top of the validation record. The blank tabs observed in the synthetic run were traced to its menu fixture. Four subsequent live UAL/DAL cycles matched actual API responses without an empty bar; legitimate empty server menus retain their existing semantics. This correction remains under review; the earlier acceptance results do not automatically validate this new revision.