ATlast — you'll never need to find your favorites on another platform again. Find your favs in the ATmosphere.
atproto
1# CLAUDE.md 2 3This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. 4 5# Project Instructions 6 7## Decision Graph Workflow 8 9**THIS IS MANDATORY. Log decisions IN REAL-TIME, not retroactively.** 10 11### The Core Rule 12 13``` 14BEFORE you do something -> Log what you're ABOUT to do 15AFTER it succeeds/fails -> Log the outcome 16CONNECT immediately -> Link every node to its parent 17AUDIT regularly -> Check for missing connections 18``` 19 20### Behavioral Triggers - MUST LOG WHEN: 21 22| Trigger | Log Type | Example | 23|---------|----------|---------| 24| User asks for a new feature | `goal` **with -p** | "Add dark mode" | 25| Choosing between approaches | `decision` | "Choose state management" | 26| About to write/edit code | `action` | "Implementing Redux store" | 27| Something worked or failed | `outcome` | "Redux integration successful" | 28| Notice something interesting | `observation` | "Existing code uses hooks" | 29 30### CRITICAL: Capture VERBATIM User Prompts 31 32**Prompts must be the EXACT user message, not a summary.** When a user request triggers new work, capture their full message word-for-word. 33 34**BAD - summaries are useless for context recovery:** 35```bash 36# DON'T DO THIS - this is a summary, not a prompt 37deciduous add goal "Add auth" -p "User asked: add login to the app" 38``` 39 40**GOOD - verbatim prompts enable full context recovery:** 41```bash 42# Use --prompt-stdin for multi-line prompts 43deciduous add goal "Add auth" -c 90 --prompt-stdin << 'EOF' 44I need to add user authentication to the app. Users should be able to sign up 45with email/password, and we need OAuth support for Google and GitHub. The auth 46should use JWT tokens with refresh token rotation. 47EOF 48 49# Or use the prompt command to update existing nodes 50deciduous prompt 42 << 'EOF' 51The full verbatim user message goes here... 52EOF 53``` 54 55**When to capture prompts:** 56- Root `goal` nodes: YES - the FULL original request 57- Major direction changes: YES - when user redirects the work 58- Routine downstream nodes: NO - they inherit context via edges 59 60**Updating prompts on existing nodes:** 61```bash 62deciduous prompt <node_id> "full verbatim prompt here" 63cat prompt.txt | deciduous prompt <node_id> # Multi-line from stdin 64``` 65 66Prompts are viewable in the TUI detail panel (`deciduous tui`) and web viewer. 67 68### ⚠️ CRITICAL: Maintain Connections 69 70**The graph's value is in its CONNECTIONS, not just nodes.** 71 72| When you create... | IMMEDIATELY link to... | 73|-------------------|------------------------| 74| `outcome` | The action/goal it resolves | 75| `action` | The goal/decision that spawned it | 76| `option` | Its parent decision | 77| `observation` | Related goal/action | 78 79**Root `goal` nodes are the ONLY valid orphans.** 80 81### Node Lifecycle Management 82 83**Every node has a lifecycle. Update status in REAL-TIME:** 84 85```bash 86# 1. Create node (defaults to 'pending') 87deciduous add action "Implementing feature X" -c 85 88 89# 2. IMMEDIATELY link to parent (before doing anything else) 90deciduous link <parent_id> <new_node_id> -r "Reason for connection" 91 92# 3. Mark as in_progress BEFORE starting work 93deciduous status <node_id> in_progress 94 95# 4. Do the work... 96 97# 5. Mark as completed IMMEDIATELY after work finishes 98deciduous status <node_id> completed 99``` 100 101**Status Transitions:** 102- `pending` → Default state when created 103- `in_progress` → Mark BEFORE starting work (only ONE at a time) 104- `completed` → Mark IMMEDIATELY when done (proven by git commit, test pass, etc.) 105 106**CRITICAL RULES:** 107- ✅ Link nodes IMMEDIATELY after creation (same command sequence) 108- ✅ Update status to `completed` as soon as work is done 109- ✅ Only ONE node should be `in_progress` at a time 110- ✅ Verify link exists before moving on (check `deciduous edges`) 111- ❌ NEVER leave completed work marked as `pending` 112- ❌ NEVER create orphan nodes (except root goals) 113- ❌ NEVER batch status updates - update immediately 114 115**Verification Workflow:** 116```bash 117# After creating and linking a node, verify: 118deciduous edges | grep <new_node_id> # Should show incoming edge 119deciduous nodes | grep <new_node_id> # Check status is correct 120``` 121 122**Common Mistakes That Break the Graph:** 123 1241. **Creating nodes without linking** → Orphans 125 ```bash 126 # WRONG 127 deciduous add action "Fix bug" -c 85 128 # (forget to link, move on to next task) 129 130 # RIGHT 131 deciduous add action "Fix bug" -c 85 132 deciduous link 42 43 -r "Action to resolve goal #42" 133 ``` 134 1352. **Leaving nodes as "pending" after work completes** → Stale status 136 ```bash 137 # WRONG 138 git commit -m "fix: bug fixed" 139 # (forget to update node status) 140 141 # RIGHT 142 git commit -m "fix: bug fixed" 143 deciduous status 43 completed 144 ``` 145 1463. **Batch-creating multiple nodes before linking** → Connection gaps 147 ```bash 148 # WRONG 149 deciduous add action "Task 1" -c 85 150 deciduous add action "Task 2" -c 85 151 deciduous add action "Task 3" -c 85 152 # (now have to remember all IDs to link) 153 154 # RIGHT 155 deciduous add action "Task 1" -c 85 156 deciduous link 42 43 -r "First task" 157 deciduous add action "Task 2" -c 85 158 deciduous link 42 44 -r "Second task" 159 ``` 160 1614. **Not regenerating parent list during orphan checks** → False positives 162 ```bash 163 # WRONG 164 # (generate parent list once) 165 deciduous link X Y -r "fix orphan" 166 # (check orphans with stale parent list) 167 168 # RIGHT 169 deciduous link X Y -r "fix orphan" 170 # Regenerate parent list before checking again 171 deciduous edges | tail -n+3 | awk '{print $3}' | sort -u > /tmp/has_parent.txt 172 ``` 173 174### Quick Commands 175 176```bash 177deciduous add goal "Title" -c 90 -p "User's original request" 178deciduous add action "Title" -c 85 179deciduous link FROM TO -r "reason" # DO THIS IMMEDIATELY! 180deciduous serve # View live (auto-refreshes every 30s) 181deciduous sync # Export for static hosting 182 183# Metadata flags 184# -c, --confidence 0-100 Confidence level 185# -p, --prompt "..." Store the user prompt (use when semantically meaningful) 186# -f, --files "a.rs,b.rs" Associate files 187# -b, --branch <name> Git branch (auto-detected) 188# --commit <hash|HEAD> Link to git commit (use HEAD for current commit) 189 190# Branch filtering 191deciduous nodes --branch main 192deciduous nodes -b feature-auth 193``` 194 195### ⚠️ CRITICAL: Link Commits to Actions/Outcomes 196 197**After every git commit, link it to the decision graph!** 198 199```bash 200git commit -m "feat: add auth" 201deciduous add action "Implemented auth" -c 90 --commit HEAD 202deciduous link <goal_id> <action_id> -r "Implementation" 203``` 204 205The `--commit HEAD` flag captures the commit hash and links it to the node. The web viewer will show commit messages, authors, and dates. 206 207### Git Commit Message Format 208 209**Keep commit messages clean and concise:** 210- NO "Generated with Claude Code" or similar by-lines 211- NO "Co-Authored-By:" lines 212- NO "Files updated:" sections or file lists 213- Use concise summary line describing the change 214- Add additional details if needed, but keep it focused 215 216**CRITICAL: Commit separate concerns separately** 217- Each commit should address ONE specific fix or feature 218- DO NOT bundle multiple unrelated changes into a single commit 219- If fixing multiple bugs, create separate commits for each bug fix 220- Example: Login typeahead fix, card spacing fix, and toast refactor = 3 commits 221 222**Example:** 223```bash 224git commit -m "remove deprecated 'followed' field and cleanup codebase 225 226Removed backward compatibility code for deprecated 'followed' field: 227- Removed from AtprotoMatch type 228- Updated 6 files to use only followStatus Record 229- Replaced simple boolean check with multi-lexicon support 230- Database schema preserved (no migration needed) 231 232Also removed empty 'nul' file created accidentally." 233``` 234 235### Git History & Deployment 236 237**CRITICAL: Include decision graph in code commits** 238 239Always run `deciduous sync` BEFORE committing code changes, then include the graph updates in the same commit: 240 241```bash 242# After making code changes and adding decision nodes: 243deciduous sync # Export graph 244git add src/your-file.ts docs/ # Stage code + graph 245git commit -m "your change" # Single commit with both 246``` 247 248**DO NOT create separate commits for graph updates.** The decision graph is part of the code history and should be committed together. 249 250```bash 251# Export graph AND git history for web viewer 252deciduous sync 253 254# This creates: 255# - docs/graph-data.json (decision graph) 256# - docs/git-history.json (commit info for linked nodes) 257``` 258 259To deploy to GitHub Pages: 2601. `deciduous sync` to export 2612. Push to GitHub 2623. Settings > Pages > Deploy from branch > /docs folder 263 264Your graph will be live at `https://<user>.github.io/<repo>/` 265 266### Branch-Based Grouping 267 268Nodes are auto-tagged with the current git branch. Configure in `.deciduous/config.toml`: 269```toml 270[branch] 271main_branches = ["main", "master"] 272auto_detect = true 273``` 274 275### Audit Checklist (Before Every Sync) 276 277Run these checks before `deciduous sync`: 278 2791. **Connection integrity**: Does every non-goal node have a parent? 280 ```bash 281 deciduous edges | tail -n+3 | awk '{print $3}' | sort -u > /tmp/has_parent.txt 282 deciduous nodes | tail -n+3 | awk '{print $1}' > /tmp/all_nodes.txt 283 while read id; do grep -q "^$id$" /tmp/has_parent.txt || echo "CHECK: $id"; done < /tmp/all_nodes.txt 284 # Only root goals should appear 285 ``` 286 2872. **Status accuracy**: Are completed nodes marked `completed`? 288 ```bash 289 deciduous nodes | grep pending 290 # Review: is this work actually still pending, or is it done? 291 ``` 292 2933. **Active work**: Is there exactly ONE `in_progress` node? 294 ```bash 295 deciduous nodes | grep in_progress 296 # Should see 0-1 nodes, not multiple 297 ``` 298 2994. **Logical flow**: Does every outcome link back to what caused it? 300 - `outcome` → `action` or `goal` 301 - `action` → `goal` or `decision` 302 - `observation` → related `goal` or `action` 303 304### Session Start Checklist 305 306```bash 307deciduous nodes # What decisions exist? 308deciduous edges # How are they connected? Any gaps? 309git status # Current state 310``` 311 312### Multi-User Sync 313 314Share decisions across teammates: 315 316```bash 317# Export your branch's decisions 318deciduous diff export --branch feature-x -o .deciduous/patches/my-feature.json 319 320# Apply patches from teammates (idempotent) 321deciduous diff apply .deciduous/patches/*.json 322 323# Preview before applying 324deciduous diff apply --dry-run .deciduous/patches/teammate.json 325``` 326 327PR workflow: Export patch → commit patch file → PR → teammates apply. 328 329### API Trace Capture 330 331Capture Claude API traffic to correlate decisions with actual API work: 332 333```bash 334# Run Claude through the deciduous proxy 335deciduous proxy -- claude 336 337# View traces in TUI (press 't' for Trace view) 338deciduous tui 339 340# View traces in web viewer (click "Traces" tab) 341deciduous serve 342``` 343 344**Auto-linking**: When running through `deciduous proxy`, any `deciduous add` commands automatically link to the active API span. You'll see output like: 345 346``` 347Created action #42 "Implementing auth" [traced: span #7] 348``` 349 350This lets you see exactly which API calls produced which decisions - perfect for "vibe coding" visibility. 351 352**Trace commands:** 353```bash 354deciduous trace sessions # List all sessions 355deciduous trace spans <session_id> # List spans in a session 356deciduous trace link <session_id> <node_id> # Manual linking 357deciduous trace prune --days 30 # Cleanup old traces 358``` 359 360## Project Overview 361 362ATlast is a web application that helps users find their followed accounts from other social platforms (TikTok, Instagram, Twitter/X) on Bluesky/AT Protocol. Users upload their data export files, which are parsed for usernames, then searched in the AT Protocol network. 363 364## Development Commands 365 366### Monorepo Structure 367 368This project uses pnpm workspaces with three packages: 369- `packages/web` - React frontend 370- `packages/functions` - Netlify serverless functions 371- `packages/shared` - Shared TypeScript types 372 373### Local Development 374 375**IMPORTANT**: Due to Netlify CLI monorepo detection, you must use the `--filter` flag: 376 377```bash 378# Mock mode (frontend only, no backend/OAuth/database) 379pnpm run dev:mock 380 381# Full mode (with backend, OAuth, database) - RECOMMENDED 382npx netlify-cli dev --filter @atlast/web 383 384# Alternative: Update root package.json scripts to use npx netlify-cli 385pnpm run dev # If scripts are updated 386 387# Build for production 388pnpm run build 389 390# Initialize local database 391pnpm run init-db 392 393# Generate encryption keys for OAuth 394pnpm run generate-key 395``` 396 397**Note**: On Windows, `netlify` command may not work in Git Bash. Use `npx netlify-cli` instead, or run from Windows CMD/PowerShell. 398 399### Environment Configuration 400 401Two development modes supported: 402 403**Mock Mode** (.env.mock): 404- Set `VITE_LOCAL_MOCK=true` 405- No database or OAuth required 406- Uses MockApiAdapter for fake data 407 408**Full Mode** (.env): 409- Set `VITE_LOCAL_MOCK=false` 410- Requires PostgreSQL (local or Neon) 411- Requires OAuth keys and configuration 412- Must use `http://127.0.0.1:8888` (NOT localhost) for OAuth to work 413- See CONTRIBUTING.md for detailed setup 414 415## Architecture Overview 416 417### Frontend (React + TypeScript + Vite) 418 419**Core Structure:** 420- `src/pages/` - Page components (Login, Upload, Results, etc.) 421- `src/components/` - Reusable UI components 422- `src/lib/parsers/` - File parsing logic for different platforms 423- `src/lib/api/` - API client with adapter pattern (Real vs Mock) 424- `src/contexts/` - React contexts (SettingsContext for theme/preferences) 425- `src/hooks/` - Custom React hooks 426 427**API Client Pattern:** 428The app uses an adapter pattern for the API layer: 429- `src/lib/api/client.ts` - Factory that returns Real or Mock adapter based on ENV.IS_LOCAL_MOCK 430- `src/lib/api/adapters/RealApiAdapter.ts` - Calls Netlify Functions 431- `src/lib/api/adapters/MockApiAdapter.ts` - Returns fake data for frontend development 432- `src/lib/api/IApiClient.ts` - Interface both adapters implement 433 434**Platform Parsers:** 435- `src/lib/parsers/platformDefinitions.ts` - Defines parsing rules for each platform (Instagram, TikTok, etc.) 436- `src/lib/parsers/fileExtractor.ts` - Handles ZIP file uploads and extracts usernames 437- `src/lib/parsers/parserLogic.ts` - Implements extraction logic (HTML regex, JSON path traversal, TEXT regex) 438 439Each platform has multiple ParseRule entries defining: 440- `zipPath` - Location inside ZIP archive 441- `format` - "HTML" | "TEXT" | "JSON" 442- `rule` - Regex pattern string or JSON key path array 443 444### Backend (Netlify Functions) 445 446**Function Structure:** 447``` 448netlify/functions/ 449├── core/ 450│ ├── middleware/ # Auth, error handling, session security 451│ ├── types/ # Shared types 452│ ├── errors/ # Custom error classes 453│ └── config/ # Configuration 454├── infrastructure/ 455│ ├── oauth/ # OAuth client factory, stores 456│ ├── database/ # Database connection, service layer 457│ ├── cache/ # Caching utilities 458│ └── lexicons/ # AT Protocol lexicons 459├── services/ # Business logic (SessionService, FollowService, etc.) 460├── repositories/ # Data access layer 461└── utils/ # Shared utilities 462``` 463 464**Key Functions:** 465- `oauth-start.ts` / `oauth-callback.ts` - AT Protocol OAuth flow 466- `session.ts` - Session management and validation 467- `batch-search-actors.ts` - Searches multiple usernames on Bluesky (includes ranking algorithm) 468- `check-follow-status.ts` - Checks if user follows specific DIDs 469- `batch-follow-users.ts` - Bulk follow operations 470- `save-results.ts` - Persists search results to database 471- `get-uploads.ts` - Retrieves user's upload history 472 473**Authentication Pattern:** 474All protected endpoints use: 4751. `withAuthErrorHandling()` middleware wrapper 4762. `AuthenticatedHandler` type (provides `context.sessionId`, `context.did`, `context.event`) 4773. `SessionService.getAgentForSession()` to get authenticated AT Protocol agent 478 479**Database:** 480- PostgreSQL via Neon serverless 481- Accessed through `DatabaseService` (infrastructure/database/) 482- Repositories pattern for data access (repositories/) 483 484**OAuth:** 485- AT Protocol OAuth using `@atproto/oauth-client-node` 486- OAuth client factory creates client with session/state stores 487- Private key (ES256) stored in `OAUTH_PRIVATE_KEY` env var 488- Public JWK served at `/jwks` endpoint 489- Must use `127.0.0.1` (not localhost) for local OAuth redirects 490 491### UI/Styling 492 493**Tailwind CSS with dual theme support:** 494- Light mode: purple/cyan color scheme 495- Dark mode: cyan/purple inverted 496- All components use `dark:` variant classes 497 498**Color System (from CONTRIBUTING.md):** 499- Text: purple-950/cyan-50 (primary), purple-750/cyan-250 (secondary) 500- Borders: cyan-500/purple-500 with opacity variants 501- Buttons: orange-600 (primary CTA), slate-600/700 (secondary) 502- Backgrounds: white/slate-900 (primary), purple-50/slate-900 (secondary) 503- Selected states: cyan-50 border-cyan-500 / purple-950/30 border-purple-500 504- Accents: orange-500/amber-500 (badges, progress) 505 506**Key Patterns:** 507- Mobile-first responsive design 508- List virtualization with `@tanstack/react-virtual` for large result sets 509- Code splitting and lazy loading for pages 510- Error boundaries throughout the app 511- Loading skeletons for async operations 512 513### Search & Matching Algorithm 514 515**Username Matching (batch-search-actors.ts):** 516The search uses a scoring system (0-100): 5171. Exact handle match (before first dot): 100 5182. Exact full handle match: 90 5193. Exact display name match: 80 5204. Partial handle match (contains): 60 5215. Partial full handle match: 50 5226. Partial display name match: 40 5237. Reverse partial match: 30 524 525All comparisons use normalized strings (lowercase, no special chars). 526Returns top 5 ranked results per username. 527 528**Result Enrichment:** 529- Fetches profiles in batches of 25 for post/follower counts 530- Checks follow status using custom lexicons (default: `app.bsky.graph.follow`) 531- Attaches follow status to each actor result 532 533## Key Technical Details 534 535### AT Protocol Integration 536- Uses `@atproto/api` for Bluesky API interactions 537- Uses `@atcute/identity-resolver` for DID resolution 538- Supports custom lexicons for experimental features 539- OAuth flow follows AT Protocol OAuth spec 540 541### File Upload Flow 5421. User uploads ZIP file (Instagram/TikTok data export) 5432. `fileExtractor.ts` reads ZIP in browser (using JSZip) 5443. Matches file paths to platform rules from `platformDefinitions.ts` 5454. `parserLogic.ts` extracts usernames (regex for HTML/TEXT, path traversal for JSON) 5465. Deduplicates and returns username list 5476. Frontend calls `/batch-search-actors` with username batches (max 50) 5487. Results stored in database via `/save-results` 549 550### Session Management 551- Session IDs stored in httpOnly cookies 552- Sessions linked to encrypted OAuth state stores 553- Session validation middleware checks database and expiry 554- Sessions tied to specific DIDs (user accounts) 555 556### Deployment 557- Hosted on Netlify 558- Static frontend built with Vite 559- Serverless functions for backend 560- Database on Neon (PostgreSQL) 561- OAuth redirects configured in netlify.toml 562 563## Adding New Features 564 565### Adding a New Social Platform 5661. Add parsing rules to `src/lib/parsers/platformDefinitions.ts`: 567```typescript 568export const PLATFORM_RULES: Record<string, ParseRule[]> = { 569 newplatform: [ 570 { 571 zipPath: "path/in/zip/file.json", 572 format: "JSON", 573 rule: ["key", "path", "to", "username"], 574 }, 575 ], 576}; 577``` 5782. Test with real data export from that platform 5793. Update UI in platform selection components 580 581### Adding a New API Endpoint 5821. Create function in `netlify/functions/your-endpoint.ts` 5832. Use `withAuthErrorHandling()` for protected endpoints 5843. Implement `AuthenticatedHandler` type 5854. Add method to `IApiClient.ts` interface 5865. Implement in both `RealApiAdapter.ts` and `MockApiAdapter.ts` 5876. Use via `apiClient.yourMethod()` in components 588 589### Adding Database Models 5901. Create migration script in `scripts/` 5912. Run against local database 5923. Update repository in `netlify/functions/repositories/` 5934. Update DatabaseService if needed 594 595## Important Notes 596 597- **OAuth Localhost Issue**: Must use `127.0.0.1:8888` not `localhost:8888` for local OAuth to work 598- **Batch Limits**: Search endpoint limited to 50 usernames per request 599- **Profile Fetching**: Batched in groups of 25 to avoid rate limits 600- **Normalization**: All username comparisons use lowercase + special char removal 601- **Security**: CSP headers configured in netlify.toml, session security middleware prevents CSRF 602- **Error Handling**: Custom error classes (ValidationError, AuthenticationError, etc.) with proper HTTP status codes