ATlast — you'll never need to find your favorites on another platform again. Find your favs in the ATmosphere.
atproto

fix: sourceUser should be object {username, date} not string

Was setting sourceUser to result.sourceUser.username (string)
Should be result.sourceUser (SourceUser object)

This caused:
- useSearch to call batch.map(r => r.sourceUser.username) on strings
- .username on string returns undefined
- batch-search-actors received null values
- ValidationError: expected string, received null

Also caused localeCompare error when sorting undefined values.

byarielm.fyi 0afa0ffa a90f3a81

verified
Changed files
+116 -17
docs
packages
web
src
+16 -16
docs/git-history.json
··· 8 8 "files_changed": 5 9 9 }, 10 10 { 11 - "hash": "b6b8c3968bbe1d71e38d2acbea3f31c77f08656a", 12 - "short_hash": "b6b8c39", 11 + "hash": "6ced3f0b015af1c9126559a393996576402cfd03", 12 + "short_hash": "6ced3f0", 13 13 "author": "Ariel M. Lighty", 14 - "date": "2025-12-26T13:52:59-05:00", 15 - "message": "fix: correct function name to handleLoadUpload in uploadId useEffect", 16 - "files_changed": 3 14 + "date": "2025-12-26T14:12:46-05:00", 15 + "message": "fix extension flow: create user_source_follows, auto-search, time display\n\nBackend (extension-import.ts):\n- Now creates user_source_follows entries linking upload to source accounts\n- Without these, get-upload-details returned empty (queries FROM user_source_follows)\n- Uses bulkCreate return value (Map<username, id>) to create links\n\nFrontend (App.tsx):\n- handleLoadUpload now detects if upload has no matches yet\n- Sets isSearching: true for new uploads\n- Automatically triggers searchAllUsers for new uploads\n- Saves results after search completes\n- Changed platform from hardcoded \"tiktok\" to \"twitter\"\n\nFrontend (HistoryTab.tsx):\n- Fixed time display: removed \"Uploaded\" prefix\n- Now shows \"about 5 hours ago\" instead of \"Uploaded in about 5 hours\"\n- formatRelativeTime with addSuffix already provides complete sentence\n\nResolves:\n- Empty results on page load\n- No automatic searching\n- History navigation not working (will work after search)\n- Grammatically incorrect time display", 16 + "files_changed": 5 17 17 }, 18 18 { 19 19 "hash": "581ed00fec3c0c5f472c6ff92e00bf4ed5b27e9a", ··· 40 40 "files_changed": 2 41 41 }, 42 42 { 43 - "hash": "a203bc343b1768cd51e08b51ed296eddb51d21b7", 44 - "short_hash": "a203bc3", 43 + "hash": "95636330f387598f55017eda668fb9f91ccde509", 44 + "short_hash": "9563633", 45 45 "author": "Ariel M. Lighty", 46 - "date": "2025-12-26T00:50:44-05:00", 47 - "message": "update documentation with current debugging status\n\nPLAN.md updates:\n- Added current status section with recent fixes and active work\n- Marked Phase 0 as complete\n- Marked Phase 1 as in progress (debugging)\n- Updated changelog with 2025-12-26 progress\n- Updated decision graph count to 288 nodes\n\nEXTENSION_STATUS.md updates:\n- Changed state from READY FOR TESTING to DEBUGGING\n- Added fixed issues section (NaN bug, database init)\n- Added active debugging section\n- Updated decision graph summary to 288 nodes\n- Added node references for recent fixes (#287-288)\n\nDecision graph:\n- Synced with latest nodes (288 total, 276 edges)\n- Tracked database initialization outcome", 48 - "files_changed": 4 46 + "date": "2025-12-26T13:35:52-05:00", 47 + "message": "fix extension api-client: unwrap ApiResponse.data structure\n\nBackend endpoints use successResponse() which wraps data in:\n { success: true, data: {...} }\n\nExtension was expecting flat response structure, causing:\n- uploadToATlast to return undefined (missing importId, redirectUrl)\n- checkSession to return wrapped object instead of user data\n- Invalid URL error: \"http://127.0.0.1:8888undefined\"\n\nFixed both uploadToATlast and checkSession to access apiResponse.data", 48 + "files_changed": 2 49 49 }, 50 50 { 51 51 "hash": "34bd9dcd1237971a87627b148c0452b8484e4871", ··· 88 88 "files_changed": 3 89 89 }, 90 90 { 91 - "hash": "32cdee3aeac7ef986df47e0fff786b5f7471e55b", 92 - "short_hash": "32cdee3", 91 + "hash": "ba29fd68872913ba0a587aa7f29f97b3d373a732", 92 + "short_hash": "ba29fd6", 93 93 "author": "Ariel M. Lighty", 94 94 "date": "2025-12-25T13:22:32-05:00", 95 95 "message": "configure Netlify dev for monorepo with --filter flag\n\nFixed Netlify CLI monorepo detection issue by using --filter flag:\n- Updated root package.json scripts to use 'npx netlify-cli dev --filter @atlast/web'\n- Updated netlify.toml [dev] section to use npm with --prefix for framework command\n- Added monorepo development instructions to CLAUDE.md\n- Documented Windows Git Bash compatibility issue with netlify command\n\nSolution: Use 'npx netlify-cli dev --filter @atlast/web' to bypass monorepo\nproject selection prompt and specify which workspace package to run.\n\nDev server now runs successfully at http://localhost:8888 with all backend\nfunctions loaded.", 96 - "files_changed": 4 96 + "files_changed": 5 97 97 }, 98 98 { 99 - "hash": "ba29fd68872913ba0a587aa7f29f97b3d373a732", 100 - "short_hash": "ba29fd6", 99 + "hash": "32cdee3aeac7ef986df47e0fff786b5f7471e55b", 100 + "short_hash": "32cdee3", 101 101 "author": "Ariel M. Lighty", 102 102 "date": "2025-12-25T13:22:32-05:00", 103 103 "message": "configure Netlify dev for monorepo with --filter flag\n\nFixed Netlify CLI monorepo detection issue by using --filter flag:\n- Updated root package.json scripts to use 'npx netlify-cli dev --filter @atlast/web'\n- Updated netlify.toml [dev] section to use npm with --prefix for framework command\n- Added monorepo development instructions to CLAUDE.md\n- Documented Windows Git Bash compatibility issue with netlify command\n\nSolution: Use 'npx netlify-cli dev --filter @atlast/web' to bypass monorepo\nproject selection prompt and specify which workspace package to run.\n\nDev server now runs successfully at http://localhost:8888 with all backend\nfunctions loaded.", 104 - "files_changed": 5 104 + "files_changed": 4 105 105 }, 106 106 { 107 107 "hash": "c3e7afad396d130791d801a85cbfc9643bcd6309",
+99
docs/graph-data.json
··· 3442 3442 "created_at": "2025-12-26T16:08:24.248208800-05:00", 3443 3443 "updated_at": "2025-12-26T16:08:24.248208800-05:00", 3444 3444 "metadata_json": "{\"branch\":\"master\",\"confidence\":95}" 3445 + }, 3446 + { 3447 + "id": 314, 3448 + "change_id": "6837403f-1e30-4a71-bcf5-71db0cac6afc", 3449 + "node_type": "goal", 3450 + "title": "Fix validation error and undefined localeCompare in extension flow", 3451 + "description": null, 3452 + "status": "pending", 3453 + "created_at": "2025-12-26T20:17:59.516959100-05:00", 3454 + "updated_at": "2025-12-26T20:17:59.516959100-05:00", 3455 + "metadata_json": "{\"branch\":\"master\",\"confidence\":90}" 3456 + }, 3457 + { 3458 + "id": 315, 3459 + "change_id": "a08d22fc-5970-4a5d-8454-4a1ef2efc7e4", 3460 + "node_type": "observation", 3461 + "title": "Two errors: 1) batch-search-actors gets null in usernames array, 2) Frontend localeCompare on undefined - likely wrong SearchResult structure", 3462 + "description": null, 3463 + "status": "pending", 3464 + "created_at": "2025-12-26T20:18:03.693879700-05:00", 3465 + "updated_at": "2025-12-26T20:18:03.693879700-05:00", 3466 + "metadata_json": "{\"branch\":\"master\",\"confidence\":95}" 3467 + }, 3468 + { 3469 + "id": 316, 3470 + "change_id": "58ef0c82-402c-4fff-8421-83c5417475b1", 3471 + "node_type": "action", 3472 + "title": "Fix SearchResult structure - sourceUser should be object not string", 3473 + "description": null, 3474 + "status": "pending", 3475 + "created_at": "2025-12-26T20:19:47.621459800-05:00", 3476 + "updated_at": "2025-12-26T20:19:47.621459800-05:00", 3477 + "metadata_json": "{\"branch\":\"master\",\"confidence\":95}" 3478 + }, 3479 + { 3480 + "id": 317, 3481 + "change_id": "3a24a4a2-b4d0-4629-a29b-b33994d50e75", 3482 + "node_type": "outcome", 3483 + "title": "Fixed SearchResult structure - sourceUser is now correct SourceUser object instead of string", 3484 + "description": null, 3485 + "status": "pending", 3486 + "created_at": "2025-12-26T20:20:22.507291300-05:00", 3487 + "updated_at": "2025-12-26T20:20:22.507291300-05:00", 3488 + "metadata_json": "{\"branch\":\"master\",\"confidence\":95}" 3445 3489 } 3446 3490 ], 3447 3491 "edges": [ ··· 6777 6821 "weight": 1.0, 6778 6822 "rationale": "Implementation complete", 6779 6823 "created_at": "2025-12-26T16:08:26.942822600-05:00" 6824 + }, 6825 + { 6826 + "id": 304, 6827 + "from_node_id": 313, 6828 + "to_node_id": 314, 6829 + "from_change_id": "5fae9da8-2a31-4f99-9686-7bfb28c443e8", 6830 + "to_change_id": "6837403f-1e30-4a71-bcf5-71db0cac6afc", 6831 + "edge_type": "leads_to", 6832 + "weight": 1.0, 6833 + "rationale": "New errors found", 6834 + "created_at": "2025-12-26T20:18:01.626612100-05:00" 6835 + }, 6836 + { 6837 + "id": 305, 6838 + "from_node_id": 314, 6839 + "to_node_id": 315, 6840 + "from_change_id": "6837403f-1e30-4a71-bcf5-71db0cac6afc", 6841 + "to_change_id": "a08d22fc-5970-4a5d-8454-4a1ef2efc7e4", 6842 + "edge_type": "leads_to", 6843 + "weight": 1.0, 6844 + "rationale": "Initial analysis", 6845 + "created_at": "2025-12-26T20:18:45.898518700-05:00" 6846 + }, 6847 + { 6848 + "id": 306, 6849 + "from_node_id": 315, 6850 + "to_node_id": 316, 6851 + "from_change_id": "a08d22fc-5970-4a5d-8454-4a1ef2efc7e4", 6852 + "to_change_id": "58ef0c82-402c-4fff-8421-83c5417475b1", 6853 + "edge_type": "leads_to", 6854 + "weight": 1.0, 6855 + "rationale": "Fix identified", 6856 + "created_at": "2025-12-26T20:19:50.103362300-05:00" 6857 + }, 6858 + { 6859 + "id": 307, 6860 + "from_node_id": 316, 6861 + "to_node_id": 317, 6862 + "from_change_id": "58ef0c82-402c-4fff-8421-83c5417475b1", 6863 + "to_change_id": "3a24a4a2-b4d0-4629-a29b-b33994d50e75", 6864 + "edge_type": "leads_to", 6865 + "weight": 1.0, 6866 + "rationale": "Implementation complete", 6867 + "created_at": "2025-12-26T20:20:24.693529800-05:00" 6868 + }, 6869 + { 6870 + "id": 308, 6871 + "from_node_id": 314, 6872 + "to_node_id": 317, 6873 + "from_change_id": "6837403f-1e30-4a71-bcf5-71db0cac6afc", 6874 + "to_change_id": "3a24a4a2-b4d0-4629-a29b-b33994d50e75", 6875 + "edge_type": "leads_to", 6876 + "weight": 1.0, 6877 + "rationale": "Goal achieved", 6878 + "created_at": "2025-12-26T20:20:26.885283800-05:00" 6780 6879 } 6781 6880 ] 6782 6881 }
+1 -1
packages/web/src/App.tsx
··· 185 185 const hasMatches = data.results.some(r => r.atprotoMatches.length > 0); 186 186 187 187 const loadedResults: SearchResult[] = data.results.map((result) => ({ 188 - sourceUser: result.sourceUser.username, 188 + sourceUser: result.sourceUser, // SourceUser object { username, date } 189 189 sourcePlatform: platform, 190 190 isSearching: !hasMatches, // Search if no matches exist yet 191 191 atprotoMatches: result.atprotoMatches || [],