Monorepo for Aesthetic.Computer
aesthetic.computer
1# ATProto Sync Implementation Progress
2
3## Completed Work ✅
4
5### 1. Unified Media Module (media-atproto.mjs)
6**Status:** ✅ Complete and tested
7
8**Features:**
9- Single source of truth for all ATProto media operations
10- Support for 5 media types: paintings, moods, pieces, kidlisp, tapes
11- Proper lexicon implementation with full schemas
12- Blob generation for paintings (thumbnails via /api/pixel)
13- Recording detection for paintings (checks ZIP existence)
14- Guest/anonymous user support
15
16**Lexicons Implemented:**
17- ✅ **Paintings**: `computer.aesthetic.painting`
18 - Fields: slug, code, imageUrl, acUrl, when, ref
19 - Blob: 512x512 thumbnail (via /api/pixel :contain mode)
20 - Optional: recordingUrl (if ZIP exists)
21
22- ✅ **Moods**: `computer.aesthetic.mood`
23 - Fields: text, when, ref
24 - No blob, no anonymous support
25
26- ✅ **Pieces**: `computer.aesthetic.piece`
27 - Fields: slug, when, ref
28 - No blob, supports anonymous
29
30- ✅ **Kidlisp**: `computer.aesthetic.kidlisp`
31 - Fields: code, source, acUrl, when, ref
32 - No blob, supports anonymous
33
34- ✅ **Tapes**: `computer.aesthetic.tape`
35 - Fields: slug, code, zipUrl, acUrl, when, ref
36 - Blob: MP4 video (TODO: conversion not implemented yet)
37 - Supports anonymous
38
39**URL Formats:**
40- Paintings: `https://aesthetic.computer/#${code}` (hash symbol)
41- Kidlisp: `https://aesthetic.computer/$${code}` (dollar symbol)
42- Tapes: `https://aesthetic.computer/!${code}` (exclamation symbol)
43
44**Media URLs:**
45- Paintings image: `https://aesthetic.computer/media/paintings/${code}`
46- Paintings recording: `https://aesthetic.computer/media/paintings/${code}.zip`
47- Tapes ZIP: Direct to DigitalOcean Spaces (no short code routing yet)
48
49### 2. Sync Script Optimizations (sync-atproto.mjs)
50**Status:** ✅ Complete
51
52**Performance:**
53- Parallel user processing: 50 users at a time
54- Batch creates: 10 concurrent (reduced from 50 to avoid rate limiting)
55- Batch MongoDB updates: bulkWrite with 100 ops per batch
56
57**Admin Tools:**
58- `--wipe=handle`: Delete all ATProto records + clear MongoDB rkeys
59- `--restore=handle`: Sync MongoDB → ATProto per user
60- Content type filtering: `--paintings-only`, `--moods-only`, etc.
61- Dry run support: Test without making changes
62
63**Anonymous/Guest Support:**
64- ✅ Wipe supports art.at.aesthetic.computer
65- ✅ Restore supports art.at.aesthetic.computer
66- ✅ Anonymous content query: `{ user: { $exists: false } }`
67- ✅ User content query: `{ user: userId }`
68
69### 3. Real-Time Sync Integration
70**Status:** ✅ Complete
71
72**Netlify Functions Updated:**
73- ✅ `track-media.mjs`: Auto-syncs paintings/pieces after MongoDB insert
74- ✅ `store-kidlisp.mjs`: Auto-syncs kidlisp after MongoDB insert
75- ✅ Background sync: Fire-and-forget, doesn't block response
76- ✅ MongoDB updates: Stores rkey after successful ATProto creation
77
78### 4. Testing Completed
79**Status:** ✅ Verified with jeffrey.at.aesthetic.computer
80
81**Test Results:**
82- ✅ Moods: 226/226 synced successfully
83- ✅ Pieces: 26/26 synced successfully
84- ✅ Kidlisp: 168/2220 synced (partial test, working)
85- ✅ Paintings: 19/395 synced (partial test, working with blobs)
86- ✅ Blob generation: Confirmed working (~9KB thumbnails)
87- ✅ Lexicons: All fields present and correct
88- ✅ Rate limiting: 10/batch stable, no errors
89
90## Current Sprint 🎯
91
92### Anonymous Tape Testing
93
94**Goal:** Test MP4 blob workflow with anonymous tapes
95
96**Plan:**
971. ✅ Update wipe/restore to support art.at.aesthetic.computer
982. ✅ Add anonymous content queries (no user field)
993. ⏳ Wipe art.at.aesthetic.computer ATProto data
1004. ⏳ Restore ONLY tapes (32 anonymous tapes)
1015. ⏳ Test MP4 blob generation workflow
1026. ⏳ Verify tape records in ATProto
103
104**Anonymous Tape Data:**
105- Total: 32 tapes
106- User: undefined (no user field)
107- Bucket: art-aesthetic-computer
108- Format: ZIP with frames + timing.json + metadata.json
109
110**Sample Tapes:**
111```json
112[
113 {
114 "code": "9Yo",
115 "slug": "OJZDQoBh",
116 "when": "2025-10-17T23:43:17.100Z",
117 "bucket": "art-aesthetic-computer"
118 },
119 {
120 "code": "Eue",
121 "slug": "lYrrBWh5",
122 "when": "2025-10-18T05:28:11.538Z",
123 "bucket": "art-aesthetic-computer"
124 }
125]
126```
127
128**Next Steps:**
1291. Run wipe: `node sync-atproto.mjs live --wipe=art.at.aesthetic.computer --tapes-only`
1302. Run restore: `node sync-atproto.mjs live --restore=art.at.aesthetic.computer --tapes-only`
1313. Observe MP4 blob workflow (will show TODO warning for now)
1324. Document what's needed for MP4 conversion
133
134## Pending Work 📋
135
136### 1. Tape MP4 Conversion
137**Status:** ⏳ Documented, not implemented
138
139**Plan:** See `TAPE-MP4-CONVERSION-PLAN.md`
140
141**Implementation Phases:**
142- Phase 1: ✅ Basic sync without MP4 (current state)
143- Phase 2: ⏳ Local MP4 conversion using ffmpeg
144- Phase 3: ⏳ Production deployment
145
146**Technical Requirements:**
147- Download ZIP from storage
148- Extract frames and timing.json
149- Convert to MP4 using ffmpeg
150- Upload MP4 blob to ATProto
151- Include video blob in tape record
152
153### 2. Full User Sync
154**Status:** ⏳ Ready to run after tape testing
155
156**jeffrey.at.aesthetic.computer:**
157- 395 paintings (with blobs + recordings)
158- 226 moods
159- 26 pieces
160- 2220 kidlisp
161- 0 tapes (user tapes don't exist yet)
162
163**Estimated Time:**
164- Paintings: ~40 minutes (blob generation + rate limiting)
165- Moods: ~2 minutes
166- Pieces: ~1 minute
167- Kidlisp: ~20 minutes
168- Total: ~1 hour for full sync
169
170### 3. Production Rollout
171**Status:** ⏳ After testing complete
172
173**Tasks:**
174- [ ] Commit all changes to main
175- [ ] Full sync for all 4,201 users
176- [ ] Monitor logs for errors
177- [ ] Verify data quality in ATProto
178- [ ] Deploy Netlify function updates
179
180**Files to Commit:**
181- NEW: `media-atproto.mjs` (330+ lines)
182- NEW: `ATPROTO-SYNC-CONSOLIDATION-PLAN.md`
183- NEW: `ATPROTO-SYNC-PROGRESS.md`
184- NEW: `TAPE-MP4-CONVERSION-PLAN.md`
185- MODIFIED: `sync-atproto.mjs` (refactored to use media-atproto)
186- MODIFIED: `track-media.mjs` (real-time sync)
187- MODIFIED: `store-kidlisp.mjs` (real-time sync)
188- MODIFIED: `tape-atproto.mjs` (added acUrl)
189
190### 4. Deprecation
191**Status:** ⏳ After commit
192
193**Files to Remove:**
194- `painting-atproto.mjs` (logic moved to media-atproto.mjs)
195
196## Architecture Decisions 📐
197
198### Why Unified Module?
199- **Before**: Duplicate logic in painting-atproto.mjs, sync-atproto.mjs, Netlify functions
200- **After**: Single source of truth in media-atproto.mjs
201- **Benefits**:
202 - Consistent lexicons across all entry points
203 - Easier to maintain and extend
204 - Single place to update ATProto logic
205
206### Why Short Code URLs?
207- **Permanent**: Codes don't change even if handles change
208- **Shareable**: Short, memorable codes (#abc, $def, !ghi)
209- **Routable**: Edge function handles redirects to storage
210- **ATProto-friendly**: Clean URLs for external apps
211
212### Why Separate Tape Module?
213- **Before**: Considered adding tapes to unified module
214- **After**: Tapes keep separate tape-atproto.mjs due to async MP4 workflow
215- **Rationale**: MP4 conversion is async webhook-driven, different from other media
216
217### Why Local MP4 Conversion for Sync?
218- **Flexibility**: Control quality and conversion parameters
219- **Testing**: Easier to iterate and debug locally
220- **Deployment**: Can be deployed incrementally
221- **Async**: Production can use webhook service if needed
222
223## Metrics 📊
224
225### Performance
226- Users processed: ~50/batch
227- Creates per batch: 10 concurrent
228- Throughput: ~100 records/minute
229- Rate limit avoidance: 10/batch stable
230
231### Data Quality
232- Blob sizes: 7-12KB thumbnails
233- Lexicon compliance: 100%
234- Recording detection: Working (HEAD request to ZIP)
235- URL formats: All using short codes
236
237### Coverage
238- Total users: 4,201
239- Users with ATProto accounts: TBD
240- Anonymous tapes: 32
241- Authenticated tapes: 3 (jeffrey has 0)
242
243## Known Issues 🐛
244
245### 1. MP4 Conversion Not Implemented
246**Status:** Expected, documented
247**Workaround:** Tapes sync without video blob for now
248**Fix:** Implement tape-to-mp4.mjs module (see TAPE-MP4-CONVERSION-PLAN.md)
249
250### 2. No Short Code Routing for Tape ZIPs
251**Status:** Known limitation
252**Impact:** Tape zipUrl uses direct DigitalOcean URL
253**Fix:** Add `/media/tapes/CODE` routing to edge function
254
255### 3. Rate Limiting at 50 Concurrent
256**Status:** Fixed
257**Solution:** Reduced to 10 concurrent creates
258**Result:** Stable, no more "Internal Server Error"
259
260## Next Actions 🎬
261
262**Immediate (Today):**
2631. ✅ Document progress
2642. ⏳ Wipe art.at.aesthetic.computer
2653. ⏳ Restore tapes only
2664. ⏳ Test and document MP4 workflow needs
267
268**Short Term (This Week):**
2691. Implement tape MP4 conversion
2702. Full sync for jeffrey.at.aesthetic.computer
2713. Verify all data in ATProto
2724. Commit changes to main
273
274**Long Term (Next Week):**
2751. Full sync for all 4,201 users
2762. Monitor production performance
2773. Optimize if needed
2784. Document lessons learned