1# Feed Implementation - Coves Mobile App 2 3**Date:** October 28, 2025 4**Status:** ✅ Complete 5**Branch:** main (uncommitted) 6 7## Overview 8 9This document details the implementation of the feed functionality for the Coves mobile app, including integration with the Coves backend API for authenticated timeline and public discovery feeds. 10 11--- 12 13## Features Implemented 14 15### 1. Backend API Integration 16- ✅ Connected Flutter app to Coves backend at `localhost:8081` 17- ✅ Implemented authenticated timeline feed (`/xrpc/social.coves.feed.getTimeline`) 18- ✅ Implemented public discover feed (`/xrpc/social.coves.feed.getDiscover`) 19- ✅ JWT Bearer token authentication from OAuth session 20- ✅ Cursor-based pagination for infinite scroll 21 22### 2. Data Models 23- ✅ Created comprehensive post models matching backend schema 24- ✅ Support for external link embeds with preview images 25- ✅ Community references, author info, and post stats 26- ✅ Graceful handling of null/empty feed responses 27 28### 3. Feed UI 29- ✅ Pull-to-refresh functionality 30- ✅ Infinite scroll with pagination 31- ✅ Loading states (initial, pagination, error) 32- ✅ Empty state messaging 33- ✅ Post cards with community badges, titles, and stats 34- ✅ Link preview images with caching 35- ✅ Error handling with retry capability 36 37### 4. Network & Performance 38- ✅ ADB reverse port forwarding for local development 39- ✅ Android network security config for HTTP localhost 40- ✅ Cached image loading with retry logic 41- ✅ Automatic token injection via Dio interceptors 42 43--- 44 45## Architecture 46 47### File Structure 48 49``` 50lib/ 51├── models/ 52│ └── post.dart # Data models for posts, embeds, communities 53├── services/ 54│ └── coves_api_service.dart # HTTP client for Coves backend API 55├── providers/ 56│ ├── auth_provider.dart # OAuth session & token management (modified) 57│ └── feed_provider.dart # Feed state management with ChangeNotifier 58├── screens/home/ 59│ └── feed_screen.dart # Feed UI with post cards (rewritten) 60└── config/ 61 └── oauth_config.dart # API endpoint configuration (modified) 62``` 63 64--- 65 66## Implementation Details 67 68### Data Models (`lib/models/post.dart`) 69 70**Created comprehensive models:** 71 72```dart 73TimelineResponse // Top-level feed response with cursor 74 └─ FeedViewPost[] // Individual feed items 75 ├─ PostView // Post content and metadata 76 ├─ AuthorView 77 ├─ CommunityRef 78 ├─ PostStats 79 ├─ PostEmbed (optional) 80 └─ ExternalEmbed (for link previews) 81 └─ PostFacet[] (optional) 82 └─ FeedReason (optional) 83``` 84 85**Key features:** 86- All models use factory constructors for JSON deserialization 87- Handles null feed arrays (backend returns `{"feed": null}` for empty feeds) 88- External embeds parse thumbnail URLs, titles, descriptions 89- Optional fields properly handled throughout 90 91**Example PostEmbed with ExternalEmbed:** 92```dart 93class PostEmbed { 94 final String type; // e.g., "social.coves.embed.external" 95 final ExternalEmbed? external; // Parsed external link data 96 final Map<String, dynamic> data; // Raw embed data 97} 98 99class ExternalEmbed { 100 final String uri; // Link URL 101 final String? title; // Link title 102 final String? description; // Link description 103 final String? thumb; // Thumbnail image URL 104 final String? domain; // Domain name 105} 106``` 107 108--- 109 110### API Service (`lib/services/coves_api_service.dart`) 111 112**Purpose:** HTTP client for Coves backend using Dio 113 114**Configuration:** 115```dart 116Base URL: http://localhost:8081 117Timeout: 10 seconds 118Authentication: Bearer JWT tokens via interceptors 119``` 120 121**Key Methods:** 122 1231. **`getTimeline({String? cursor, int limit = 15})`** 124 - Endpoint: `/xrpc/social.coves.feed.getTimeline` 125 - Authenticated: ✅ (requires Bearer token) 126 - Returns: `TimelineResponse` with personalized feed 127 1282. **`getDiscover({String? cursor, int limit = 15})`** 129 - Endpoint: `/xrpc/social.coves.feed.getDiscover` 130 - Authenticated: ❌ (public endpoint) 131 - Returns: `TimelineResponse` with public discover feed 132 133**Interceptor Architecture:** 134```dart 1351. Auth Interceptor (adds Bearer token) 136 1372. Logging Interceptor (debug output) 138 1393. HTTP Request 140``` 141 142**Token Management:** 143- Token extracted from OAuth session via `AuthProvider.getAccessToken()` 144- Automatically injected into all authenticated requests 145- Token can be updated dynamically via `updateAccessToken()` 146 147--- 148 149### Feed State Management (`lib/providers/feed_provider.dart`) 150 151**Purpose:** Manages feed data and loading states using ChangeNotifier pattern 152 153**State Properties:** 154```dart 155List<FeedViewPost> posts // Current feed posts 156bool isLoading // Initial load state 157bool isLoadingMore // Pagination load state 158String? error // Error message 159String? _cursor // Pagination cursor 160bool hasMore // More posts available 161``` 162 163**Key Methods:** 164 1651. **`fetchTimeline()`** 166 - Loads authenticated user's timeline 167 - Clears existing posts 168 - Updates loading state 169 - Fetches access token from AuthProvider 170 1712. **`fetchDiscover()`** 172 - Loads public discover feed 173 - No authentication required 174 1753. **`loadMore({required bool isAuthenticated})`** 176 - Appends next page using cursor 177 - Prevents multiple simultaneous requests 178 - Updates `hasMore` based on response 179 1804. **`retry({required bool isAuthenticated})`** 181 - Retries failed requests 182 - Used by error state UI 183 184**Error Handling:** 185- Network errors (connection refused, timeouts) 186- Authentication errors (401, token expiry) 187- Empty/null responses 188- User-friendly error messages 189 190--- 191 192### Feed UI (`lib/screens/home/feed_screen.dart`) 193 194**Complete rewrite** from StatelessWidget to StatefulWidget 195 196**Features:** 197 1981. **Pull-to-Refresh** 199 ```dart 200 RefreshIndicator( 201 onRefresh: _onRefresh, 202 // Reloads appropriate feed (timeline/discover) 203 ) 204 ``` 205 2062. **Infinite Scroll** 207 ```dart 208 ScrollController with listener 209 - Detects 80% scroll threshold 210 - Triggers pagination automatically 211 - Shows loading spinner at bottom 212 ``` 213 2143. **UI States:** 215 - **Loading:** Centered CircularProgressIndicator 216 - **Error:** Icon, message, and retry button 217 - **Empty:** Custom message based on auth status 218 - **Content:** ListView with post cards + pagination 219 2204. **Post Card Layout (`_PostCard`):** 221 ``` 222 ┌─────────────────────────────────────┐ 223 │ [Avatar] community-name │ 224 │ Posted by username │ 225 │ │ 226 │ Post Title (bold, 18px) │ 227 │ │ 228 │ [Link Preview Image - 180px] │ 229 │ │ 230 │ ↑ 42 💬 5 │ 231 └─────────────────────────────────────┘ 232 ``` 233 2345. **Link Preview Images (`_EmbedCard`):** 235 - Uses `CachedNetworkImage` for performance 236 - 180px height, full width, cover fit 237 - Loading placeholder with spinner 238 - Error fallback with broken image icon 239 - Rounded corners with border 240 241**Lifecycle Management:** 242- ScrollController properly disposed 243- Fetch triggered in `initState` 244- Provider listeners cleaned up automatically 245 246--- 247 248### Authentication Updates (`lib/providers/auth_provider.dart`) 249 250**Added method:** 251```dart 252Future<String?> getAccessToken() async { 253 if (_session == null) return null; 254 255 try { 256 final session = await _session!.sessionGetter.get(_session!.sub); 257 return session.tokenSet.accessToken; 258 } catch (e) { 259 debugPrint('❌ Failed to get access token: $e'); 260 return null; 261 } 262} 263``` 264 265**Purpose:** Extracts JWT access token from OAuth session for API authentication 266 267--- 268 269### Network Configuration 270 271#### Android Manifest (`android/app/src/main/AndroidManifest.xml`) 272 273**Added:** 274```xml 275<application 276 android:usesCleartextTraffic="true" 277 android:networkSecurityConfig="@xml/network_security_config"> 278``` 279 280**Purpose:** Allows HTTP traffic to localhost for local development 281 282#### Network Security Config (`android/app/src/main/res/xml/network_security_config.xml`) 283 284**Created:** 285```xml 286<network-security-config> 287 <domain-config cleartextTrafficPermitted="true"> 288 <domain includeSubdomains="true">192.168.1.7</domain> 289 <domain includeSubdomains="true">localhost</domain> 290 <domain includeSubdomains="true">127.0.0.1</domain> 291 <domain includeSubdomains="true">10.0.2.2</domain> 292 </domain-config> 293</network-security-config> 294``` 295 296**Purpose:** Whitelists local development IPs for cleartext HTTP 297 298--- 299 300### Configuration Changes 301 302#### OAuth Config (`lib/config/oauth_config.dart`) 303 304**Added:** 305```dart 306// API Configuration 307// Using adb reverse port forwarding, phone can access via localhost 308// Setup: adb reverse tcp:8081 tcp:8081 309static const String apiUrl = 'http://localhost:8081'; 310``` 311 312#### Main App (`lib/main.dart`) 313 314**Changed from single provider to MultiProvider:** 315```dart 316runApp( 317 MultiProvider( 318 providers: [ 319 ChangeNotifierProvider.value(value: authProvider), 320 ChangeNotifierProvider(create: (_) => FeedProvider()), 321 ], 322 child: const CovesApp(), 323 ), 324); 325``` 326 327#### Dependencies (`pubspec.yaml`) 328 329**Added:** 330```yaml 331dio: ^5.9.0 # HTTP client 332cached_network_image: ^3.4.1 # Image caching with retry logic 333``` 334 335--- 336 337## Development Setup 338 339### Local Backend Connection 340 341**Problem:** Android devices can't access `localhost` on the host machine directly. 342 343**Solution:** ADB reverse port forwarding 344 345```bash 346# Create tunnel from phone's localhost:8081 -> computer's localhost:8081 347adb reverse tcp:8081 tcp:8081 348 349# Verify connection 350adb reverse --list 351``` 352 353**Important Notes:** 354- Port forwarding persists until device disconnects or adb restarts 355- Need to re-run after device reconnection 356- Does not affect regular phone usage 357 358### Backend Configuration 359 360**For local development, set in backend `.env.dev`:** 361```bash 362# Skip JWT signature verification (trust any valid JWT format) 363AUTH_SKIP_VERIFY=true 364``` 365 366**Then export and restart backend:** 367```bash 368export AUTH_SKIP_VERIFY=true 369# Restart backend service 370``` 371 372⚠️ **Security Warning:** `AUTH_SKIP_VERIFY=true` is for Phase 1 local development only. Must be `false` in production. 373 374--- 375 376## Known Issues & Limitations 377 378### 1. Community Handles Not Included 379**Issue:** Backend `CommunityRef` only returns `did`, `name`, `avatar` - no `handle` field 380 381**Current Display:** `c/test-usnews` (name only) 382 383**Desired Display:** `test-usnews@coves.social` (full handle) 384 385**Solution:** Backend needs to: 3861. Add `handle` field to `CommunityRef` struct 3872. Update feed SQL queries to fetch `c.handle` 3883. Populate handle in response 389 390**Status:** 🔜 Backend work pending 391 392### 2. Image Loading Errors 393**Issue:** Initial implementation with `Image.network` had "Connection reset by peer" errors from Kagi proxy 394 395**Solution:** Switched to `CachedNetworkImage` which provides: 396- Retry logic for flaky connections 397- Disk caching for instant subsequent loads 398- Better error handling 399 400**Status:** ✅ Resolved 401 402### 3. Post Text Body Removed 403**Decision:** Removed post text body from feed cards to keep UI clean 404 405**Current Display:** 406- Community & author 407- Post title (if present) 408- Link preview image (if present) 409- Stats 410 411**Rationale:** Text preview was redundant with title and made cards too busy 412 413--- 414 415## Testing Notes 416 417### Manual Testing Performed 418 419✅ **Feed Loading** 420- Authenticated timeline loads correctly 421- Unauthenticated discover feed works 422- Empty feed shows appropriate message 423 424✅ **Pagination** 425- Infinite scroll triggers at 80% threshold 426- Cursor-based pagination works 427- No duplicate posts loaded 428 429✅ **Pull to Refresh** 430- Clears and reloads feed 431- Works on both timeline and discover 432 433✅ **Authentication** 434- Bearer tokens injected correctly 435- 401 errors handled gracefully 436- Token refresh tested 437 438✅ **Images** 439- Link preview images load successfully 440- Caching works (instant load on scroll back) 441- Error fallback displays for broken images 442- Loading placeholder shows during fetch 443 444✅ **Error Handling** 445- Connection errors show retry button 446- Network timeouts handled 447- Null feed responses handled 448 449✅ **Performance** 450- Smooth 60fps scrolling 451- Images don't block UI thread 452- No memory leaks detected 453 454--- 455 456## Performance Optimizations 457 4581. **Image Caching** 459 - `CachedNetworkImage` provides disk cache 460 - SQLite-based cache metadata 461 - Reduces network requests significantly 462 4632. **ListView.builder** 464 - Only renders visible items 465 - Efficient for large feeds 466 4673. **Pagination** 468 - Load 15 posts at a time 469 - Prevents loading entire feed upfront 470 4714. **State Management** 472 - ChangeNotifier only rebuilds affected widgets 473 - No unnecessary full-screen rebuilds 474 475--- 476 477## Future Enhancements 478 479### Short Term 480- [ ] Update UI to use community handles when backend provides them 481- [ ] Add post detail view (tap to expand) 482- [ ] Add comment counts and voting UI 483- [ ] Implement user profile avatars (currently placeholder) 484- [ ] Add community avatars (currently initials only) 485 486### Medium Term 487- [ ] Add post creation flow 488- [ ] Implement voting (upvote/downvote) 489- [ ] Add comment viewing 490- [ ] Support image galleries (multiple images) 491- [ ] Support video embeds 492 493### Long Term 494- [ ] Offline support with local cache 495- [ ] Push notifications for feed updates 496- [ ] Advanced feed filtering/sorting 497- [ ] Search functionality 498 499--- 500 501## PR Review Fixes (October 28, 2025) 502 503After initial implementation, a comprehensive code review identified several critical issues that have been addressed: 504 505### 🚨 Critical Issues Fixed 506 507#### 1. P1: Access Token Caching Issue 508**Problem:** Access tokens were cached in `CovesApiService`, causing 401 errors after ~1 hour when atProto OAuth rotates tokens. 509 510**Fix:** [lib/services/coves_api_service.dart:19-75](../lib/services/coves_api_service.dart#L19-L75) 511- Changed from `setAccessToken(String?)` to constructor-injected `tokenGetter` function 512- Dio interceptor now fetches fresh token before **every** authenticated request 513- Prevents stale credential issues entirely 514 515**Before:** 516```dart 517void setAccessToken(String? token) { 518 _accessToken = token; // ❌ Cached, becomes stale 519} 520``` 521 522**After:** 523```dart 524CovesApiService({Future<String?> Function()? tokenGetter}) 525 : _tokenGetter = tokenGetter; 526 527onRequest: (options, handler) async { 528 final token = await _tokenGetter(); // ✅ Fresh every time 529 options.headers['Authorization'] = 'Bearer $token'; 530} 531``` 532 533#### 2. Business Logic in Widget Layer 534**Problem:** `FeedScreen` contained authentication decision logic, violating clean architecture. 535 536**Fix:** [lib/providers/feed_provider.dart:45-55](../lib/providers/feed_provider.dart#L45-L55) 537- Moved auth-based feed selection logic into `FeedProvider.loadFeed()` 538- Widget layer now simply calls provider methods without business logic 539 540**Before (in FeedScreen):** 541```dart 542void _loadFeed() async { 543 if (authProvider.isAuthenticated) { 544 final token = await authProvider.getAccessToken(); 545 feedProvider.setAccessToken(token); 546 feedProvider.fetchTimeline(refresh: true); // ❌ Business logic in UI 547 } else { 548 feedProvider.fetchDiscover(refresh: true); 549 } 550} 551``` 552 553**After (in FeedProvider):** 554```dart 555Future<void> loadFeed({bool refresh = false}) async { 556 if (_authProvider.isAuthenticated) { // ✅ Logic in provider 557 await fetchTimeline(refresh: refresh); 558 } else { 559 await fetchDiscover(refresh: refresh); 560 } 561} 562``` 563 564**After (in FeedScreen):** 565```dart 566void _loadFeed() { 567 feedProvider.loadFeed(refresh: true); // ✅ No business logic 568} 569``` 570 571#### 3. Production Security Risk 572**Problem:** Network security config allowed cleartext HTTP without warnings, risking production leak. 573 574**Fix:** [android/app/src/main/res/xml/network_security_config.xml:3-15](../android/app/src/main/res/xml/network_security_config.xml#L3-L15) 575- Added prominent XML comments warning about development-only usage 576- Added TODO items for production build flavors 577- Clear documentation that cleartext is ONLY for localhost 578 579#### 4. Missing Test Coverage 580**Problem:** No tests for critical auth and feed functionality. 581 582**Fix:** Created comprehensive test files with 200+ lines each 583- `test/providers/auth_provider_test.dart` - Unit tests for authentication 584- `test/providers/feed_provider_test.dart` - Unit tests for feed state 585- `test/widgets/feed_screen_test.dart` - Widget tests for UI 586 587**Added dependencies:** 588```yaml 589mockito: ^5.4.4 590build_runner: ^2.4.13 591``` 592 593**Test coverage includes:** 594- Sign in/out flows with error handling 595- Token refresh failure → auto sign-out 596- Feed loading (timeline/discover) 597- Pagination and infinite scroll 598- Error states and retry logic 599- Widget lifecycle (mounted checks, dispose) 600- Accessibility (Semantics widgets) 601 602### ⚠️ Important Issues Fixed 603 604#### 5. Code Duplication (DRY Violation) 605**Problem:** `fetchTimeline()` and `fetchDiscover()` had 90% identical code. 606 607**Fix:** [lib/providers/feed_provider.dart:57-117](../lib/providers/feed_provider.dart#L57-L117) 608- Extracted common logic into `_fetchFeed()` method 609- Both methods now use shared implementation 610 611**After:** 612```dart 613Future<void> _fetchFeed({ 614 required bool refresh, 615 required Future<TimelineResponse> Function() fetcher, 616 required String feedName, 617}) async { 618 // Common logic: loading states, error handling, pagination 619} 620 621Future<void> fetchTimeline({bool refresh = false}) => _fetchFeed( 622 refresh: refresh, 623 fetcher: () => _apiService.getTimeline(...), 624 feedName: 'Timeline', 625); 626``` 627 628#### 6. Token Refresh Failure Handling 629**Problem:** If token refresh failed (e.g., revoked server-side), app stayed in "authenticated" state with broken tokens. 630 631**Fix:** [lib/providers/auth_provider.dart:47-65](../lib/providers/auth_provider.dart#L47-L65) 632- Added automatic sign-out when `getAccessToken()` throws 633- Clears invalid session state immediately 634 635**After:** 636```dart 637try { 638 final session = await _session!.sessionGetter.get(_session!.sub); 639 return session.tokenSet.accessToken; 640} catch (e) { 641 debugPrint('🔄 Token refresh failed - signing out user'); 642 await signOut(); // ✅ Clear broken session 643 return null; 644} 645``` 646 647#### 7. No SafeArea Handling 648**Problem:** Content could be obscured by notches, home indicators, system UI. 649 650**Fix:** [lib/screens/home/feed_screen.dart:71-73](../lib/screens/home/feed_screen.dart#L71-L73) 651```dart 652body: SafeArea( 653 child: _buildBody(feedProvider, isAuthenticated), 654), 655``` 656 657#### 8. Inefficient Provider Listeners 658**Problem:** Widget rebuilt on **every** `AuthProvider` change, not just `isAuthenticated`. 659 660**Fix:** [lib/screens/home/feed_screen.dart:60](../lib/screens/home/feed_screen.dart#L60) 661```dart 662// Before 663final authProvider = Provider.of<AuthProvider>(context); // ❌ Rebuilds on any change 664 665// After 666final isAuthenticated = context.select<AuthProvider, bool>( 667 (p) => p.isAuthenticated // ✅ Only rebuilds when this specific field changes 668); 669``` 670 671#### 9. Missing Mounted Check 672**Problem:** `addPostFrameCallback` could execute after widget disposal. 673 674**Fix:** [lib/screens/home/feed_screen.dart:25-28](../lib/screens/home/feed_screen.dart#L25-L28) 675```dart 676WidgetsBinding.instance.addPostFrameCallback((_) { 677 if (mounted) { // ✅ Check before using context 678 _loadFeed(); 679 } 680}); 681``` 682 683#### 10. Network Timeout Too Short 684**Problem:** 10-second timeouts fail on slow mobile networks (3G, poor signal). 685 686**Fix:** [lib/services/coves_api_service.dart:23-24](../lib/services/coves_api_service.dart#L23-L24) 687```dart 688connectTimeout: const Duration(seconds: 30), // ✅ Was 10s 689receiveTimeout: const Duration(seconds: 30), 690``` 691 692#### 11. Missing Accessibility 693**Problem:** No screen reader support for feed posts. 694 695**Fix:** [lib/screens/home/feed_screen.dart:191-195](../lib/screens/home/feed_screen.dart#L191-L195) 696```dart 697return Semantics( 698 label: 'Feed post in ${post.post.community.name} by ${author}. ${title}', 699 button: true, 700 child: _PostCard(post: post), 701); 702``` 703 704### 💡 Suggestions Implemented 705 706#### 12. Debug Prints Not Wrapped 707**Fix:** [lib/screens/home/feed_screen.dart:367-370](../lib/screens/home/feed_screen.dart#L367-L370) 708```dart 709if (kDebugMode) { // ✅ No logging overhead in production 710 debugPrint('❌ Image load error: $error'); 711} 712``` 713 714--- 715 716## Code Quality 717 718✅ **Flutter Analyze:** 0 errors, 0 warnings 719```bash 720flutter analyze lib/ 721# Result: No errors, 0 warnings (7 deprecation infos in unrelated file) 722``` 723 724✅ **Architecture Compliance:** 725- Clean separation: UI → Provider → Service 726- No business logic in widgets 727- Dependencies injected via constructors 728- State management consistently applied 729 730✅ **Security:** 731- Fresh token retrieval prevents stale credentials 732- Token refresh failures trigger sign-out 733- Production warnings in network config 734 735✅ **Performance:** 736- Optimized widget rebuilds (context.select) 737- 30-second timeouts for mobile networks 738- SafeArea prevents UI obstruction 739 740✅ **Accessibility:** 741- Semantics labels for screen readers 742- Proper focus management 743 744✅ **Testing:** 745- Comprehensive unit tests for providers 746- Widget tests for UI components 747- Mock implementations for services 748- Error state coverage 749 750✅ **Best Practices Followed:** 751- Controllers properly disposed 752- Const constructors used where possible 753- Null safety throughout 754- Error handling comprehensive 755- Debug logging for troubleshooting 756- Clean separation of concerns 757- DRY principle (no code duplication) 758 759--- 760 761## Deployment Checklist 762 763Before deploying to production: 764 765- [ ] Change backend URL from `localhost:8081` to production endpoint 766- [ ] Remove cleartext traffic permissions from Android config 767- [ ] Ensure `AUTH_SKIP_VERIFY=false` in backend production environment 768- [ ] Test with real OAuth tokens from production PDS 769- [ ] Verify image caching works with production CDN 770- [ ] Add analytics tracking for feed engagement 771- [ ] Add error reporting (Sentry, Firebase Crashlytics) 772- [ ] Test on both iOS and Android physical devices 773- [ ] Performance testing with large feeds (100+ posts) 774 775--- 776 777## Resources 778 779### Backend Endpoints 780- Timeline: `GET /xrpc/social.coves.feed.getTimeline?cursor={cursor}&limit={limit}` 781- Discover: `GET /xrpc/social.coves.feed.getDiscover?cursor={cursor}&limit={limit}` 782 783### Key Dependencies 784- `dio: ^5.9.0` - HTTP client 785- `cached_network_image: ^3.4.1` - Image caching 786- `provider: ^6.1.5+1` - State management 787 788### Related Documentation 789- `CLAUDE.md` - Project instructions and guidelines 790- Backend PRD: `/home/bretton/Code/Coves/docs/PRD_POSTS.md` 791- Backend Community Feeds: `/home/bretton/Code/Coves/docs/COMMUNITY_FEEDS.md` 792 793--- 794 795## Contributors 796- Implementation: Claude (AI Assistant) 797- Product Direction: @bretton 798- Backend: Coves AppView API 799 800--- 801 802*This implementation document reflects the state of the codebase as of October 28, 2025.*