Fix false connectivity banner on private Atomic sites#22926
Conversation
|
|
|
|
🤖 Build Failure AnalysisThis build has failures. Claude has analyzed them - check the build annotations for details. |
85cf367 to
b964f35
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## release/26.8 #22926 +/- ##
================================================
+ Coverage 37.33% 37.34% +0.01%
================================================
Files 2319 2320 +1
Lines 124674 124714 +40
Branches 16951 16959 +8
================================================
+ Hits 46551 46580 +29
- Misses 74361 74370 +9
- Partials 3762 3764 +2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
b964f35 to
fd57211
Compare
|
@jkmassel I still see this banner immediately after logging in.
However, the banner disappears if I pull-to-refresh. |
|
@jkmassel Claude identified this as a minor issue worth resolving before merging:
|
Addressed by bf2142e |
This was a race between the application password being created and the checks happening. It's fixed by 3a10158 and ce19bee. You can test this by logging out of the app entirely, then logging back in and choosing that site - you shouldn't see the banner. |
|
@jkmassel This is working for me and I'll approve it, but you may want to address this comment from Claude: PR description contradicts the implementation. The "Out of scope" section says: ▎ Atomic sites with no app-password credentials whose host blocks anonymous discovery still surface the banner But
|
Editor capability detection on Atomic sites probes the direct host via unauthenticated REST API discovery (added in #22883). Private Atomic hosts gate anonymous requests, so discovery fails and the My Site "Unable to connect to your site" banner appears even though the site is reachable. Fall back to an authenticated app-password probe against the same host when discovery fails and credentials exist.
The authenticated app-password fallback previously returned a silent `false` on a non-success response, and the no-credentials path was also silent. Log both (mirroring the discovery-failure log) so a recurrence of the connectivity banner can be diagnosed from AppLog rather than being indistinguishable from "discovery failed, no creds".
On first login an Atomic site's application password is minted asynchronously on the My Site screen, so the connectivity capability fetch can race ahead of it and fail purely for lack of credentials — flashing a false "unable to connect" banner that clears on the next refresh. Treat a credential-less fetch as pending rather than a connection failure, mirroring the existing offline suppression; the application-password card already owns the no-credentials state.
The connectivity banner's capability fetch and the application-password mint both run on the My Site screen with no ordering, so on first login the fetch loses the race and capability flags aren't detected until the next resume/refresh. Add an app-scoped CredentialsChangedNotifier that both credential-establishment paths (headless mint and interactive login) emit to, and have the connectivity slice re-run detection on it — re-reading a fresh site so it observes the just-persisted credentials. This also closes the asymmetry where only the interactive path announced credential changes globally.
The four-way suppression condition tripped detekt's ComplexCondition threshold once the pending-credentials term was added. Lift it into a named `suppressBanner` flag — clearer intent, and out of the if-condition detekt inspects. No behavior change.
d27828b to
77f2cb1
Compare
The pending-credentials suppression also hid the banner when the headless mint *terminally* failed — and the application-password card can't render for private Atomic sites either (its authorize-URL discovery hits the same gated host), so the user was left with a broken site and no signal at all. Track terminal mint failure in the renamed ApplicationPasswordMonitor (was CredentialsChangedNotifier) and suppress only while provisioning is genuinely in flight; once the mint fails, surface the banner. The monitor also wakes capability detection on failure so the banner appears without waiting for the next resume/refresh.
…ils" This reverts commit 357fd82. The terminal-failure surfacing called onMintFailed on every non-success, including transient failures, so a transient first-login mint failure latched hasMintFailed and showed the false connectivity banner this PR set out to suppress. Revert to the pending-suppression behavior; the durable single-source-of-truth redesign is tracked in #22942.
This is a fairly narrow case, so I'm going to leave it alone for this PR and work toward something a bit more comprehensive in |
#22926's first-login hardening signalled credential establishment through a process-global CredentialsChangedNotifier that the banner collected against a re-read of getSelectedSite() — the staleness race called out in #22942. EditorCapabilityDetector.refresh(storedSite), called from the mint path on the exact mutated SiteModel, replaces that coordination, so drop the notifier and its wiring in ApplicationPasswordLoginHelper.
* Bump version number * Update draft release notes for 26.8 * Update draft release notes for Jetpack 26.8. * Release Notes: add new section for next version (26.9) * Use wordpress-rs 0.4.0 * Merge strings from libraries for translation * Freeze strings for translation * Keep JNA/UniFFI classes so wordpress-rs survives R8 in release builds (#22916) AGP 9's R8 strips the @com.sun.jna.Structure.FieldOrder annotation that wordpress-rs JNA bindings read reflectively at runtime, causing a launch crash in minified release builds. Keep the JNA classes, UniFFI bindings, and the annotation so native struct layouts still resolve. * Add temporary release notes * Merge release_notes/26.8 into release/26.8 (#22917) * Update WordPress `PlayStoreStrings.po` for version 26.8 * Update Jetpack `PlayStoreStrings.po` for version 26.8 * Update translations * Update translations * Update WordPress metadata translations for 26.8 * Update Jetpack metadata translations for 26.8 * Bump version number * Merge release_notes/26.8 into release/26.8 (#22922) * Update editorialized release notes * Update WordPress `PlayStoreStrings.po` for version 26.8 * Update Jetpack `PlayStoreStrings.po` for version 26.8 --------- Co-authored-by: Tony Li <tony.li@automattic.com> * Update WordPress metadata translations for 26.8 * Update Jetpack metadata translations for 26.8 * Bump version number * Bump version number * Fix Czech confirm_remove_site translation breaking release lint The 26.8 translation sync introduced a doubled backslash (\\") in the Czech confirm_remove_site string. Android reads \\ as an escaped literal backslash, which hides the %s placeholder from lint, so StringFormatMatches fails: ChooseSiteActivity supplies 1 argument but the string is seen as requiring 0. Use a single-backslash escape (\") to match the English source and every other locale. * Fix Czech delete-confirmation translations breaking release lint term_delete_confirmation_message carried the same doubled backslash (\\") as confirm_remove_site, hiding its %1$s placeholder so lint sees 0 args while TermsDataViewActivity supplies 1 (StringFormatMatches). It was the second such error; lint prints only the first failure, so it surfaced once confirm_remove_site was fixed. Also fix the two identical siblings delete_menu_item_confirmation_message and delete_menu_confirmation_message, which share the defect. Verified locally: :WordPress:lintWordpressRelease BUILD SUCCESSFUL, 0 errors. * Bump version number * Merge release_notes/26.8 into release/26.8 (#22937) * Update release notes * Update WordPress `PlayStoreStrings.po` for version 26.8 * Update Jetpack `PlayStoreStrings.po` for version 26.8 --------- Co-authored-by: Tony Li <tony.li@automattic.com> * Fix false connectivity banner on private Atomic sites (#22926) * Fix false connectivity banner on private Atomic sites Editor capability detection on Atomic sites probes the direct host via unauthenticated REST API discovery (added in #22883). Private Atomic hosts gate anonymous requests, so discovery fails and the My Site "Unable to connect to your site" banner appears even though the site is reachable. Fall back to an authenticated app-password probe against the same host when discovery fails and credentials exist. * Log authenticated direct-host probe failures for diagnosability The authenticated app-password fallback previously returned a silent `false` on a non-success response, and the no-credentials path was also silent. Log both (mirroring the discovery-failure log) so a recurrence of the connectivity banner can be diagnosed from AppLog rather than being indistinguishable from "discovery failed, no creds". * Suppress the connectivity banner while the app password is being minted On first login an Atomic site's application password is minted asynchronously on the My Site screen, so the connectivity capability fetch can race ahead of it and fail purely for lack of credentials — flashing a false "unable to connect" banner that clears on the next refresh. Treat a credential-less fetch as pending rather than a connection failure, mirroring the existing offline suppression; the application-password card already owns the no-credentials state. * Re-detect editor capabilities when an app password is established The connectivity banner's capability fetch and the application-password mint both run on the My Site screen with no ordering, so on first login the fetch loses the race and capability flags aren't detected until the next resume/refresh. Add an app-scoped CredentialsChangedNotifier that both credential-establishment paths (headless mint and interactive login) emit to, and have the connectivity slice re-run detection on it — re-reading a fresh site so it observes the just-persisted credentials. This also closes the asymmetry where only the interactive path announced credential changes globally. * Hoist connectivity banner suppression into a named flag The four-way suppression condition tripped detekt's ComplexCondition threshold once the pending-credentials term was added. Lift it into a named `suppressBanner` flag — clearer intent, and out of the if-condition detekt inspects. No behavior change. * Surface the connectivity banner when the app-password mint fails The pending-credentials suppression also hid the banner when the headless mint *terminally* failed — and the application-password card can't render for private Atomic sites either (its authorize-URL discovery hits the same gated host), so the user was left with a broken site and no signal at all. Track terminal mint failure in the renamed ApplicationPasswordMonitor (was CredentialsChangedNotifier) and suppress only while provisioning is genuinely in flight; once the mint fails, surface the banner. The monitor also wakes capability detection on failure so the banner appears without waiting for the next resume/refresh. * Revert "Surface the connectivity banner when the app-password mint fails" This reverts commit 357fd82. The terminal-failure surfacing called onMintFailed on every non-success, including transient failures, so a transient first-login mint failure latched hasMintFailed and showed the false connectivity banner this PR set out to suppress. Revert to the pending-suppression behavior; the durable single-source-of-truth redesign is tracked in #22942. --------- Co-authored-by: Automattic Release Bot <mobile+wpmobilebot@automattic.com> Co-authored-by: Tony Li <tony.li@automattic.com>
#22926's first-login hardening signalled credential establishment through a process-global CredentialsChangedNotifier that the banner collected against a re-read of getSelectedSite() — the staleness race called out in #22942. EditorCapabilityDetector.refresh(storedSite), called from the mint path on the exact mutated SiteModel, replaces that coordination, so drop the notifier and its wiring in ApplicationPasswordLoginHelper.




Description
Fixes a regression where the My Site "Unable to connect to your site" banner appears for private WP.com Atomic sites even though the site is fully reachable. Introduced by #22883 (present in both
trunkandrelease/26.8).There's a narrow case where this fix doesn't work:
/wp-jsonThis is caused by #22905.
Root cause
The banner is driven by
EditorSettingsRepository.fetchEditorCapabilitiesForSite(), which runs two probes and requires both to pass. For Atomic sites the route-support probe was changed in #22883 to run REST API autodiscovery against the site's own host instead of the WP.com proxy:WpLoginClientis the login-flow client — it's constructed with no credentials (ApplicationModule.provideWpLoginClient), so that discovery call is unauthenticated. A private Atomic site gates anonymous requests to its host, so discovery fails (FailureFetchAndParseApiRoot), the route probe returnsfalse, and the banner renders — even though the proxy-backed theme probe in the same fetch succeeds.Confirmed on device against a private Atomic site, where a single refresh shows the failure is specifically the unauthenticated probe:
FailureFetchAndParseApiRootFix
When direct-host discovery fails and the site has application-password credentials, fall back to an authenticated probe against the same direct host using the app-password (Basic auth) client — the transport the editor actually uses on Atomic. Routes are read from
apiRoot().get()and resolved against the direct host, mirroringfetchRouteSupportViaConfiguredClientbut bypassing the WP.com proxy. Public Atomic sites are unchanged (discovery still succeeds on the first try).Verified on-device: after the fix the banner no longer appears on a private Atomic site — discovery still fails, the authenticated probe succeeds, and capability detection completes.
EditorSettingsRepository:fetchRouteSupportViaDirectHostDiscoveryfalls back to a newfetchRouteSupportViaApplicationPasswordClientwhen discovery fails andhasApplicationPasswordCredentials()is true.WpApiClientProvider: addsgetDirectHostApiUrlResolver(site)— the resolver counterpart togetApplicationPasswordClient, so routes fetched through that client resolve against the host they came from.First-login hardening (from review)
The application password is minted asynchronously on the My Site screen, so on a fresh login the first capability fetch can race ahead of it. Three follow-on behaviours keep that from misfiring:
EditorSettingsRepository.isAwaitingApplicationPassword).ApplicationPasswordMonitorsignals when credentials are established (headless mint or interactive login); the connectivity slice re-runs detection immediately, so capabilities settle without a manual pull-to-refresh.What we explored
/wp-json" robustness for the common case.Out of scope
Giving private Atomic sites a specific "set up access to your site" prompt — rather than the generic connectivity banner — on terminal mint failure. The natural owner, the application-password card, can't render on a private host today because its authorize-URL lookup relies on the same unauthenticated discovery the gated host rejects; fixing that is a separate follow-up.
Testing instructions
First login (the banner race):
Private Atomic site (the regression):
Public Atomic site (regression check):
automatticwidgets.wpcomstaging.com).Simple site (regression check):
Unit tests:
./gradlew :WordPress:testJetpackDebugUnitTestacrossEditorSettingsRepositoryTest,SiteConnectivityBannerViewModelSliceTest,ApplicationPasswordViewModelSliceTest, andApplicationPasswordLoginHelperTest— covering the authenticated fallback, pending-vs-failed banner suppression, auto re-detect on credential establishment, and the terminal-mint-failure banner.