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