···45454646- Starter pack & list views
47474848+#### 2026-03-29
4949+5050+- Offline/low-connectivity detection and handling with cached data display
5151+5252+#### 2026-04-01
5353+5454+- Profile Context (Blocking/Blocked By, Lists, etc.) section accessible from profiles
5555+4856#### 2026-04-11
49575058- Follow hygiene feature to identify and unfollow inactive or problematic accounts
+1-7
docs/tasks/phase-4.md
···14141515## M15 — Offline Reading & Network Resilience
16161717-- [x] `ConnectivityCubit` via **connectivity_plus** — expose network state stream
1818-- [x] Cache last-fetched feed page as serialised JSON in Drift
1919-- [x] Display cached data immediately on launch, refresh in background
2020-- [x] "You're offline" banner when connectivity is lost
2121-- [x] Disable network-dependent actions (compose, like, repost, follow) when offline with tooltip
2222-- [x] Notifications and DM screens show "No connection" empty state when offline with no cache
2323-- [x] In Debug/Dev mode, add "Simulate Offline" toggle in settings to test offline UI
1717+Complete [2026-03-29](../../CHANGELOG.md#2026-03-29)
24182519## M16 — Jump to Profile
2620
+1-46
docs/tasks/phase-5.md
···76767777## M23 - Profile Context (Constellation)
78787979-### Core - Constellation Client
8080-8181-- [x] `ConstellationClient` - thin HTTP client (`http` package) targeting configurable base URL (default `https://constellation.microcosm.blue`), 10s timeout, `User-Agent: lazurite`
8282-- [x] `Settings` - add `constellation_url` key with default value; expose in Settings screen under "Advanced"
8383-- [x] `getBacklinksCount(subject, source)` → `int` total
8484-- [x] `getDistinct(subject, source, {limit, cursor})` → `({int total, List<String> dids, String? cursor})`
8585-- [x] `getBacklinks(subject, source, {limit, cursor})` → `({int total, List<ConstellationLinkRecord> records, String? cursor})`
8686-- [x] `getManyToMany(subject, source, pathToOther, {limit, cursor})` → `({List<ManyToManyItem> items, String? cursor})`
8787-- [x] `ConstellationLinkRecord` model - `did`, `collection`, `rkey`
8888-- [x] `ManyToManyItem` model - `linkRecord: ConstellationLinkRecord`, `otherSubject: String`
8989-9090-### Core - Profile Context Repository
9191-9292-- [x] `ProfileContextRepository` - depends on `ConstellationClient` + `Bluesky`
9393-- [x] `getBlockedByCount(did)` - calls `getBacklinksCount(did, 'app.bsky.graph.block:subject')`
9494-- [x] `getBlockedByProfiles(did, {cursor})` - calls `getDistinct`, hydrates DIDs via `bluesky.actor.getProfiles` (batched 25), returns `({List<ProfileView> profiles, String? cursor, int total})`
9595-- [x] `getBlockingProfiles(did, {cursor})` - calls `com.atproto.repo.listRecords(repo: did, collection: 'app.bsky.graph.block')`, extracts subject DIDs, hydrates via `getProfiles`, returns same shape
9696-- [x] `getListsOn(did, {cursor})` - calls `getManyToMany(did, 'app.bsky.graph.listitem:subject', 'list')`, derives list AT-URIs from `otherSubject`, hydrates via `bluesky.graph.getList`, returns `({List<ListView> lists, String? cursor, int total})`
9797-9898-### Cubit
9999-100100-- [x] `ProfileContextCubit` - manages tab state, loads counts on init for all three tabs
101101-- [x] `ProfileContextState` - fields: `blockedByCount`, `blockingCount`, `listsOnCount`, per-tab `status` (initial/loading/loaded/error), per-tab item list + cursor
102102-- [x] `loadBlockedBy({cursor})` - fetches page of blocked-by profiles, appends to state
103103-- [x] `loadBlocking({cursor})` - fetches page of blocking profiles, appends to state
104104-- [x] `loadListsOn({cursor})` - fetches page of lists, appends to state
105105-- [x] Handle own-profile vs other-profile: blocking tab only available for own profile
106106-107107-### UI
108108-109109-- [x] Profile screen overflow menu - add "Profile Context" entry (available for all profiles)
110110-- [x] Route: `/profile-context?did={DID}` in `app_router.dart`
111111-- [x] `ProfileContextScreen` - `AppBar` (title + handle subtitle), `TabBar` with 3 tabs, `BlocProvider` creating cubit
112112-- [x] **Blocked By tab** - count header, "Show accounts" expand, paginated profile tiles (avatar, name, handle), tap → profile navigation, contextualizing note text
113113-- [x] **Blocking tab** - same layout; hidden or explanatory text when viewing other profiles
114114-- [x] **Lists tab** - list cards (name, owner, purpose badge, member count, description), grouped by purpose, tap → `/list?uri=`
115115-- [x] Per-tab states: skeleton shimmer (loading), contextual empty state, inline error with retry
116116-- [x] Pull-to-refresh per tab
117117-- [x] Infinite scroll pagination per tab
118118-119119-### Tests
120120-121121-- [x] Unit tests: `ConstellationClient` - each endpoint method, error handling, timeout, URL construction
122122-- [x] Unit tests: `ProfileContextRepository` - DID hydration batching, list URI derivation, cursor passthrough
123123-- [x] Unit tests: `ProfileContextCubit` - state transitions for each tab, own-profile vs other-profile logic, pagination appending
124124-- [x] Widget tests: screen renders 3 tabs, blocked-by count + expand, profile tiles render and navigate, list cards render and navigate, empty states, error + retry, blocking tab hidden for non-own profiles
7979+Completed [2026-04-01](../../CHANGELOG.md#2026-04-01)