TLDR
- Reviewed the stacked Service Layer Decoupling refactor: 8 branches
codex/service-layer-phase0tophase7, PRs 2505 and 2509 to 2515, tip6633bb59, against the plan inSERVICE-LAYER-DECOUPLING-PLAN.html.- Verdict: do not merge PR 2513 (phase 5) as is. One high finding: the JWT exchange lost all transport retry, so one network blip fails a token refresh, clears the token and blocks refresh for 30 seconds (F57).
- Four more phase-5 mediums sit in the same auth-lifecycle code (F62, F64, F65, F69). Two further mediums: F38 (PR 2511, notification reads and push registration lost transient retry) and F73 (PR 2514, one bad FTDT record aborts the whole import).
- PR 2505 (phase 0) must not ship alone: it removes the offline-cache fallback on timeouts until phases 2 to 7 land (F01).
- Tests and builds reproduce at the tip: 2,607 Debug and 2,601 Release unit tests pass, and the iOS and Android Debug builds succeed. The PRs had no CI, no policies and no reviewers, so this is the first independent run.
- 118 findings were raised, 89 confirmed (1 high, 6 medium, 71 low, 11 nit), 28 refuted, 1 unverified (F68).
- Open before the final PR into
imp/blazor-hybrid: device walkthroughs (P2-A4 to P7-A5) and a merge withimp/blazor-hybrid(4774e079), which now conflicts with the tip inALPAMobile/HeadServiceCollectionExtensions.csandALPAMobile/Services/Authentication.cs.
The refactor moves DataManager, RestService and Authentication behind N-Tier ports: Infrastructure services, a shared authenticated transport, a shared cache query, and four narrow auth ports. It is delivered as one branch and one PR per phase, each branch containing all earlier phases. Every PR targets imp/blazor-hybrid-service-layer-decoupling (dca3123d), so the PR diff in Azure DevOps is cumulative (phases 0 to N). The final integration into imp/blazor-hybrid has not been opened.
| PR | Phase | Branch | Tip commit | Files (incremental / cumulative) | Verdict |
|---|---|---|---|---|---|
| 2505 | P0 safety baseline | codex/service-layer-phase0 |
505ff93a |
54 / 54 | approve with changes |
| 2509 | P1 transport foundation | codex/service-layer-phase1 |
dd855bf2 |
38 / 85 | approve with changes |
| 2510 | P2 MEC pilot | codex/service-layer-phase2 |
9751e6ab |
27 / 101 | approve |
| 2511 | P3 remaining domains and prefetch | codex/service-layer-phase3 |
7d73ca93 |
99 / 173 | approve with changes |
| 2512 | P4 documents | codex/service-layer-phase4 |
19f852a7 |
54 / 208 | approve with changes |
| 2513 | P5 authentication split | codex/service-layer-phase5 |
4707790e |
157 / 327 | block |
| 2514 | P6 retire RestService |
codex/service-layer-phase6 |
14af5811 |
92 / 363 | approve with changes |
| 2515 | P7 retire DataManager |
codex/service-layer-phase7 |
6633bb59 |
85 / 411 | approve with changes |
PR titles: 2505 "refactor: establish service-layer phase 0 safety baseline", 2509 "refactor: add service-layer phase 1 transport foundation", 2510 "refactor: extract MEC queries in service-layer phase 2", 2511 "refactor: extract remaining service domains in phase 3", 2512 "refactor: extract document services in phase 4", 2513 "refactor: decouple authentication lifecycle in phase 5", 2514 "refactor: retire REST aggregate in phase 6", 2515 "refactor: retire data facade in phase 7".
Source documents in the repo:
docs/detail/SERVICE-LAYER-DECOUPLING-PLAN.html (phases in section 5, gates in section 7, completion criteria in section 10).docs/detail/SERVICE-LAYER-IMPLEMENTATION.html (on the stack branches).docs/detail/SERVICE-LAYER-PHASE-0-INVENTORY.html (on the stack branches).A multi-agent review run on 2026-09-21. The agents read files only through git show and git diff against the origin/codex/service-layer-phase* refs. Nothing was checked out, edited, posted or voted on.
The first workflow run hit a session limit partway through; it was resumed and completed, and this document is from the completed run.
ALPAMobile.Infrastructure once, RestService is deleted, IAuthentication splits into four ports plus an obsolete composite, the cache store keeps Newtonsoft, NSwag clients stay. Departures documented in the implementation doc as "deliberate refinements" were judged on whether their reasoning holds, not simply reported.imp/blazor-hybrid, not to the integration branch.origin/imp/blazor-hybrid into the integration branch, resolve the two conflicting files at hunk level, and rerun the full unit suite and both native builds at the merged commit.Line numbers refer to the phase-7 ref (6633bb59) unless a phase is named. IDs are stable within this document only.
Confirmed findings: 89. Severity after verifier corrections: 1 high, 6 medium, 71 low, 11 nit. Refuted: 28. Unverified: 1 (F68).
| PR | Phase | Verdict | Reason |
|---|---|---|---|
| 2505 | P0 | approve-with-changes | Blanket OperationCanceledException rethrow in 23 DataManager catch blocks removes the offline-cache fallback on HTTP timeouts and Android TCP aborts (F01). Fixed for DataManager by P2 to P7, so P0 must not ship alone. Also open: F02, F03, F04 (all low, F02 and F04 fixed in P5). |
| 2509 | P1 | approve-with-changes | iOS ServiceLayerHttpClient drops the TimeoutIntervalForRequest=30 / Resource=60 config from Fix 1988 (F12). Needs a device check; two-line fix. F14 (ValidateOnStart never runs in the MAUI host), F15, F16, F13 are low. |
| 2510 | P2 | approve | Only low or nit findings (F27, F29, F28, F30, F31). |
| 2511 | P3 | approve-with-changes | Notification lists and push registration lost the legacy transient retry, and the test was renamed to pin Requests == 1 (F38, medium, 3/3). Also F33, F36 (low after correction), F45. |
| 2512 | P4 | approve-with-changes | Document download lost the 5s retry and the silent-login retry (F50, low by majority of verifiers, medium as filed). Either add a bounded retry / one 401 refresh replay or record it as an accepted regression. |
| 2513 | P5 | block | F57 (high, 3/3): JWT exchange lost all transport retry, so one network blip fails refresh, clears the token and arms the 30s cooldown. Also F65 (lost Session Expired / Account Locked dialog on cold start), F64 (logout awaits push POST inside the revocation window), F62 (theme epoch throw aborts logged-out /home), F69 (no test builds the real auth DI graph). |
| 2514 | P6 | approve-with-changes | F73 (medium): FTDT import maps the whole page in the adapter, so one unmappable record stops all import and the sync timestamp never advances. F74, F75, F76 are low. |
| 2515 | P7 | approve-with-changes | Code is clean apart from low items (F84, F85, F86, F87, F88). Blocked on process gates: device walkthroughs P7-A4 and P7-A5 are open, and origin/imp/blazor-hybrid (4774e079) now conflicts with the tip in ALPAMobile/HeadServiceCollectionExtensions.cs and ALPAMobile/Services/Authentication.cs. |
Recommended merge and fix order:
imp/blazor-hybrid: merge origin/imp/blazor-hybrid into the integration branch, resolve the two conflicting files at hunk level, rerun the full unit suite and both native builds at the merged SHA, and run the open device walkthroughs (section 6).Sev column: corrected severity (filed severity in parentheses where it changed). Votes: held / total verifiers. Fixed later: whether a later phase in the stack repairs it.
| ID | Sev | PR / phase | File:line | Title | Votes | Fixed later |
|---|---|---|---|---|---|---|
| F57 | high | 2513 / P5 | ALPAMobile.Infrastructure/Remote/Authentication/AuthRestClient.cs:15,23-26 | JWT exchange lost all transport retry; one blip fails refresh, clears token, arms 30s cooldown | 3/3 | no |
| F38 | medium | 2511 / P3 | ALPAMobile.Infrastructure/Remote/NotificationsApiClient.cs:58,68 | Notification list reads and push registration lost legacy transient retry; test pins the regression | 3/3 | no |
| F62 | medium | 2513 / P5 | ALPAMobile.Application/Theming/ThemeCoordinator.cs:109,194,350 | ThemeCoordinator propagates ContextInvalidatedException and aborts whole page resolve |
3/3 | no |
| F64 | medium | 2513 / P5 | ALPAMobile/Services/Authentication/LogoutParticipants.cs:134-142 | Logout awaits push re-registration POST inside the revocation window | 3/3 | no |
| F65 | medium | 2513 / P5 | ALPAMobile/Services/Authentication.Session.cs:267-285,411-431 | Startup second logout bumps epoch and cancels the Session Expired / Account Locked dialog | 3/3 | no |
| F69 | medium | 2513 / P5 | ALPAMobile/HeadServiceCollectionExtensions.cs:157-201 | No test builds the real auth DI graph (P5-A1) | 3/3 | no |
| F73 | medium | 2514 / P6 | ALPAMobile.Infrastructure/Remote/FlightTimeDutyTime/FtdtApiClientAdapter.cs:40 | FTDT import maps whole page unguarded; one bad record aborts import | 3/3 | no |
| F01 | low (high) | 2505 / P0 | ALPAMobile/Services/DataManager.cs:393 | Blanket OCE rethrow removes offline cache fallback on timeouts | 3/3 | yes (P2-P7) |
| F02 | low (medium) | 2505 / P0 | ALPAMobile/Services/Authentication.cs:932-1000 | Logout cleanup reordered; cache or token-store failure aborts sign-out | 2/3 | yes (P5) |
| F03 | low (medium) | 2505 / P0 | ALPAMobile/Services/DataManager.cs:1833-1866 | Coordinator lock held across bulk insert, messenger send, file writes | 2/3 | no |
| F04 | low (medium) | 2505 / P0 | ALPAMobile/Services/Authentication.cs:936-975 | Token refresh not generation-guarded; can resurrect session after logout | 2/3 | yes (P5) |
| F06 | low | 2505 / P0 | ALPAMobile.Infrastructure/Remote/DynamicFeedQueries.cs:67-90 | ContextInvalidatedException treated as page failure by UI callers |
1/1 | no |
| F07 | low (medium) | 2505 / P0 | ALPAMobile/Services/MenuService.cs:287,440,466 | ContextInvalidatedException thrown into unprepared consumers; throws after successful favorites mutation |
3/3 | no |
| F12 | low (medium) | 2509 / P1 | ALPAMobile/MauiProgram.cs:247-296 | iOS ServiceLayerHttpClient drops NSUrlSession timeouts (Fix 1988) |
3/3 | no |
| F14 | low | 2509 / P1 | ALPAMobile/HeadServiceCollectionExtensions.cs:164-207 | ValidateOnStart on ApiEndpointOptions never runs in MAUI host |
1/1 | no |
| F15 | low | 2509 / P1 | ALPAMobile.Infrastructure/Remote/AuthenticatedRequestProvider.cs:115,161 | Failed/offline force-refresh reported as success; 401 replay reuses rejected token | 1/1 | no |
| F16 | low | 2509 / P1 | ALPAMobile.Infrastructure/Caching/CachedRemoteQuery.cs:154-185 | refresh:true joins in-flight fetch and returns pre-mutation data |
1/1 | no |
| F18 | low (medium) | 2509 / P1 | ALPAMobile.Infrastructure/Caching/CachedRemoteQuery.cs:203-215 | OCE catch precedes transient classifier; blocks stale fallback | 2/3 | no |
| F20 | low | cross | docs/detail/SERVICE-LAYER-IMPLEMENTATION.html:34-48 | Test evidence (TRX) not reproducible from repo | 1/1 | no |
| F21 | low | 2509 / P1 | UnitTest/Authentication_TokenProviderTests.cs:64-71 | InvalidatedContext_DoesNotReturnLateToken cannot fail on the generation guard |
1/1 | no |
| F22 | low | 2509 / P1 | ALPAMobile.Infrastructure/Remote/AuthenticatedRequestProvider.cs:22-25,96-103 | Default Optional bearer attaches JWT to any absolute URI, no host guard | 1/1 | no |
| F23 | low | 2509 / P1 | AGENTS.md:72 | Stale RestService.SetEndpoints() / KcmLegacyGatewayUrl references |
1/1 | partial |
| F24 | low | 2509 / P1 | ALPAMobile.Infrastructure/Remote/RequestProvider.cs:105,112,143-144 | Final transport and deserialization failures no longer logged | 1/1 | no |
| F27 | low | 2510 / P2 | ALPAMobile.Infrastructure/Caching/CachedRemoteQuery.cs:186-207 | EventsReadyMessage published from pool thread under coordinator lock |
1/1 | no |
| F29 | low | 2510 / P2 | ALPAMobile.Infrastructure/Services/Mec/MecService.cs:62-84 | Two independent generation captures can share a stale delegate | 1/1 | no |
| F31 | low | 2510-2511 / P2-3 | ALPAMobile.Infrastructure/Remote/MecApiClient.cs:25-39 | Routes and scope strings hard-coded inline | 1/1 | no |
| F33 | low (medium) | 2511 / P3 | ALPAMobile.Application/Services/Caching/CacheContentService.cs:197-206 | _backgrounded latch cleared only at end of resume; guest banner refresh skipped on session expiry |
3/3 | no |
| F34 | low | 2511 / P3 | ALPAMobile.Application/Services/Caching/CacheContentService.cs:263-285 | Removed in-flight guards let duplicate waves queue on _waveGate |
1/1 | no |
| F35 | low | 2511 / P3 | ALPAMobile.Infrastructure/Remote/Content/ContentApiClient.cs:108-116 | Content failures reported as synthetic InvalidOperationException |
1/1 | no |
| F36 | low (medium) | 2511 / P3 | ALPAMobile.Application/Services/Caching/CacheContentService.cs:197,217,371-376 | Logout no longer cancels in-flight authenticated prefetch wave | 3/3 | no |
| F37 | low | 2511 / P3 | ALPAMobile.Application/Services/Caching/CacheContentService.cs:46,52,378-390 | Readiness getters take the coordinator lock on the UI thread | 1/1 | no |
| F40 | low | 2511 / P3 | ALPAMobile.Infrastructure/Services/LegacyCatalogFailure.cs:21-26 | Reported exception lacks operation name and stack | 1/1 | no |
| F41 | low | 2511 / P3 | ALPAMobile.Infrastructure/Remote/MemberApiClient.cs:59-70 | Member-number lookup requires valid JSON body | 1/1 | no |
| F42 | low | 2511 / P3 | ALPAMobile.Infrastructure/Remote/JumpseatApiClient.cs:42 | Jumpseat catalogue parse coerces ISO-looking strings to dates | 1/1 | no |
| F44 | low | 2511 / P3 | ALPAMobile.Infrastructure/Services/Notifications/NotificationsService.cs:30-38 | Shared cache operation for flight notifications ignores audience set | 1/1 | no |
| F45 | low (medium) | 2511 / P3 | UnitTest/Phase3ServiceCompositionTests.cs:17-38 | P3-A1 scaffold routers never resolved in a test | 3/3 | no |
| F46 | low | 2511 / P3 | ALPAMobile.Infrastructure/Persistence/Search/ContentPrefetchCatalog.cs:7-25 | Prefetch catalog wiring untested | 1/1 | no |
| F47 | low | 2511 / P3 | UnitTest/CacheContentService_BackgroundingTests.cs:44-47,105-106 | Source-text and endpoint-toggle tests weakened | 1/1 | no |
| F50 | low (medium) | 2512 / P4 | ALPAMobile.Infrastructure/Remote/Documents/DocumentsApiClient.cs:13-38 | Document download lost all retry and silent-login / 401 recovery | 3/3 | no |
| F51 | low | 2512 / P4 | ALPAMobile.Infrastructure/Services/Documents/DocumentCacheService.cs:35-40,99-109 | Global lock held during file reads and catalogue scan | 1/1 | no |
| F55 | low | 2512 / P4 | UnitTest/Phase4ServiceCompositionTests.cs:22-40 | Container test overrides the registrations it should prove | 1/1 | no |
| F58 | low | 2513 / P5 | ALPAMobile.Infrastructure/Remote/Authentication/AuthRestClient.cs:40 | Real 500/502/504 mapping can raise modal Authentication Error on silent refresh | 1/1 | no |
| F60 | low | 2513 / P5 | ALPAMobile.Infrastructure/Security/SecureTokenStore.cs:115,159-167 | ClearAsync waits on unbounded write gate |
1/1 | no |
| F61 | low | 2513 / P5 | ALPAMobile.Application/Services/FlightTimeDutyTime/FTDTDutyPeriodService.cs:53-56,93-98 | IsSyncing stuck true after context change |
1/1 | no |
| F66 | low | 2513 / P5 | ALPAMobile/Helpers/FTDTDataSyncHelper.cs:311-330 | FTDT import self-heal saves server response wholesale (AB#2219 null-protection dropped) | 1/1 | no |
| F67 | low | 2513 / P5 | ALPAMobile.Presentation/ViewModels/LoginPageViewModel.cs:287,351-352 | Login returns silently when sign-in attempt is rejected | 1/1 | no |
| F70 | low (medium) | 2513 / P5 | ALPAMobile/Services/Authentication/LegacyAuthBearerBridge.cs:4-9 | P5-A5 bridge has no test; doc drops A5 | 3/3 | code yes (P6); doc no |
| F71 | low | 2513 / P5 | ALPAMobile.Application/Theming/ThemeCoordinator.cs | Phase 5 carries unplanned refactors; "AuthenticateUser" Sentry transaction dropped | 1/1 | no |
| F72 | low | 2513 / P5 | UnitTest/Architecture/head-consumers.json | No ratchet test for IAuthentication consumers |
1/1 | no |
| F74 | low | 2514 / P6 | ALPAMobile.Infrastructure/Remote/MobileMenuApiClient.cs:23-105 | Retry diagnostics and Sentry transactions dropped | 1/1 | no |
| F75 | low | 2514 / P6 | ALPAMobile.Infrastructure/Remote/MobileMenuApiClient.cs:45,115,140 | Cancellation and ContextInvalidatedException reported as GeneralError |
1/1 | no |
| F76 | low | 2514 / P6 | ALPAMobile.Infrastructure/Remote/DynamicContentApiClient.cs:53-62 | Optional-bearer clients block on token refresh inside their deadline | 1/1 | no |
| F79 | low | 2514 / P6 | UnitTest/TransientNetworkErrorClassifierTests.cs:56 | Transient-profile test asserts a production-dead method | 1/1 | no |
| F80 | low | 2514 / P6 | UnitTest/Architecture/LayerDependencyRulesTests.cs | No no-leak architecture test for FTDT/MobileMenu generated types | 1/1 | no |
| F84 | low | 2515 / P7 | ALPAMobile.Infrastructure/Caching/SignInContextReset.cs:28-37 | Cache cleanup failure during sign-in escalates to credential-erasing logout | 1/1 | no |
| F85 | low | 2515 / P7 | ALPAMobile.Infrastructure/Caching/SignInContextReset.cs:16-40 | New member's token persisted before outgoing member's cache purge | 1/1 | no |
| F86 | low | 2515 / P7 | ALPAMobile/Helpers/AppProperties.cs:430 | Readers of ITokenStore.UserInfoJson bypass the published-identity snapshot |
1/1 | no |
| F87 | low | 2515 / P7 | ALPAMobile/Services/Authentication.Session.cs:35-52,74-85,171 | Abandoned explicit sign-in leaves _explicitSignInEpoch unpublished |
1/1 | no |
| F88 | low (medium) | 2515 / P7 | UnitTest/Authentication_PublicationTests.cs:134-170 | Real sign-in publication path never exercised end to end | 3/3 | no |
| F89 | low | 2515 / P7 | ALPAMobile.Infrastructure/Caching/SignInContextReset.cs:29-38 | Sign-in reset clears only API cache and document DB | 1/1 | no |
| F90 | low | 2513 / P5 | ALPAMobile/HeadServiceCollectionExtensions.cs:170-199 | Logout phase/order declared twice | 1/1 | no |
| F91 | low | pre-existing | ALPAMobile/Services/SharedActionsService.cs:48 | Resolves unregistered concrete ALPALogService; document-open event never logged |
1/1 | no |
| F92 | low | 2513 / P5 | ALPAMobile/Services/Authentication.Session.cs:231-237,264 | Superseded sign-in can leave new username with old password | 1/1 | no |
| F93 | low | 2511 / P3 (pre-existing) | ALPAMobile/Services/FlightSearchPushNotificationService.cs:273 | Raw push tokens and Installation logged despite doc claim | 1/1 | no |
| F94 | low | 2505 / P0 | UnitTest/Architecture/SourceConsumerScanner.cs:57-70 | Scanner alias detection is exact-match | 1/1 | no |
| F95 | low | 2509, 2513 / P1, P5 | UnitTest/Architecture/HeadConsumerRatchetTests.cs:56-64 | Ratchet allowlist grew twice; "only remove" rule not enforced | 1/1 | partial |
| F96 | low (medium) | cross | docs/detail/SERVICE-LAYER-DECOUPLING-PLAN.html:1823,1947 | Plan-committed raw-boundary architecture check does not exist | 3/3 | no |
| F97 | low | cross | docs/detail/SERVICE-LAYER-PHASE-0-INVENTORY.html:80,82 | P7-A3 inventory absent; Phase 0 inventory cites deleted test classes | 1/1 | no |
| F98 | low | 2514 / P6 | scripts/gateway-readiness-check.py:51,1098 | Gateway catalog drift guard silently no-ops after RestService.cs deletion |
1/1 | no |
| F99 | low | 2514-2515 / P6-7 | docs/FTDT-DATA-MIGRATION-PLAN.md:147,154,310 | Runbooks still point at removed RestService/DataManager symbols |
1/1 | no |
| F101 | low | 2513-2515 / P5-7 | ALPAMobile.Application/Abstractions/Authentication/IAuthentication.cs:55 | Production code kept alive only by tests | 1/1 | no |
| F102 | low | 2514 / P6 | ALPAMobile/Services/Platform/LegacyRestNetworkErrorClassifier.cs:33 | iOS NSURLError transient-code list duplicated in three places | 1/1 | no |
| F104 | low | 2509, 2514 / P1, P6 | ALPAMobile.Presentation/Components/Pages/SettingsPage.razor.cs:153-185 | Environment toggle flips endpoints before session is revoked | 1/1 | no |
| F106 | low | 2512 / P4 | ALPAMobile/Services/Authentication/LogoutParticipants.cs:45-58 | Downloaded PDFs not purged at sign-out | 1/1 | no |
| F108 | low (medium) | 2511 / P3 | docs/detail/SERVICE-LAYER-DECOUPLING-PLAN.html:333,740 | Plan 4.2 auth-fallback statement contradicts itself; Phase 0 matrix omits subscriptions and airports | 3/3 | no |
| F109 | low (medium) | 2513 / P5 | docs/detail/SERVICE-LAYER-IMPLEMENTATION.html | Phase 5 evidence table reuses plan IDs with different meanings | 3/3 | no |
| F112 | low | 2509 / P1 | docs/detail/SERVICE-LAYER-IMPLEMENTATION.html:28 | Doc says only GET retries; false at tip | 1/1 | no |
| F117 | low | cross | docs/detail/SERVICE-LAYER-DECOUPLING-PLAN.html:327 | Integration branch not synced with imp/blazor-hybrid |
1/1 | no |
| F10 | nit (low) | 2505 / P0 | UnitTest/DataManager_CacheContextTests.cs:94 | Late-response barrier tested for few of ~25 paths | 1/1 | partial |
| F13 | nit (low) | 2509 / P1 | ALPAMobile.Infrastructure/Remote/AuthenticatedRequestProvider.cs:105-107 | 401 recovery disabled for GET with MaxRetries=0 |
1/1 | no |
| F28 | nit (low) | 2510 / P2 | ALPAMobile.Infrastructure/Remote/Mec/MecWireModels.cs:96-97 | STJ MEC wire models lack Newtonsoft empty-string numeric leniency | 1/1 | no |
| F30 | nit (low) | 2510 / P2 | UnitTest/MecCompatibilityTests.cs:251-271 | Phase 2 acceptance evidence partly missing | 1/1 | moot (P7) |
| F52 | nit | 2512 / P4 | ALPAMobile.Infrastructure/Caching/DocumentCacheCodecs.cs:19-21 | Persisted "null" catalogue treated as corrupt each launch | 1/1 | no |
| F81 | nit | 2514 / P6 | UnitTest/ApiEndpointOptionsTests.cs:143 | Tautological assertion | 1/1 | no |
| F82 | nit | 2514-2515 / P6-7 | ALPAMobile/Platforms/iOS/AppDelegate.cs:381 | Stale DataManager/RestService names in messages, comments, tests |
1/1 | no |
| F83 | nit | 2515 / P7 | ALPAMobile.Presentation/Components/Pages/JumpseatResultsPage.razor.cs:136-150 | Comments attribute flight-search contract to the jumpseat service | 1/1 | no |
| F100 | nit | 2515 / P7 | docs/component-specifications/card/pilot-card-property-mapping.html:130,170 | Component specs still prescribe injecting DataManager |
1/1 | no |
| F103 | nit (low) | 2510-2511 / P2-3 | ALPAMobile.Infrastructure/Remote/JumpseatApiClient.cs:27,37 | Member JWT sent to legacy gateway host on token-free endpoint | 1/1 | no |
| F105 | nit (low) | 2511 / P3 | ALPAMobile.Infrastructure/Remote/MemberApiClient.cs:34-38,51-55 | Deserialization exception messages embed payload values | 1/1 | no |
Format: defect, failure scenario, evidence, fix, verifier reasoning. Severity corrections are the majority of verifier votes.
F57 (high, 3/3 held; votes medium / high / high -> high). P5, PR 2513. JWT exchange lost all transport retry.
ALPAMobile.Infrastructure/Remote/Authentication/AuthRestClient.cs:15,23-26, ALPAMobile/Services/Authentication.cs:515-558,626-653, ALPAMobile/MauiProgram.cs:157-190, UnitTest/AuthRestClientTests.cs:69, plan lines 798 and 1551.RetryTransientAsync (3 attempts, 2s/4s) plus the Polly fallback that mapped transport errors and status >= 500 to a synthetic 503, which GetJWTAuthTokenWithRetryAsync retried again. AuthRestClient now uses Bearer = None, MaxRetries = 0 on ServiceLayerHttpClient (no Polly, no fallback), and RequestProvider never replays a POST. A transport failure becomes ConnectivityException / TaskCanceledException, classified Unreachable, which the refresh loop explicitly does not retry (Authentication.cs:550, "Unreachable-transport ... Not retrying").isAuthenticated=false, authToken cleared, lastRefreshFailureUtc stamped so every caller fast-fails with an empty token for 30s. Interactive login shows Connection Error on a single flake; LoginPageViewModel.RetryTransientLoginAsync retries only the 503/500 message strings.Policy = new() { Bearer = BearerPolicy.None, MaxRetries = 0 }; test NetworkFailure_IsOneAttempt_AndRemainsTransportClassifiable asserts Uris.Count == 1 (locks the regression in). The plan premise "JWT's refresh loop is the only retry" is true only for 5xx.Unreachable transport errors while NetworkAccess is Internet (bounded, jittered), or give AuthRestClient a transport-only retry (the JWT POST is the auth exchange, not a business write); correct the plan text. Unchanged at tip.F38 (medium, 3/3 held; votes medium x3). P3, PR 2511. Notification list reads and push registration lost transient retry.
NotificationsApiClient.cs:58,68, RequestProvider.cs:105-107, NotificationsService.cs:61, UnitTest/NotificationsFailureReportingTests.cs:15-32.RestService wrapped notif/list, the flight notification list (BUG-2206) and CreateUpdateRegistration (AB#2039) in RetryTransientAsync. The new client uses PostAsync, which RequestProvider retries zero times (only GET and opted-in DELETE retry). The doc rationale "single-send POSTs" fits writes; both lists are read-only and registration is an upsert. FlightSearchApiClient adds its own local 3-attempt read-POST retry for the same class of call. Plan line 797 requires an explicit allowlist or an approved change for read-by-POST endpoints; no approval is recorded.createupdate hits a SocketException: legacy retried at 2s/4s; new returns TransportError, swallowed without a report, so the device has no PilotComms push registration until the next trigger. One gateway 503 on notif/list yields a stale inbox plus a Sentry event.Assert.That(handler.Requests, Is.EqualTo(1)) at lines 31, 44, 55.FlightSearchApiClient retry owner or an idempotent-POST option; re-arm registration on transport failure.F62 (medium, 3/3; votes medium / low / medium -> medium). P5, PR 2513. ThemeCoordinator aborts whole page resolve.
ThemeCoordinator.cs:109,194,350, DynamicPageOrchestrator.cs:87-90, AlpaIHomePage.razor.cs:150, Authentication.Session.cs:363-365, LogoutParticipants.cs:91._cacheEpoch (bumped by ClearCache) makes Execute throw ContextInvalidatedException; both catch (OperationCanceledException) blocks rethrow it; DynamicPageOrchestrator has no catch. Logout raises LoggedInChanged (navigating to /home, starting a theme resolve at epoch E0) before ThemeLogoutParticipant (order 50) bumps the epoch to E1. This contradicts the IThemeCoordinator "non-fatal" contract and is new in P5 (base ClearCache only cleared dictionaries)./home LoadLoggedOutFeedAsync starts EnsureThemeAsync("GLOBAL") at (G2,E0); the participant sets E1; the resolve throws; AlpaIHomePage sets _pageNotFound = true, empties components, reports to Sentry, and ReconcileLoggedOutFeedAsync is skipped. Same for Settings ClearCache and the endpoint toggle.ContextInvalidatedException at the coordinator/orchestrator boundary, or run the theme participant before NotifySignedOut; drop the private epoch if the generation suffices.F64 (medium, 3/3; medium x3). P5, PR 2513. Logout awaits push re-registration POST inside the revocation window.
LogoutParticipants.cs:134-142, Authentication.Session.cs:35-53,158-241,374-381,414-431, AuthSessionEffects.cs:66-91, NotificationsApiClient.cs:54-63, ProfilePage.razor.cs:209-222, SettingsPage.razor.cs:181,277._ = Task.Run(registerPushToken);. PushLogoutParticipant (AfterCredentialClear) now returns push.RegisterAsync(), an HTTP POST with a 30s TotalTimeout, awaited inside CompleteLogoutAsync while _logoutGate / _publicationGate are held and _tokenRevocations != 0. In that window BeginSignInAttempt returns (-1,-1), IsAttemptCurrent is false and token reads return null. Profile, Settings and the session-expired dialog all await the same task. Undocumented.CompleteJwtSignInAsync rejects; LoginPageViewModel silently returns. The Session Expired dialog is delayed by the same interval.LogoutParticipants.cs:141 public Task LogOutAsync(SignOutRequest request) => push.RegisterAsync(); then Interlocked.Decrement(ref _tokenRevocations) at :380.LogOutAsync or BeginSignInAttempt.NotificationsService swallows non-cancellation errors); the stall needs a slow network.F65 (medium, 3/3; medium x3). P5, PR 2513. Startup second logout cancels the Session Expired / Account Locked dialog.
Authentication.Session.cs:267-285,411-431, Authentication.cs:367,600-609,644-645, App.xaml.cs:97-132.SilentLoginAsync calls QueueRejectedSessionRecovery, which runs StartLogout synchronously (epoch E1) and shows the dialog only if Epoch == logout.Epoch && !IsLoggedIn. GetJWTAuthTokenCore returns null without its own dialog because the attempt is no longer current. App.ValidateLoginStateAsync then sees !IsLoggedIn and calls _session.LogOutAsync, bumping the epoch to E2, so the queued dialog exits. The MissingCredentials path never shows a dialog. The App.xaml.cs comment (lines 110-113) still claims the earlier call shows it. At phase 4 both dialogs were shown unconditionally.App.xaml.cs:119 bool sessionRejected = !Authentication.IsLoggedIn; then :132 await _session.LogOutAsync(...); Session.cs:423 if (Epoch != logout.Epoch || IsLoggedIn) return;.MissingCredentials; update the comment.F68 (low, UNVERIFIED). P5. AuthSessionEffects.cs:37-44, FTDTDataSyncHelper.cs:124-140. StartCurrent calls effect() inside TryExecute, so the synchronous prefix of push registration / FTDT SyncAllAsync (LiteDB read, DI resolution, token acquisition) runs under the global coordinator lock, against the stated "never run work inside the coordinator" invariant. The verifier for this finding failed to return (infrastructure error), so it has not been independently checked. Fix: check under the lock, start the effect outside via Task.Run / Task.Yield.
F69 (medium, 3/3; medium x3). P5, PR 2513. No test builds the real auth DI graph (P5-A1).
HeadServiceCollectionExtensions.cs:157-201, UnitTest/Phase3/4/6ServiceCompositionTests.cs, SERVICE-LAYER-IMPLEMENTATION.html:234.ISessionState, ICredentialStore, IAuthSession and IAuthTokenProvider with fakes after AddHeadServices. The ActivatorUtilities + SecureCredentialStore workaround for the ICredentialStore alias cycle, alias identity, Func<> deferral and ten LogoutParticipantRegistration rows are never resolved. Plan P5-A1 names "DI identity/graph tests" as evidence; the implementation doc cites "DI identity aliases" but no such test exists.Authentication constructor or the ICredentialStore alias causes a startup circular-dependency exception on device with green unit tests.SameAs across aliases and the participant table; comment the cycle.x.di probe on the tip resolved the whole graph without a cycle and alias identity held, so this is a guard gap, not a live defect.F73 (medium, 3/3; medium x3). P6, PR 2514. FTDT import maps the whole page unguarded.
FtdtApiClientAdapter.cs:40, FTDTDutyPeriodMapper.cs:477, FTDTDataSyncHelper.cs:112-133,157-171.result.AddRange(records.Select(FtdtRawRepresentationFactory.CreateFromRaw)) runs eagerly and unguarded; the mapper's default branch throws InvalidOperationException("Unknown DPOperationType: ..."). ImportDutyPeriodsAsync catches, returns false, and SyncAllAsync skips advancing FTDTSyncTimeStamp.OperationType: legacy imported 9 of 10; now none import, and every sync refetches the same page and fails on the same record. Earlier records on the page are lost too.F01 (low; filed high; votes low / low / medium -> low). P0, PR 2505. DataManager.cs:393 (23 sites), MenuService.cs:269, FavoritesService.cs:121. Phase 0 adds if (ex is OperationCanceledException) throw; after EnsureCacheContext(generation). None of those methods take a caller token, so the only OCEs reaching them are HttpClient timeouts (TaskCanceledException), Android TCP aborts (AB#2494) and ContextInvalidatedException; the base fell through to cached JSON. Failure at P0 through P6: poor network exhausts retries with TaskCanceledException; GetMemberAsync throws (contradicting AB#2396), the tab bar is not primed and Jumpseat/KCM/MEC/notification screens fail instead of showing cache. Only ContextInvalidatedException needed the rethrow, and EnsureCacheContext already throws for it. No test seeds a cache and throws a timeout. Fix: rethrow only ContextInvalidatedException or OCE with a cancelled supplied token. Verifiers: real at P0; at the tip DataManager is deleted and RequestProvider + LegacyCatalogFailure turn timeouts into TimeoutException / HttpRequestException, so CachedRemoteQuery falls back. Two verifiers say the tip MenuService:252 / FavoritesService rethrow only sees real caller cancellation; one says a refresh=true timeout inside the SDK path can still skip fallback (medium). Majority: low at tip, high in P0 to P6 intermediate states. Do not ship PR 2505 alone.
F02 (low; filed medium; votes low / low / refuted -> low; 2/3). P0, PR 2505. Authentication.cs:932-1000. ClearSessionCacheContextAsync now runs first; a ClearCaches() (LiteDB DeleteAll) throw exits before the sign-out flags flip; a _tokenStore.ClearAsync throw skips cookies, image cache, theme/site memos, FavoriteToggleContext.Invalidate and AppProperties.Clear. Needs a corrupt/locked LiteDB or keystore fault. Fixed by P5 StartLogout / CompleteLogoutAsync (flags flip first, every step in its own try/catch). Third verifier refuted (nit): legacy also aborted on a bare ClearCache() throw.
F03 (low; filed medium; votes low / refuted / low; 2/3). P0. CacheContextCoordinator holds one monitor for Generation, TryExecute and Invalidate; ClearDatabase, three BulkInsert*, DocumentsVersion++, synchronous WeakReferenceMessenger.Send and File.WriteAllBytes run inside it (still true for DocumentCatalogHydrator at the tip). Holding the lock across the writes is plan-mandated (plan lines 711, 739: cache write atomic with ClearCache); subscribers are fire-and-forget so the deadlock scenario is unsupported; no on-device measurement. Genuine residue: Generation could be a Volatile.Read. Fix: make Generation lock-free; keep the writes under the lock.
F04 (low; filed medium; votes low / low / refuted; 2/3). P0. Authentication.cs:936-975, 356-372, 1306-1314. Refresh and silent login apply results with no epoch/generation check; the final Invalidate does not repeat the bearer/authToken clear. Pre-existing race (base had no guard) and narrower than filed; fixed by P5 AuthSessionAttempt(Epoch, Generation) / IsAttemptCurrent.
F06 (low, 1/1). P0. DynamicFeedQueries.cs:67-90,139-176, AlpaIHomePage.razor.cs:321-331, Main.razor.cs:130-146. GetPageAsync/GetSiteAsync throw ContextInvalidatedException on generation change; callers catch (Exception), ex.Report(), set _pageNotFound. Sign-out during a feed fetch logs a benign error; the UI impact is mostly masked by the logged-out branch. Fix: catch ContextInvalidatedException / OCE before the generic catch.
F07 (low; filed medium; votes medium / low / low -> low). P0. MenuService.cs:287,440,466, FavoritesService.cs:231,282, TopNav.razor.cs:65,79. CommitContext throws ContextInvalidatedException on a menu request-version change (base returned fetched data uncommitted, AB#2380); the test was inverted. Reversal is deliberate (plan line 426, Phase 0 inventory line 99: "return an explicit context-invalidated outcome instead of delivering the old member's result"). Residue: FavoritesService.AddFavoriteAsync / RemoveFavoriteByIdAsync throw after the server call already succeeded, and TopNav.OnInitializedAsync awaits an unguarded menu load. Sentry claim overstated (Report() only logs). Fix: do not throw after a successful mutation; treat the exception as expected in AppShellViewModel / TopNav.
F10 (nit; filed low). P0. Late-response-after-logout tests cover notifications, documents, menu, favorites and site; the other ~20 paths are untested. All 35 CommitCacheContext sites are wrapped today, and the classes are replaced by CacheContextSafetyTests at the tip. Missing: a rule against raw SetCache. Fix: parameterized late-response test plus a source rule.
F12 (low; filed medium; votes low / medium / low -> low). P1, PR 2509. MauiProgram.cs:247-296, InfrastructureServiceCollectionExtensions.cs:67-68, RequestProvider.cs:102-120. Legacy DefaultHttpClient sets TimeoutIntervalForRequest=30 / Resource=60 (Fix 1988: HttpClient.Timeout ignored by NSUrlSessionHandler, 209s in Sentry). The new client uses bare DefaultSessionConfiguration, infinite HttpClient.Timeout, and only a linked CTS CancelAfter(30s) with no WaitAsync guard. Whether NSUrlSessionHandler honours the token is unverified (the repo's own view-model comment treats managed cancellation against it as functional; default request timeout of 60s still bounds an idle stall). Failure if the token is ignored: a degraded gateway outlives the 30s deadline across the whole migrated API surface. Fix: set the same two timeouts on the service-layer session configuration; verify on device. Not fixed at tip.
F14 (low, 1/1). P1. HeadServiceCollectionExtensions.cs:206. ValidateOnStart() never runs because MauiApp.Build() uses a plain provider and nothing calls IStartupValidator; validation is lazy on the first IOptionsMonitor.CurrentValue read, and ToggleEndpointEnvironment sets DevEnv before ApplyEndpoints validates. Impossible with today's hard-coded values. Fix: read CurrentValue after Build, or drop the fail-fast claim.
F15 (low, 1/1). P1. AuthenticatedRequestProvider.cs:115,161, Authentication.TokenProvider.cs:14. ForceRefreshAsync returns true for any non-empty token, including the unchanged current token on offline/exception paths; ProduceRefreshAsync caches (generation, T1) -> true, blocking later real refreshes until the token changes. Fix: require the re-read token to differ from key.Token.
F16 (low, 1/1). P1. CachedRemoteQuery.cs:154-185. GetAsync(refresh:true) joins any in-flight operation for the key regardless of start time and commits its result; concrete instance: flight notifications after an audience change. Fix: start a fresh operation when the existing one began earlier, or add Invalidate(key).
F18 (low; filed medium; votes low / refuted / low; 2/3). P1. CachedRemoteQuery.cs:203-215. First catch stores any OCE without checking operation.Token, so the transient-classifier catch is unreachable for OCE. In production every fetch goes through RequestProvider, which converts timeouts to Result failures (Timeout / TransportError, both fallback-eligible), so only an OCE from the token step (GetValidTokenAsync) can escape. Test ThrownCancellation_NeverBecomesTransportFallback pins the strict contract. Fix: filter with when (operation.Token.IsCancellationRequested || generation stale).
F20 (low, 1/1). cross. TRX files are git-ignored (test-logs/), zero .trx are tracked, the tested SHA is "recorded in the phase PR", phases 4, 5, 7 lack SHAs in the doc, and the phase 7 TRX finished at 07:05:39, before tip commit 6633bb59 (07:07:30). Counts match on-disk TRX. Note: section 5 reproduces 2,607 / 2,601 at the tip, which closes this for the tip. Fix: attach TRX summaries with tested SHA to each PR.
F21 (low, 1/1). P1. Authentication_TokenProviderTests.cs:64-71. The test sets IsLoggedIn false inside Invalidate, and the code also requires IsLoggedIn, so removing the generation guard still yields null. Fix: keep IsLoggedIn true across the invalidation.
F22 (low, 1/1). P1. AuthenticatedRequestProvider.cs:96-103, RequestProvider.cs:117-119. Default Bearer = Optional attaches the member JWT to any absolute URI; no host check against ApiEndpointOptions. All current callers use configured hosts. Fix: attach only for configured hosts, or default to None.
F23 (low, 1/1). P1. Stale refs to RestService.SetEndpoints() / KcmLegacyGatewayUrl: AGENTS.md:74, docs/detail/GATEWAY-QA-READINESS-CHECK.md:18,27, docs/mobile-content-api/index.html:167,172, tools/fault-injection/mitmproxy_dispose_repro.py:21. AGENTS.md:72 and ImageCachePolicy.cs:44 were fixed later.
F24 (low, 1/1). P1. RequestProvider.cs:135-136,171. Retry exhaustion and JSON failure no longer log (base logged both at error level); only retry warnings remain. Callers such as FlightSearchApiClient, JumpseatApiClient, NotificationsApiClient show no logging on those paths. Fix: log the final failure without the query string.
F27 (low, 1/1). P2. CachedRemoteQuery.cs:186-207, MecService.cs:78-85, MyPilotGroupPageViewModel.cs:93-110,286-350. ProduceAsync resumes on a pool thread (ConfigureAwait(false)) and sends EventsReadyMessage inside the coordinator lock; the handler enumerates Groups and sets list-item properties with no main-thread marshalling. Legacy sent from a finally on the caller's context. Failure: race with SetGroupedItems; exception swallowed by ex.Report(). Fix: send after releasing the lock via a main-thread dispatcher, or marshal in the handler.
F28 (nit; filed low). P2. MecWireModels.cs:96-97. decimal? latitude/longitude have no numeric-leniency converter; Newtonsoft mapped "" to null, STJ throws JsonException. No fixture or capture shows the gateway ever sends "". Fix: lenient nullable-number converter, or confirm with a gateway capture.
F29 (low, 1/1). P2. MecService.cs:62-84, CachedRemoteQuery.cs:52,167-183. MecService captures Generation and closes delegates over it; GetAsync captures again. A logout+login landing between the two captures installs the operation at the new generation with an old-generation delegate, so a current-session joiner gets ContextInvalidatedException. Window is a few statements; self-heals on the next call. Fix: pass the query's captured generation into the delegates.
F30 (nit; filed low). P2. Missing: DataManager forwarding test (moot at P7), MyPilotGroupPageViewModel EventsReadyMessage subscriber test (real gap), end-to-end MecService corrupt-row test (decoders are tested individually). ClearInMemoryCache no longer exists at the tip.
F31 (low, 1/1). P2-3. Inline route and scope strings in MecApiClient.cs:25-39, MecService.cs:49, NotificationsApiClient.cs:31,58, SubscriptionsApiClient.cs:18,36, ContentApiClient.cs:41-88; one route already lives in Constants (FlightNotificationListPath). Legacy RestService also inlined routes. Violates the "no magic strings" rule in CLAUDE.md. Fix: shared route constants; ask before changing the Phase 2 idiom.
F33 (low; filed medium; votes medium / low / low -> low). P3. CacheContentService.cs:198-204,321-341,639, App.xaml.cs:264-300. SuspendWaves sets _backgrounded; only RetryIncompleteWavesIfNeededAsync clears it (after a 1500ms delay and token refresh). The "refresh throws, latch never clears" path is effectively unreachable (GetJWTAuthTokenCore swallows exceptions). Reachable: on resume with an expired token, the rejected refresh raises IsAuthenticatedChanged(false) while the latch is set; the handler returns early, so FetchContentUnAuthorized() (guest banner refresh) is skipped and Retry does not replay it. Search index notifications still fire via CacheContentAsync. Fix: clear the latch at the start of Resumed or in finally; run the guest banner refresh on backgrounded sign-out.
F34 (low, 1/1). P3. CacheContentService.cs:263-285,425-431,648-655,698-704. Legacy skipped triggers while a wave ran; now waiters serialize on _waveGate and re-check only after a successful wave, so N queued handlers each replay a full failed wave. The orphaned connectivity CTS existed in legacy too. Fix: coalesce waiters onto the in-flight wave task or add a cooldown.
F35 (low, 1/1). P3. ContentApiClient.cs:108-116, ContentService.cs:170-172. Failures reported as new InvalidOperationException($"{operation}: {error.Message}") via ICrashReporter, bypassing ex.Report(): no ALPA Event_GeneralError, Sentry grouping changes, dedupe key bypassed. Other new services still use ex.Report(). Fix: report through a path that also writes the ALPA event.
F36 (low; filed medium; votes medium / low / low -> low). P3. CacheContentService.cs:197,217-218,371-376. StartLogout invalidates the generation before NotifySignedOut raises IsAuthenticatedChanged(false), so SynchronizeStateGeneration() zeroes the flags first and the if (prefetchingAuth || caching && ...) authCancellationSource?.Cancel() guard is never true; the outgoing wave keeps dispatching steps and holds _waveGate. Impact is smaller than filed: post-logout requests fail fast with auth-required (token already cleared), commits are generation-gated, the only image prefetch source (kcm-map) has ClearOnSignOut: false. No logout-during-wave test. Fix: capture pre-reset flags or cancel sources before synchronizing; check generation between steps.
F37 (low, 1/1). P3. CacheContentService.cs:46,52,378-390, SearchViewModel.cs:85,106. IsSearchIndexReady / IsFetchInProgress now take the coordinator lock, so a render overlapping Invalidate waits for the cache wipe. Brief hitch, no deadlock. Fix: volatile fields or an immutable snapshot.
F40 (low, 1/1). P3. LegacyCatalogFailure.cs:21-26. Default branch is new InvalidOperationException(error.Message) with no operation and no stack, then Report(); the caller logged is "Create". KCM airports and subscriptions both returning 500 collapse into one identical event. Fix: $"[{operation}] {error.Message}" as ContentService does.
F41 (low, 1/1). P3. MemberApiClient.cs:59-70. GetAsync<JsonElement> throws on a bare body (A12345, 00123456); legacy stripped quotes from raw text. Only reachable if the server sends unquoted text; tests cover "A12345" and 12345 only. Fix: read as raw text.
F42 (low, 1/1). P3. JumpseatApiClient.cs:42. JToken.Parse uses the default DateParseHandling.DateTime; an ISO-like string field becomes culture-formatted text (FlightSearchApiClient sets DateParseHandling.None). Fix: JsonTextReader with DateParseHandling.None.
F44 (low, 1/1). P3. NotificationsService.cs:30-38. The fetch closure captures per-call selected audiences but the operation is keyed by one registry key; a refresh joining an in-flight fetch returns and persists the first caller's audience set. Self-heals on the next fetch. Fix: include the audience hash in operation identity.
F45 (low; filed medium; votes low x3 -> low). P3. Phase3ServiceCompositionTests.cs:17-38. No test resolves any *QueriesRouter; plan row P3-A1 "scaffold routers retain source policy" cites per-slice DI tests. Typed router constructors and the global-port Check<> assertions partly guard it. Fix: resolve each router and assert live/mock delegates under the flag.
F46 (low, 1/1). P3. ContentPrefetchCatalog.cs:7-25. Seven key-to-query mappings untested; orchestration tests use a fake catalog; composition test does not resolve CacheContentService. All seven are correct today.
F47 (low, 1/1). P3. CacheContentService_BackgroundingTests.cs:105-106 loosened to Does.Contain("!prefetchedAuth") / "!cached" (source-text scans); ApiEndpointOptionsTests no longer checks the /v2 toggle for Notifications, Subscriptions, Jumpseat. The clients read CurrentValue per request today. Fix: behavioural tests.
F50 (low; filed medium; votes medium / low / low -> low). P4, PR 2512. DocumentsApiClient.cs:13-38, AuthenticatedRequestProvider.cs:106, RequestProvider.cs:105, DocumentCacheService.cs:64-70, UnitTest/DocumentsApiClientTests.cs:193. Legacy DownloadDocumentItem made up to three attempts (5s retry, then 5s + SilentLoginWithRetryAsync + retry). The new client POSTs once (MaxRetries = 2 is inert for POST; 401 replay is GET-only); prefetch loses retry too; user Retry calls GetValidTokenAsync(force:false), which reuses a token the gateway rejected but the client still considers valid. Test DownloadFailure_IsNeverReplayedOrUsedToForceRefresh pins it. /api/doc/downloadfile is read-only, so the "no write replay" rationale does not apply. Two verifiers lowered severity because it is documented as a deliberate refinement (implementation doc ~line 184; plan lines 797 and 1126) and only bites on revocation/clock skew or a transient 5xx. Recommendation stands: add a bounded read-POST retry plus one forced-refresh replay for downloadfile, or record it as an accepted regression.
F51 (low, 1/1). P4. DocumentCacheService.cs:35-40,60,99-109. File.ReadAllBytes and the catalogue-versus-disk scan (GetDocuments, File.Exists each, GetCategories) now run inside TryExecute; legacy held the lock only around WriteAllBytes. Milliseconds to tens of ms. Fix: do reads outside the lock with Ensure(generation).
F52 (nit). P4. DocumentCacheCodecs.cs:19-21, DocumentCatalogService.cs:71-75,121. ProduceAsync persists "null" and the codec fails it as Corrupt, so each cold start logs a warning and deletes the row. Only for an account whose catalogue is null. Fix: do not persist null.
F55 (low, 1/1). P4. Phase4ServiceCompositionTests.cs:22-40. After AddHeadServices() the test re-registers IDocumentPrefetchConfiguration, ISettingsService, IOptionsMonitor<PrefetchTagOptions>, so the head aliases and PrefetchTagOptionsConfigurator are never resolved. Fix: fake only platform leaves.
F58 (low, 1/1). P5. AuthRestClient.cs:40, Authentication.cs:647-648. Legacy Polly turned every >= 500 into 503 (silent branch); the new client delivers real 500/502/504, which reaches ShowAuthenticationErrorMessage during a silent background refresh. AuthUiMessageGate limits it to one dialog at a time. Fix: map to service-unavailable or suppress the dialog on silent refresh.
F60 (low, 1/1). P5. SecureTokenStore.cs:115,159-167, Authentication.Session.cs:177-213,343-380. ClearAsync waits on an unbounded SemaphoreSlim; if a keystore write hangs, CompleteLogoutAsync never decrements _tokenRevocations, so BeginSignInAttempt returns (-1,-1) until restart. Rare and hypothetical. Fix: bound the wait.
F61 (low, 1/1). P5. FTDTDutyPeriodService.cs:53-56,93-98. The finally resets IsSyncing only through TryExecute(generation), skipped after a context change; the wait loop has no timeout. The same DutyPeriod instance must survive logout and re-sign-in. Fix: reset IsSyncing unconditionally.
F66 (low, 1/1). P5. FTDTDataSyncHelper.cs:311-330. Import self-heal saves the SyncFDPAsync response wholesale, dropping the AB#2219 null-skipping merge that legacy SaveAndSyncAsync had. Narrow: freshly imported record; Results is the only locally computed field at risk. Fix: reuse the PushAsync merge.
F67 (low, 1/1). P5. LoginPageViewModel.cs:287,351-352. if (!await authentication.CompleteJwtSignInAsync(...)) return; gives the user no message, and an earlier success analytics event has already fired; sibling view models show "This sign-in attempt is no longer active." No unit tests for LoginPageViewModel.
F70 (low; filed medium; votes low x3). P5. LegacyAuthBearerBridge.cs:4-9. P5-A5 "bridge regression tests" has no test (only the AuthCoreBearer recording fake); the doc table lists A1 to A4 only and remaps A3 (FTDT guards) and A4 (build counts). The bridge was deleted in P6, so the mis-wiring scenario cannot occur; the doc gap remains at the tip.
F71 (low, 1/1). P5. Unplanned refactors in an "auth split": ThemeCoordinator / ThemeStore rewrite and IThemeStore.SaveAsync(ThemeResponse, long), ~490-line FTDTDataSyncHelper AsyncLocal generation change, SecureTokenStore gating, ExceptionReportedDataKey alias, larger port surface. The FTDT and theme generation changes are documented; not documented: the _tracer.StartTransaction("AuthenticateUser", ...) login-latency Sentry span was silently dropped (AuthRestClient has no tracer). Fix: restore the span or document.
F72 (low, 1/1). P5. The ratchet tracks only IRestService, DataManager, IRestEndpointProvider (HeadConsumerRatchetTests.cs:192-194); the plan's "IAuthentication referenced only by ..." exit has no enforcing test. At the tip it holds in code; [Obsolete] is an error in strict layers (EnableStrictWarnings) but only a warning in the head project and tests. The cited line 505 does not exist at the tip (the only reference is line 200). Fix: add IAuthentication to the ratchet.
F74 (low, 1/1). P6. MobileMenuApiClient.cs:23-105, RequestProvider.cs:127. RestService opened Sentry transactions for GetMenuItemsAsync / GetFavoriteItemTypesAsync / GetFavoritesAsync and RetryTransientAsync showed the "Network issue detected. Retrying..." snackbar plus an Event_GeneralDebug log; now only an ILogger warning without operation name. Not documented. Verified for menu and favorites only. Fix: retry observer plus ITracer spans, or document the removal.
F75 (low, 1/1). P6. MobileMenuApiClient.cs:45,57,69,93,101,115,140. Every adapter catch calls Report(error) unfiltered, including ContextInvalidatedException and cancellation, which stamps the exception so outer layers cannot filter. MemberApiClient already has catch (OperationCanceledException) { throw; }. Fix: add that catch before the general one.
F76 (low, 1/1). P6. DynamicContentApiClient.cs:53-62, LogApiClient.cs:19-24, AuthenticatedRequestProvider.cs:96-102. Optional-bearer clients now await GetValidTokenAsync inside their deadline (DynamicContent 15s), which can run a full refresh and raise session-recovery dialogs from background work (log flush). Bounded and transient. Fix: peek the current token without refresh, or use None for /api/log.
F79 (low, 1/1). P6. TransientNetworkErrorClassifierTests.cs:56 asserts LegacyRestNetworkErrorClassifier.IsTransient, which has no production caller; the real gate is the catch filter in RequestProvider.cs:130-132. Fix: drive the table through RequestProvider or delete the helper.
F80 (low, 1/1). P6. Plan P6-A2 ("FTDT generated types do not leak") has no architecture rule; LayerDependencyRulesTests has 10 rules, none for the generated namespaces; the package reference has no PrivateAssets. Code is clean today. P6-A4 native viewer walkthrough is pending. Fix: architecture test restricting generated namespaces to Infrastructure.
F81 (nit). P6. ApiEndpointOptionsTests.cs:143 asserts on a snapshot captured before the failing apply; cannot fail. The other two assertions are meaningful.
F82 (nit). P6-7. 22 "DataManager is null" / "not available" strings in non-test code (e.g. AppDelegate.cs:381), class DataManagerAirportLookup (HeadServiceCollectionExtensions.cs:246), UnitTest/DataManager_* file names and ~65 comments still name deleted types. Sentry messages point at a nonexistent class. Fix: reword to name the null port; rename the class and tests.
F83 (nit). P7. JumpseatResultsPage.razor.cs:136-150. Comments say "the jumpseat service preserves the AB#1883 rethrow contract"; the code is FlightSearchService.SearchFlightsAsync. Behaviour matches legacy.
F84 (low, 1/1). P7. SignInContextReset.cs:28-37, Authentication.Session.cs:143-152,266-269, Authentication.cs:274-292,864-876. SignInContextReset throws AggregateException on cache/LiteDB cleanup failure; PublishJwtAsync and the new ApplyAcquiredTokenAsync catch route it to HandleSecureStorageFailureAsync, which logs "SecureStorage failure" and starts a SessionExpired logout with ClearStoredCredentials: true. At P6 the MSAL path just reported and returned false. The wipe-on-storage-failure decision is a recorded owner ruling (AB#2220), but it covers store failures, not cache cleanup. Fix: revoke-only logout with the correct label for cleanup failures.
F85 (low, 1/1). P7. Authentication.Session.cs:239-260, SignInContextReset.cs:16-40. SetAuthTokenAsync / SetUserInfoJsonAsync for the incoming member run before PublishContextAsync waits on MutationGate and purges. A kill in that window hydrates B on relaunch with A's rows on disk, and same-member NeedsContextPublication skips the purge. Requires explicit sign-in over a still-signed-in member. Base cleared only on logout, so not a regression. Fix: write a durable purge-pending marker first.
F86 (low, 1/1). P7. AppProperties.cs:430, FlightSearchPushNotificationService.cs:245, PushNotificationHandler.cs:47 read _tokenStore.UserInfoJson, which SecureTokenStore.WriteAsync updates before guarded publication, while CurrentUser still returns the outgoing member; memberJson in PushNotificationHandler is an unused read. Fix: read via ISessionState.CurrentUser.
F87 (low, 1/1). P7. Authentication.Session.cs:48,74-85,171. BeginSignInAttempt sets _explicitSignInEpoch; _publishedSignInEpoch advances only on publication. A rejected explicit login while a session is retained leaves the epochs unequal, so the next silent refresh takes the context-publication path and wipes the API cache and document DB unnecessarily. No test. Fix: clear the explicit epoch on failure.
F88 (low; filed medium; votes low x3). P7. Authentication_PublicationTests.cs:134-170, AuthCoreTestPorts.cs:34-46. AuthCoreEffects.PublishContext re-implements the SignInContextReset barrier; the real class is tested only in isolation (SignInContextResetTests); nothing resolves IAuthSessionEffects from a container. DI registrations are statically sound and the phase 5 exit requires live verification, not a resolution test. Fix: composition test plus an Authentication test over the real reset.
F89 (low, 1/1). P7. SignInContextReset.cs:29-38. NeedsContextPublication fires on a changed UserName/MEC/LEC/UserType, but the reset clears only ICacheStore and the document DB. Feed and theme state are invalidated by the generation bump; favorites hearts (FavoriteToggleContext) and preferences run only at logout. Same as old DataManager, so not a regression.
F90 (low, 1/1). P5. HeadServiceCollectionExtensions.cs:170-199, LogoutParticipants.cs:13-28. The runner orders by LogoutParticipantRegistration.Order/Phase and never reads ILogoutParticipant.Phase/Order; both are declared. Values agree today. Fix: single source of truth or a test asserting agreement.
F91 (low, pre-existing). SharedActionsService.cs:46-48. GetService<ALPALogService>() returns null because only IALPALogService is registered (ApplicationServiceCollectionExtensions.cs:31), so Event_DocumentOpened is never logged. Same code at dca3123d. Flight.cs:1866,1927 have the same defect. Fix: resolve IALPALogService.
F92 (low, 1/1). P5. Authentication.Session.cs:231-237,264. SetUserNameAsync then IsAttemptCurrent then SetPasswordAsync; early returns skip any rollback (the only rollback is _tokenStore.ClearAsync), so a superseded attempt can leave B's username beside A's password, and the next silent login is rejected. Narrow; logout normally clears credentials afterward. Fix: write the pair as one operation.
F93 (low, pre-existing, 1/1). FlightSearchPushNotificationService.cs:273 logs the serialized Installation (UserId = user name, PushChannel = device token); MauiProgram.cs:525, FirebaseService.cs:33, AppDelegate.cs:308,312 log raw tokens. SERVICE-LAYER-IMPLEMENTATION.html:134 claims raw tokens are no longer logged; false. Identical at base. Fix: redact the lines or correct the doc.
F94 (low, 1/1). P0. SourceConsumerScanner.cs:57-70. Aliases register only on an exact target-name match, so global::-qualified or nested-type aliases used only in platform-conditional code evade the ratchet (confirmed by mutation). All five targets are deleted at the tip; the IL scanner is a second check.
F95 (low, 1/1). P1, P5. HeadConsumerRatchetTests.cs:56-64 checks only that JSON equals the scan. Phase 1 added 2 active entries (OptionsRestEndpointProvider), phase 5 added 2 (LegacyAuthBearerBridge); active counts p0..p7 = 54, 53, 51, 48, 48, 47, 6, 0. Both shims are migrated at the tip; the test was not hardened. Fix: pin the Phase 0 key set or assert only migrated transitions.
F96 (low; filed medium; votes low / low / medium -> low). cross. Plan row 1823 promises "raw-boundary checks" and criterion 1947 says "no service deserializes external JSON directly into domain models"; UnitTest/Architecture has no such test (e.g. AnonymousTabBarSeedProvider.cs:58 deserializes into the domain type). Not a per-phase acceptance gate. Fix: IL test forbidding generic Deserialize over Domain types in Infrastructure/Remote, or descope in the plan.
F97 (low, 1/1). cross. SERVICE-LAYER-PHASE-0-INVENTORY.html:80,82 cite DataManager_CacheContextTests and DataManager_AuthFallbackPolicyTests, both deleted; the plan's P7-A3 "test inventory comparison" was not produced; SERVICE-LAYER-IMPLEMENTATION.html:318 says only "rehomed". x.ratchet found 38 base test names gone at the tip, all rehomed or deliberately changed. Fix: check in a removed-to-rehomed mapping.
F98 (low, 1/1). P6. scripts/gateway-readiness-check.py:51,1098. --catalog-check scans RestService.cs only behind if RESTSERVICE_PATH.exists() with no else, so it now skips silently and prints gaps: 0; 64 endpoints.json rows point at the deleted file. Catalog is complete today. Fix: scan Infrastructure/Remote and Constants.cs; fail loudly on missing targets.
F99 (low, 1/1). P6-7. About 53 docs still name removed classes, e.g. docs/FTDT-DATA-MIGRATION-PLAN.md:149,153,154 ("bump ftdtClientVersion in RestService.cs"; the real switch is Version at FtdtApiClientAdapter.cs:19), GATEWAY-QA-READINESS-CHECK.md:18,41, tools/fault-injection/README.md:92. No historical banner. Fix: rewrite or banner.
F100 (nit). P7. 13 component specs still show DataManager (e.g. pilot-card-property-mapping.html:130 ctor sample). CLAUDE.md names them as the UI baseline. Fix: add a "DataManager retired; use IDocumentsQueries" note and stamp Updated.
F101 (low, 1/1). P5-7. [Obsolete] IAuthentication (IAuthentication.cs:55) has one non-test consumer (the DI alias at HeadServiceCollectionExtensions.cs:200); RunLogOutTaskAsync, ClearSessionCacheContextAsync and LegacyRestNetworkErrorClassifier.IsTransient are called only from tests; ~60 test files depend on the composite via FakeAuthentication. Doc says the composite stays "while generated-client bridges are retired in Phase 6"; P6 did not remove it. Fix: record a removal milestone.
F102 (low, 1/1). P6. iOS NSURLError codes duplicated: LegacyRestNetworkErrorClassifier.cs:32-36 (-1005, -1009, -1001, -1200), Authentication.cs:694 (same four), MauiProgram.cs:175 (three, no -1200; may differ deliberately). Authentication's copy pre-dates the stack. Fix: call IsTransientIosErrorCode from the first two.
F103 (nit; filed low). P2-3. JumpseatApiClient.cs:27,37. GetKcmAirportsAsync sends the member JWT (Bearer = Optional) to KcmLegacyGatewayUrl (gateway.alpa.org), an ALPA-owned host not switched by the QA/prod toggle. Base sent the same header via the shared client; the host choice is the documented AB#2717 workaround. Fix: BearerPolicy.None until AB#2717 is retired.
F104 (low, 1/1). P1, P6. SettingsPage.razor.cs:153-185. ToggleEndpointEnvironment sets DevEnv and calls ApplyEndpoints() (only cancels the change token), awaits two credential reads, then LogOutAsync. A request started in that window goes to the new host with the old environment's JWT. Diagnostic-Mode only; legacy had the same window; both hosts are ALPA-owned. Fix: revoke first.
F105 (nit; filed low). P3. MemberApiClient.cs:33-37,51-55. Newtonsoft conversion errors embed offending values and go to CaptureException and error.Message. MemberRaw.birthDt is string?, so the birth-date example in the filing does not apply; only typed PAC fields (amount, dates) can leak, and only if the backend breaks its contract. Pre-existing. Fix: value-free wrapper exception.
F106 (low, 1/1). P4. LogoutParticipants.cs:53-58, DocumentCacheService.cs:77,170. DocumentLogoutParticipant clears LiteDB rows only; PDFs written with File.WriteAllBytes to Path.Combine(CacheDirectory, document.FileName) remain after sign-out, and revoked documents are never purged. Legacy behaved the same. Next member sees a leftover file only for a FileID they are already entitled to. Fix: logout participant deleting downloaded files.
F108 (low; filed medium; votes low x3). P3. Plan line 333 ("protected-data auth.required never becomes a stale fallback") versus line 740 (auth fallback via a separate flag for "banners, Jumpseat policies, subscriptions and flight notifications"); the Phase 0 matrix (SERVICE-LAYER-PHASE-0-INVENTORY.html:111) omits subscriptions and airports, though SubscriptionsService.cs:17 and FlightSearchService.cs:19 set AllowAuthorizationFallback: true. Legacy did fall back; behaviour is preserved. Fix: add both to the matrix and reconcile the two plan lines.
F109 (low; filed medium; votes low x3). P5. The implementation doc reuses P5-A3 (FTDT guards) and P5-A4 (build counts) with different meanings than plan rows 1616-1640, omits P5-A5 and the account-locked walkthrough, and folds logout ordering into A2. The same row says "device login/logout/offline walkthrough remain pending" and the doc states "No device acceptance is claimed", so the device gap is disclosed. Fix: rewrite the table with plan IDs and Pass / Not run / Superseded.
F112 (low, 1/1). P1. SERVICE-LAYER-IMPLEMENTATION.html:28 says "Only GET requests retry automatically ... DELETE send once"; RequestProvider.cs:105-106 retries DELETE when RetryIdempotentDelete is set (MobileMenuApiClient.cs:25), and doc lines 123 and 278 mention read-POST and DELETE retries. Fix: append the exceptions.
F117 (low, 1/1). cross. origin/imp/blazor-hybrid was 12 commits ahead of dca3123d at 730daccc (24 files; overlap in DocumentCachePage.razor, JumpseatResultsPage.razor, JumpseatResultsPage.razor.cs; merge-tree clean). It is now at 4774e079, 24 commits ahead, and a merge into the tip conflicts in ALPAMobile/HeadServiceCollectionExtensions.cs and ALPAMobile/Services/Authentication.cs. All recorded test results predate those commits. Fix: sync, resolve at hunk level, rerun suite and both native builds at the merged SHA.
| ID | Title | Why refuted |
|---|---|---|
| F05 | SetCache / SetJson capture Generation at write time |
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. |
| F08 | Plan marks raw-DTO mapping seam as done | Plan checkboxes are all unchecked; inventory documents the prose contract and defers other families to later phases; codecs exist at the tip. |
| F09 | Document-list processed flag and 500ms poll removed | Failure scenario unreachable: non-refresh reads return the memo or persisted cache; the old flag was never reset and was the latent bug. |
| F11 | DataManager falls back to new MauiConnectivityService() |
No new DataManager exists; DI registers IConnectivityService; fallback never taken and deleted with DataManager in P7. |
| F17 | Session identity keyed only by logout-advanced generation at P1 | 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. |
| F19 | P1-A2 raw seam has no consumer and no LockedAccount/BadCredentials tests | AuthRestClient consumes IRawRequestProvider at the tip and AuthRestClientTests covers both bodies; P1 has RawNoRetry_PreservesStatusAndBody. |
| F25 | Scope creep in the P1 transport PR | Stale-snapshot scenario cannot happen (both Settings paths publish); other items are planned P1 work. |
| F26 | P2 MEC failure handling narrower than legacy | 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. |
| F32 | AppConfig and AppUpdate no longer send member bearer | 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. |
| F39 | 401 on member/content/comms reads no longer falls back | Deliberate and tested (GetMemberAsync_UnauthorizedRetainsProtectedTypedError, AdditionalAllowlistCannotOverrideAuthorizationGuard); plan lines 795-796, 1074, 1823. |
| F43 | LoggedInChanged invalidation depends on lazy DataManager construction |
App still resolves CacheContentService, which injects IDocumentsQueries (registered as DataManager), so the subscription exists before any sign-in path. |
| F48 | FlightSearch mapping re-implemented; legacy mappers are test oracle only | Infrastructure cannot reference the head model; production uses ToCriteria(); parity test is a deliberate migration guard. |
| F49 | RegisterPushTokenAsync still returns Task; layer crossing |
Plan's Result-plus-diagnostic contract is implemented at the port level; both crossings go through Application ports. |
| F53 | Prefetch aborts on per-document OCE | No OCE source reaches the loop: token path swallows exceptions and downloads convert timeouts to Result failures. |
| F54 | Logout regression test for the document memo is vacuous | DocumentLogoutParticipant is in the real logout path in that test; the assertion fails if the generation does not advance. |
| F56 | P4-A5 only compile-verified; viewer failure dialog untested | Doc discloses the device walkthrough as not run (P4-A5 row, P7-A4 open); awaited viewer behaviour is documented as deliberate. |
| F59 | Logout no longer wipes SecureStorage (RemoveAll) |
SecureMsalCache.Register is never called and UseMSAL is false, so no MSAL key exists; base never removed the MSAL account either. |
| F63 | FTDT push starts after a separate generation check | Fixed in P6 (push started inside the same TryExecute); stuck flag sits on an outgoing in-memory instance. |
| F77 | FTDT writes lost 3-attempt transient retry | 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 | Menu "count is zero" guard has no test | Menu_EmptyWireResponseRemainsAFailureForDomainFallback covers [], null, "" at the adapter. |
| F107 | Locked decision "NSwag clients stay generated" rewritten | Recorded as plan Q6 "Phase 3 refinement" and in implementation doc lines 130-135; routes and bodies pinned by tests. Refuted 3 of 3. |
| F110 | Dynamic-content pilot moved without both-simulator evidence | 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 | Doc never records Pass/Fail/Not run per criterion | Every device-gated row says "not run" or "remains pending"; P7-A4 and P7-A5 marked Open. |
| F113 | Plan edits relax process items (single P3 PR, shared fake base) | Documented deviation attributed to the implementation request; plan line 964 already permits shared fixtures. |
| F114 | No CI or branch policy on any stack PR (filed high) | 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 | All PRs show cumulative diffs against a fixed target | Plan line 966 mandates every phase PR opens into the integration branch; refuted 2 of 3. |
| F116 | Merge order and strategy unconfigured | Plan requires explicit completion when no policy exists; each PR body names the merge order; refuted 2 of 3. |
| F118 | Local-only branches and untracked dirs (informational) | Workspace housekeeping, not a defect; phase5-saved holds no unpushed commits; .agents/ and .codex/ do not show as untracked. |
6633bb59 (codex/service-layer-phase7)| Step | Result |
|---|---|
dotnet build UnitTest/UnitTest.csproj -c Debug --no-incremental |
Succeeded, 0 errors, 776 warnings. |
dotnet test UnitTest/UnitTest.csproj -c Debug --no-build |
Failed 0, Passed 2607, Skipped 0, Total 2607. Matches the doc's 2,607. |
dotnet test UnitTest/UnitTest.csproj -c Release |
Failed 0, Passed 2601, Skipped 0, Total 2601. Matches the doc's 2,601. |
dotnet build ALPAMobile/ALPAMobile.csproj -f net10.0-ios -c Debug --no-incremental |
Succeeded, 0 errors, 675 warnings. |
dotnet build ALPAMobile/ALPAMobile.csproj -f net10.0-android -c Debug --no-incremental |
Succeeded, 0 errors, 702 warnings (includes NU1903 for SQLitePCLRaw.lib.e_sqlite3.android 2.1.11, GHSA-2m69-gcr7-jv3q, unrelated to this refactor). |
#if DEBUG only (MockThemeApiClientTests plus one case). No [Ignore], [Explicit], Assert.Ignore or Assume in UnitTest at the tip.IAuthentication (deliberate [Obsolete]), 8 per platform. No CS0246/CS0234 errors; no warnings for RestService, DataManager.x.ratchet: the 40 Architecture tests pass at the tip; ratchet active set is 0; the five legacy types have zero references in .cs, .razor, .xaml, .csproj. Mutation testing showed global::-qualified and nested platform-only aliases evade the source scanner (F94).x.di probe (scratch copy only): ValidateOnBuild + ValidateScopes and resolving every registered type show no circular or captive-scope error; ISessionState, IAuthSession, ICredentialStore, IAuthentication, IAuthTokenProvider are one Authentication instance. The only unregistered injected type found is concrete ALPALogService (F91).x.security): no critical or high issue; the stack tightens auth (AuthRestClient and public app-config use BearerPolicy.None, UseCookies=false, less per-record logging).x.compat): cache keys, LiteDB collection names, persisted token/credential keys, document file paths and wire routes are unchanged; no persisted generation stamp introduced.origin for decoupling-docs (a05df6a9) and phase0 to phase7; ahead 0, behind 0. codex/service-layer-phase5-saved (3c64493d) is an ancestor of origin phase 5 with no unpushed content. Integration base origin/imp/blazor-hybrid-service-layer-decoupling = dca3123d.mergeStatus=succeeded, target refs/heads/imp/blazor-hybrid-service-layer-decoupling, one iteration each, lastMergeSourceCommit equal to the origin branch tip on all eight.codex branch or merge ref. Policies 18, 19, 20 (Build 98 Android, 99 iOS, 100 Docs) and reviewer policy 21 are scoped Exact to imp/blazor-hybrid; policy 12 to DotNet10; none target the integration branch. Consequence: none of the per-phase test and build claims was machine-verified by CI on the PRs; the section 7 reproduction is the first independent run.SKIP_DOCS_TESTS, SKIP_SECRET_SCAN); PR 2515 states none (not independently verified).Not verifiable from a read-only static review, and needs a person or a device:
/home for not-found.NSUrlSessionHandler honour the 30s token?).ActivatorUtilities are unchecked.NSUrlSessionHandler honours cancellation tokens and strips Authorization on cross-host redirects, and whether Sentry's HTTP handler applies to the named ServiceLayerHttpClient (F12, security lens).getalpano can return unquoted text (F41), whether getmecairports can send "" numerics (F28), whether downloadfile and FTDT writes are server-idempotent (F50, plan wants an explicit contract)..razor/.cs Presentation pages, most UnitTest bodies (about 2,500), the FTDT mapper body (559-line move), MecStringJsonConverter, ContentWireModels and raw wire models. MSAL paths (UseMSAL=false) were read but not traced in depth.x.security analysed the cumulative diff and tip files, so a bug introduced and fixed within the stack may not appear here.imp/blazor-hybrid has moved to 4774e079 (includes PR 2493) since the stack was cut, so the final-integration merge conflicts described in F117 and section 3 are the first thing to resolve.