mobile bluesky app made with flutter lazurite.stormlightlabs.org/
mobile bluesky flutter
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

docs: add trending

+238 -151
+187 -128
docs/specs/routing.md
··· 1 1 --- 2 - title: AppView Routing (Bluesky + Blacksky + microcosm Fallbacks) 2 + title: AppView Routing + Trending (Bluesky + Blacksky + microcosm Fallbacks) 3 3 updated: 2026-04-29 4 4 --- 5 5 ··· 7 7 8 8 1. Bluesky (`did:web:api.bsky.app#bsky_appview`) 9 9 2. Blacksky (`did:web:api.blacksky.community#bsky_appview`) 10 - 3. microcosm fallbacks for specific degraded paths (identity and backlink-style enrichments) 10 + 3. microcosm fallbacks for specific degraded paths (identity and backlink enrichments) 11 11 12 - Design goal: route intentionally, fail predictably, and avoid hidden dependence on any single provider. 12 + Design goal: route intentionally, fail predictably, and avoid hidden dependence on one provider. 13 13 14 14 ## Product Decisions 15 15 16 16 1. Provider selection is chosen on the login screen. 17 - 2. Provider selection can be changed later in Settings, but change requires app reset. 18 - 3. Cross-provider fallback (provider A -> provider B) is user-controlled, not automatic by default. 19 - 4. Slingshot identity fallback is controlled by a setting. 17 + 2. Provider selection can be changed in Settings, but change requires app reset. 18 + 3. Cross-provider fallback (A -> B) is user-controlled (default off). 19 + 4. Slingshot identity fallback is setting-gated. 20 20 5. Provider health/capability probes run at startup, with manual refresh in Settings. 21 - 6. Blacksky must be available by default on login/onboarding (no advanced-toggle gate). 21 + 6. Blacksky is available by default on login/onboarding. 22 + 7. Trending is first-class: Home/Feed includes a Trending action, and app has a dedicated Trending screen. 22 23 23 24 ## Current State (Lazurite) 24 25 25 - - OAuth entryway is currently hardcoded to `bsky.social` in auth flow. 26 - - App-level settings already support configurable network services in some areas: 27 - - Typeahead provider (`bluesky`/`community`) 28 - - Constellation base URL (default `https://constellation.microcosm.blue`) 29 - - Public profile context lookups already use direct AppView host `public.api.bsky.app` in one path. 26 + - OAuth entryway is hardcoded to `bsky.social` in auth flow. 30 27 - There is no shared AppView routing abstraction and no user-selectable AppView provider. 28 + - Home feed app bar has feed-management action only; no Trending action. 29 + - Router has no `/trending` route. 31 30 32 31 ## Research Findings 33 32 34 - ### 1. Official routing expectations 33 + ### 1. Routing and proxy expectations 35 34 36 - - Bluesky docs: authenticated `app.bsky.*` requests go through the user's PDS and are proxied to AppView. 37 - - Bluesky docs: public `app.bsky.*` endpoints can be called directly on `https://public.api.bsky.app`. 38 - - AT Protocol roadmap: clients should set `atproto-proxy` explicitly and should not depend on legacy default forwarding. 35 + - `app.bsky.*` should route via user PDS for authenticated requests, with explicit `atproto-proxy` toward selected AppView. 36 + - Public `app.bsky.*` reads can call AppView host directly (`public.api.bsky.app` for Bluesky). 37 + - Clients should not rely on legacy default forwarding behavior. 38 + 39 + ### 2. Verified AppViews and compatibility (live checks, 2026-04-29) 40 + 41 + - Bluesky DID/service: 42 + - `did:web:api.bsky.app#bsky_appview` 43 + - public host: `https://public.api.bsky.app` 44 + - Blacksky DID/service: 45 + - `did:web:api.blacksky.community#bsky_appview` 46 + - public host: `https://api.blacksky.community` 47 + - Both hosts currently respond for: 48 + - `app.bsky.actor.getProfile` 49 + - `app.bsky.unspecced.getTrends` 50 + - `app.bsky.unspecced.getTrendingTopics` 39 51 40 - ### 2. Live AppView DID documents (verified) 52 + ### 3. Trending endpoint contract (official lexicons) 41 53 42 - - `https://api.bsky.app/.well-known/did.json` includes: 43 - - `#bsky_appview` at `https://api.bsky.app` 44 - - `#bsky_notif` at `https://api.bsky.app` 45 - - `https://api.blacksky.community/.well-known/did.json` includes: 46 - - `#bsky_appview` at `https://api.blacksky.community` 47 - - `#bsky_notif` at `https://api.blacksky.community` 54 + - `app.bsky.unspecced.getTrends` 55 + - params: `limit` (default 10, min 1, max 25) 56 + - output: `trends[]` (`trendView`) 57 + - `app.bsky.unspecced.getTrendingTopics` 58 + - params: `viewer?` DID, `limit` (default 10, min 1, max 25) 59 + - output: `topics[]` + `suggested[]` (`trendingTopic`) 48 60 49 - ### 3. Live Blacksky API compatibility (spot checks) 61 + ### 4. Live provider divergence relevant to UI 50 62 51 - - `https://api.blacksky.community/xrpc/app.bsky.actor.getProfile?...` returns valid profile payload. 52 - - `https://api.blacksky.community/xrpc/app.bsky.unspecced.getTrends` returns trends payload. 63 + - Bluesky `getTrendingTopics` currently returns both `topics` and non-empty `suggested`. 64 + - Blacksky `getTrendingTopics` currently returns `topics` and often empty `suggested`. 65 + - Link formats differ: 66 + - Bluesky trend links often `/profile/.../feed/...` 67 + - Blacksky trend links often `/topic/<id>` 53 68 54 - ### 4. microcosm services (verified) 69 + ### 5. Other AppViews in ecosystem 55 70 56 - - Constellation endpoint is live for backlink-style counts: 57 - - `https://constellation.microcosm.blue/xrpc/blue.microcosm.links.getBacklinksCount` 58 - - Slingshot identity endpoint is live: 59 - - `https://slingshot.microcosm.blue/xrpc/com.bad-example.identity.resolveMiniDoc` 60 - - Returns DID, handle, and PDS. 71 + - There are additional self-hosted/experimental AppView implementations in the ecosystem. 72 + - For this phase, treat these as `custom` providers only (advanced path), not default onboarding options, until each candidate is validated for DID/service health and endpoint parity. 61 73 62 74 ## Design 63 75 ··· 67 79 68 80 - `bluesky` (default) 69 81 - `blacksky` 70 - - `custom` (optional advanced path; disabled in UI until validated) 71 - 72 - Selection lifecycle: 73 - 74 - - Initial selection is made on the login screen before auth flow starts. 75 - - Post-login changes are allowed in Settings but must trigger an app reset flow to avoid mixed-session routing state. 82 + - `custom` (advanced, validation-gated) 76 83 77 84 Provider descriptor: 78 85 ··· 80 87 class AppViewProvider { 81 88 final String key; // bluesky, blacksky, custom 82 89 final String serviceDid; // did:web:...#bsky_appview 83 - final Uri publicBaseUrl; // public unauthenticated app.bsky host 90 + final Uri publicBaseUrl; // public app.bsky host 84 91 final Uri entrywayUrl; // login/account entryway 92 + final Uri webBaseUrl; // provider web base for relative trend links 85 93 } 86 94 ``` 87 95 88 96 Built-in defaults: 89 97 90 - - Bluesky: 91 - - service DID: `did:web:api.bsky.app#bsky_appview` 92 - - public base: `https://public.api.bsky.app` 93 - - entryway: `https://bsky.social` 94 - - Blacksky: 95 - - service DID: `did:web:api.blacksky.community#bsky_appview` 96 - - public base: `https://api.blacksky.community` 97 - - entryway: `https://blacksky.app` 98 + - Bluesky: `public.api.bsky.app`, `bsky.social`, `https://bsky.app` 99 + - Blacksky: `api.blacksky.community`, `blacksky.app`, `https://blacksky.app` 98 100 99 101 ### Router abstraction 100 102 101 - Introduce `AppViewRouter` as a single source of truth: 103 + Introduce `AppViewRouter` as single source of truth: 102 104 103 105 - `Map<String, String> appBskyProxyHeaders()` 104 106 - `Uri publicEndpoint(String xrpcPath, Map<String, String> query)` 105 107 - `Uri entrywayForAuth()` 108 + - `Uri resolveWebLink(String relativeOrAbsolute)` 106 109 - `Future<AppViewHealth> probeProvider()` 107 110 108 - This keeps routing policy out of individual repositories. 111 + ### Request routing policy 112 + 113 + 1. Authenticated `app.bsky.*` 114 + - Route through PDS. 115 + - Set explicit `atproto-proxy` to selected provider DID. 116 + 117 + 2. Signed-out/public `app.bsky.*` 118 + - Call selected provider `publicBaseUrl` directly. 119 + 120 + 3. `com.atproto.*` 121 + - Never AppView-routed; resolve PDS by DID/handle as normal. 122 + 123 + ### Trending UX and routing 124 + 125 + 1. Home app bar adds `Trending` action button. 126 + - Route target: `/trending`. 127 + 128 + 2. Add dedicated `TrendingScreen`. 129 + - Primary data source: `getTrendingTopics(limit=10)`. 130 + - Required enrichment (initial implementation): `getTrends(limit=10)` for richer metadata (actors, postCount, status/category). 131 + - UI sections: 132 + - `Topics` 133 + - `Suggested` (hidden when empty) 134 + 135 + Implementation note: 136 + 137 + - The first shipped Trending screen must join `getTrendingTopics` + `getTrends` data in one load flow. 138 + - If `getTrends` fails and cross-provider fallback is disabled or unavailable, render `topics` with a degraded metadata state and explicit non-blocking error indicator. 139 + 140 + Deterministic join contract: 141 + 142 + - Build a stable join key for both `topics[]` and `trends[]` before matching. 143 + - Key precedence (in order): 144 + 1. Parsed link key (preferred): 145 + - `/topic/<id>` -> `topic:<id>` 146 + - `/profile/<actor>/feed/<rkey>` -> `feed:<actor>:<rkey>` 147 + 2. Normalized topic string fallback: 148 + - lowercase 149 + - trim 150 + - collapse internal whitespace 151 + - drop leading `#` 152 + - Matching algorithm: 153 + 1. Try exact parsed-link-key match. 154 + 2. If absent, try normalized-topic-string match. 155 + 3. If multiple trend candidates match, pick the candidate with newest `startedAt`. 156 + 4. If still tied, pick lexicographically smallest `link` for deterministic output. 157 + 5. If no match, keep topic row and mark metadata as unavailable. 158 + 159 + Trending UI state contract: 160 + 161 + - `topics` load success + `trends` load success: 162 + - render fully enriched rows (actors/postCount/status/category when present). 163 + - `topics` load success + `trends` degraded/failure: 164 + - render topic rows without metadata fields. 165 + - show non-blocking banner/chip: `Metadata temporarily unavailable`. 166 + - keep row navigation actions enabled. 167 + - `topics` failure: 168 + - render blocking error state for Trending screen. 109 169 110 - ### Request routing policy 170 + 3. Trend row actions: 171 + - Use provider-aware `resolveWebLink` for relative links. 172 + - If link maps to supported internal route, deep-link internally. 173 + - If unsupported, open external browser to provider `webBaseUrl + link`. 111 174 112 - 1. **Authenticated `app.bsky.*`** 113 - - Route through PDS as today. 114 - - Explicitly set `atproto-proxy` to selected provider DID. 115 - 2. **Signed-out/public `app.bsky.*`** 116 - - Call selected provider `publicBaseUrl` directly. 117 - 3. **`com.atproto.*`** 118 - - Never AppView-routed. Resolve target PDS by DID/handle as normal. 175 + 4. Link parsing safety: 176 + - Never assume one provider link format. 177 + - Support at least: 178 + - `/profile/<actor>/feed/<rkey>` 179 + - `/topic/<id>` 180 + - Unknown path formats degrade to external open. 119 181 120 182 ### Fallback policy (defensive) 121 - 122 - Fallback order must be explicit and bounded: 123 183 124 184 1. Try selected provider. 125 - 2. If user enabled "Cross-provider fallback", then on transient failure (`429`, `5xx`, timeout, DNS): 126 - - Try alternate built-in provider for read-only public endpoints. 127 - 3. For specific non-AppView enrichments: 128 - - Backlink/social graph counts and related index lookups: Constellation. 129 - - Identity mini-doc resolution when handle resolution is flaky: Slingshot `resolveMiniDoc` (only when enabled in settings). 130 - 4. Record failure reason and chosen fallback in logs. 131 - 5. Apply a short circuit-breaker window per failed provider/endpoint to prevent retry storms. 185 + 2. If cross-provider fallback setting is ON, then on transient read failures (`429`, `5xx`, timeout, DNS): 186 + - Try alternate built-in provider for read-only public endpoints. 187 + 3. For non-AppView enrichments: 188 + - Backlink/index enrichments: Constellation. 189 + - Identity fallback: Slingshot `resolveMiniDoc` only when enabled. 190 + 4. Log fallback reason/provider and apply endpoint-level circuit breaker. 132 191 133 192 Do not fallback across write operations. 134 193 ··· 136 195 137 196 Track endpoint support per provider to avoid blind retries: 138 197 139 - - `app.bsky.actor.getProfile` (public read): bluesky + blacksky 140 - - `app.bsky.feed.getPostThread` (public read): bluesky + blacksky 141 - - `app.bsky.unspecced.getTrends` (public read): bluesky + blacksky (verify by probe) 142 - - Custom namespaces: provider-specific only 143 - 144 - ### Auth and account UX implications 145 - 146 - - If user selects Blacksky provider, default login entryway should become `https://blacksky.app`. 147 - - Existing accounts keep current PDS/session behavior; AppView selection changes only request routing. 148 - - Add a warning in settings: provider choice affects content ranking, moderation context, and availability. 149 - - Login/onboarding must show both Bluesky and Blacksky as first-class provider options by default. 150 - - Changing provider in settings must present a reset confirmation flow. 198 + - `app.bsky.actor.getProfile` 199 + - `app.bsky.feed.getPostThread` 200 + - `app.bsky.unspecced.getTrends` 201 + - `app.bsky.unspecced.getTrendingTopics` 151 202 152 203 ### Reset UX contract (recommended) 153 204 154 - Best UX contract for provider switching: 205 + Best contract for provider switching: 155 206 156 - 1. User selects a new provider in Settings. 157 - 2. App shows a blocking confirmation sheet: 158 - - "Apply and restart now" 159 - - "Cancel" 160 - - Message: "You will remain signed in. No local data will be deleted." 161 - 3. On confirm, app performs a soft restart: 162 - - Persist new provider selection first. 163 - - Cancel in-flight requests. 164 - - Tear down and rebuild app-level DI/blocs/repositories. 165 - - Return to bootstrap/splash and rehydrate from persisted state. 207 + 1. User selects provider in Settings. 208 + 2. Blocking confirmation sheet: 209 + - `Apply and restart now` 210 + - `Cancel` 211 + - Copy: user stays signed in; no local DB wipe. 212 + 3. On confirm, perform soft restart: 213 + - Persist provider first. 214 + - Stop new requests and cancel in-flight work. 215 + - Tear down and rebuild app-level DI/blocs/services. 216 + - Return to bootstrap and rehydrate from persisted state. 166 217 167 - For this phase, do not log out users and do not wipe local database on provider change. 218 + This avoids mixed in-memory routing state while preserving session continuity. 168 219 169 220 ### State safety requirements 170 - 171 - To avoid mixed in-memory routing state: 172 221 173 222 1. `AppViewRouter` is the only runtime source of provider state. 174 - 2. Long-lived repositories/blocs must not cache provider values separately. 175 - 3. Provider switch path must block new requests until rebuild completes. 176 - 4. Use a routing epoch/version so stale pre-reset responses are ignored post-reset. 223 + 2. Long-lived repos/blocs must not cache provider independently. 224 + 3. Provider switch blocks new requests until rebuild completes. 225 + 4. Use routing epoch/version so stale pre-reset responses are dropped. 177 226 178 227 ### Login-time persistence ordering 179 228 180 - To ensure provider choice is honored from first network call: 181 - 182 - 1. Persist login-screen provider choice before starting OAuth/app-password calls. 183 - 2. Disable login submission while provider persistence is in flight. 184 - 3. Construct auth/network clients only after persisted provider is available in bootstrap. 229 + 1. Persist login-screen provider selection before any auth/network request. 230 + 2. Disable login submission while persistence is in-flight. 231 + 3. Construct auth/network clients only after provider setting loads at bootstrap. 185 232 186 233 ### Health probes 187 234 188 - - Run provider health/capability probes once at startup. 189 - - Expose a manual "Refresh Provider Health" control in Settings. 190 - - Do not run periodic background probes in this phase. 235 + - Run once at startup. 236 + - Expose manual `Refresh Provider Health` in Settings. 237 + - No periodic background probes in this phase. 191 238 192 - ## Adversarial checks (assumptions to challenge) 239 + ## Adversarial checks 193 240 194 - 1. A provider advertises `#bsky_appview` but only partially implements endpoints. 195 - 2. A provider endpoint is live but semantically diverges (labels, trends, moderation filters). 196 - 3. Docs may lag live infrastructure (observed for Blacksky roadmap vs live API host). 197 - 4. Transient success can mask long-tail reliability problems without health telemetry. 241 + 1. Provider advertises `#bsky_appview` but partially implements endpoints. 242 + 2. Semantics diverge even when endpoint exists (moderation, trends, topic links). 243 + 3. Docs can lag live infrastructure. 244 + 4. Trend link paths can drift by provider/version. 198 245 199 246 Mitigation: 200 247 201 - - Runtime capability probes. 248 + - Startup capability probes. 202 249 - Endpoint-level fallback gates. 203 - - Structured logs + per-provider failure counters. 250 + - Structured logs + provider failure counters. 251 + - Defensive link resolver with safe external fallback. 204 252 205 253 ## Testing Strategy 206 254 207 255 ### Unit 208 256 209 - - Provider selection and normalization. 210 - - Login-time provider selection persistence + settings-change reset requirement. 211 - - Bootstrap ordering: no auth/network client creation before provider setting load. 212 - - Routing epoch/version stale-response guard behavior. 213 - - Header injection (`atproto-proxy`) per request class. 214 - - Fallback state machine and circuit breaker behavior. 215 - - Capability matrix enforcement. 257 + - Provider selection/normalization. 258 + - Login-time provider persistence before auth call. 259 + - Bootstrap ordering (no client creation before provider load). 260 + - Routing epoch stale-response guard. 261 + - Header injection (`atproto-proxy`). 262 + - Fallback + circuit-breaker transitions. 263 + - Trending limit clamping (1..25). 264 + - Trend link parsing and resolver fallback behavior. 265 + - Deterministic topic/trend join precedence and tie-break behavior. 266 + - Topic-string normalization behavior for join fallback. 216 267 217 268 ### Integration 218 269 219 - - Signed-out profile/thread fetch through Bluesky and Blacksky. 220 - - Forced primary failure -> alternate provider fallback (when enabled). 221 - - Forced primary failure -> no cross-provider fallback (when disabled). 222 - - Constellation + Slingshot fallback success path. 270 + - `/trending` route reachable from Home button. 271 + - Bluesky and Blacksky trending fetch paths. 272 + - Empty `suggested` renders cleanly. 273 + - `topics` success + `trends` failure renders degraded metadata indicator with usable navigation. 274 + - Forced primary failure with fallback ON/OFF. 275 + - Provider switch soft restart fully rebuilds routing consumers. 223 276 224 277 ### Regression 225 278 226 - - Ensure `com.atproto.*` routes are unaffected. 227 - - Ensure OAuth/App Password auth flows still resolve correct PDS. 279 + - `com.atproto.*` unaffected. 280 + - OAuth/App Password flows keep correct PDS behavior. 281 + - Provider switch does not mix stale in-memory routing state. 228 282 229 - ## Non-goals (for this phase) 283 + ## Non-goals (this phase) 230 284 231 - - Supporting arbitrary third-party AppViews in UI without validation. 232 - - Automatic provider switching for write endpoints. 285 + - Auto-switch providers for write operations. 286 + - Unvalidated public listing of arbitrary third-party AppViews in onboarding. 233 287 - Replacing existing Constellation features. 234 288 235 289 ## Sources 236 290 237 291 - <https://docs.bsky.app/docs/advanced-guides/api-directory> 238 - - <https://atproto.com/blog/2025-protocol-roadmap-spring> 292 + - <https://docs.bsky.app/blog/2025-protocol-roadmap-spring> 239 293 - <https://api.bsky.app/.well-known/did.json> 240 294 - <https://api.blacksky.community/.well-known/did.json> 241 - - <https://docs.blacksky.community/list-of-our-services> 295 + - <https://raw.githubusercontent.com/bluesky-social/atproto/main/lexicons/app/bsky/unspecced/getTrends.json> 296 + - <https://raw.githubusercontent.com/bluesky-social/atproto/main/lexicons/app/bsky/unspecced/getTrendingTopics.json> 297 + - <https://raw.githubusercontent.com/bluesky-social/atproto/main/lexicons/app/bsky/unspecced/defs.json> 298 + - <https://raw.githubusercontent.com/bluesky-social/atproto/main/lexicons/app/bsky/unspecced/getTrendsSkeleton.json> 242 299 - <https://www.microcosm.blue/> 243 300 - <https://constellation.microcosm.blue/> 244 301 - <https://slingshot.microcosm.blue/> 302 + - <https://tangled.org/why.bsky.team/konbini> 303 + - <https://sdk.blue/>
+51 -23
docs/tasks/routing.md
··· 1 1 --- 2 - title: AppView Routing Milestones 2 + title: AppView Routing + Trending Milestones 3 3 updated: 2026-04-29 4 4 --- 5 + 6 + ## M0 - Research + Planning (Complete) 7 + 8 + - [x] Validate Bluesky and Blacksky AppView DID/service identities 9 + - [x] Validate live compatibility for trends/trending-topics endpoints 10 + - [x] Capture provider divergence (link formats, `suggested` behavior) 11 + - [x] Update routing spec with provider-switch UX contract and state safety rules 12 + - [x] Update routing/task plan to include Trending button + `/trending` screen 5 13 6 14 ## M1 - Core Routing Model 7 15 8 16 - [ ] Add `appview_provider` setting with defaults and validation 9 17 - [ ] Add login-screen provider selector (Bluesky + Blacksky visible by default) 10 - - [ ] Persist login-screen provider choice before any auth/network call starts 11 - - [ ] Add provider descriptor model (`serviceDid`, `publicBaseUrl`, `entrywayUrl`) 12 - - [ ] Add `AppViewRouter` abstraction for endpoint/header resolution 18 + - [ ] Persist login-screen provider choice before any auth/network call 19 + - [ ] Add provider descriptor (`serviceDid`, `publicBaseUrl`, `entrywayUrl`, `webBaseUrl`) 20 + - [ ] Add `AppViewRouter` abstraction for endpoint/header/link resolution 13 21 - [ ] Add unit tests for provider normalization/defaults and bootstrap ordering 14 22 15 23 ## M2 - Header + Request Integration 16 24 17 - - [ ] Inject explicit `atproto-proxy` for authenticated `app.bsky.*` requests 25 + - [ ] Inject explicit `atproto-proxy` for authenticated `app.bsky.*` 18 26 - [ ] Route signed-out public `app.bsky.*` reads via selected provider host 19 - - [ ] Ensure `com.atproto.*` flows bypass AppView routing changes 27 + - [ ] Ensure `com.atproto.*` bypasses AppView routing 20 28 - [ ] Add integration tests for Bluesky/Blacksky provider selection 21 29 22 - ## M3 - Fallback Engine 30 + ## M3 - Trending Surface 31 + 32 + - [ ] Add Home app bar `Trending` action button 33 + - [ ] Add `/trending` route and `TrendingScreen` 34 + - [ ] Implement `getTrendingTopics(limit=10)` fetch path 35 + - [ ] Implement required `getTrends(limit=10)` enrichment path for richer metadata 36 + - [ ] Hide `Suggested` section when provider returns empty list 37 + - [ ] Add loading/empty/error states for trending screen 38 + - [ ] Add analytics/logging for provider and fallback used on trending requests 39 + 40 + ## M4 - Trend Link Routing 41 + 42 + - [ ] Add provider-aware trend link resolver (`resolveWebLink`) 43 + - [ ] Support `/profile/<actor>/feed/<rkey>` links 44 + - [ ] Support `/topic/<id>` links 45 + - [ ] Degrade unknown link formats to safe external open 46 + - [ ] Add unit tests for link parsing and fallback resolution 47 + 48 + ## M5 - Fallback Engine 23 49 24 50 - [ ] Add user setting for cross-provider fallback (default off) 25 51 - [ ] Implement bounded fallback chain for read-only public endpoints ··· 27 53 - [ ] Add structured logs for provider/fallback decisions 28 54 - [ ] Add tests for timeout/429/5xx transitions with fallback enabled/disabled 29 55 30 - ## M4 - microcosm Fallbacks 56 + ## M6 - microcosm Fallbacks 31 57 32 - - [ ] Keep Constellation fallback paths first-class for backlink-style enrichments 58 + - [ ] Keep Constellation fallback paths first-class for backlink enrichments 33 59 - [ ] Add setting-gated Slingshot identity fallback for degraded handle resolution 34 60 - [ ] Add tests for fallback parsing and failure handling 35 61 - [ ] Document opt-in behavior and trust boundaries 36 62 37 - ## M5 - Settings and UX 63 + ## M7 - Settings and UX 38 64 39 - - [ ] Add AppView provider controls in Settings (bluesky/blacksky) 40 - - [ ] Add provider-change confirmation that performs app reset 41 - - [ ] Define reset copy: stay signed in, no local data deletion, restart required 65 + - [ ] Add AppView provider controls in Settings (Bluesky/Blacksky) 66 + - [ ] Add provider-change confirmation that performs app soft restart 67 + - [ ] Confirm reset copy: stay signed in, no local data deletion 42 68 - [ ] Show concise warning about moderation/ranking/provider differences 43 - - [ ] Add advanced diagnostics view (active provider, last fallback, last error) 44 - - [ ] Add manual "Refresh Provider Health" action 69 + - [ ] Add diagnostics view (active provider, last fallback, last error) 70 + - [ ] Add manual `Refresh Provider Health` action 45 71 46 - ## M6 - Auth Flow Alignment 72 + ## M8 - Auth + Reset Safety 47 73 48 - - [ ] Tie OAuth entryway default to selected provider (`bsky.social`/`blacksky.app`) 49 - - [ ] Validate app-password and OAuth flows remain backward compatible 74 + - [ ] Tie OAuth entryway default to selected provider (`bsky.social` / `blacksky.app`) 75 + - [ ] Ensure app-password and OAuth flows remain backward compatible 50 76 - [ ] Add migration behavior for existing saved sessions/accounts 51 - - [ ] Ensure app-level soft restart rebuilds DI/blocs/repositories after provider switch 52 - - [ ] Ensure stale pre-reset responses are ignored (routing epoch/version guard) 77 + - [ ] Ensure provider switch rebuilds DI/blocs/services before new requests 78 + - [ ] Add routing epoch/version guard to drop stale pre-reset responses 53 79 54 - ## M7 - Hardening and Release 80 + ## M9 - Hardening + Release 55 81 56 - - [ ] Add provider health probes at startup 57 - - [ ] Add capability matrix checks before retries 82 + - [ ] Run provider health probes at startup 83 + - [ ] Gate retries by capability matrix (`getTrends`, `getTrendingTopics`, etc.) 84 + - [ ] Add end-to-end regression coverage for routing + trending + fallback flows 85 + - [ ] Stage rollout behind feature flag if telemetry indicates instability