# Deliberation: requirements — Claude Opus 4.6 **Date**: 2026-03-17 22:11 **Model**: Claude Opus 4.6 **Type**: requirements **Artifact**: ## Shaped Brief: Cloud Upload from MixBoard ### Problem MixBoard can browse and stream the chad-mus --- # Requirements Critique: Cloud Upload from MixBoard ## 1. Do the acceptance criteria actually solve the user's problem? **Mostly yes, but with a critical gap.** The real user problem is: "I found music on my Mac and want it in my cloud library without SSH." The shaped brief addresses this directly. However: **The async processing gap is the biggest unsolved UX problem.** Acceptance criterion #2 says files are "automatically processed," and #3 says "upload progress is visible." But what happens *between* upload completion and beets finishing? Beets import against MusicBrainz can take 10-30 seconds per file. The brief never addresses: - Does the user see "upload complete" and then... nothing happens for 30 seconds? - What if beets *rejects* the file (no MusicBrainz match, ambiguous match)? - What if beets *renames/moves* the file in a way that changes its metadata? **This is the actual hard UX problem and it's not in the acceptance criteria.** You need a criterion like: "User can see the status of server-side processing (queued → importing → imported/failed) after upload completes." ## 2. Is the dedup approach right? **No. It's solving a problem that barely exists, and solving it badly.** - **This is a single-user personal project.** How often is one person uploading the same file twice? This is a proxy problem — it feels like good engineering but doesn't serve a real user need in v1. - **The approach is unreliable.** Client-sent metadata (artist+title+duration±2s) is trivially wrong for untagged files, files with inconsistent tagging, or different versions/remasters. You'll get both false positives (blocking legitimate uploads of different versions) and false negatives (letting through actual dupes with slightly different tags). - **It adds complexity to both client and server** for v1: the client now needs to extract and send metadata, the server needs to query before accepting. **Recommendation:** Cut dedup entirely from v1. Accept the upload blindly. If a true duplicate lands on disk, the user will see it in their library and can delete it manually — they're the only user. Dedup is a v2 concern, and when you do it, do it right with audio fingerprinting (chromaprint/acoustid), not metadata matching. ## 3. What UX questions are unanswered? - **What file formats are accepted?** The brief mentions FLAC but the file picker needs a filter. MP3? AAC? OGG? WAV? What does beets actually handle? What does chad-music's taglib parser support? - **What happens when beets can't match?** `beet import --quiet` with no match — does it skip? Does it import as-is? This determines whether the user's upload "succeeds" but the track never appears. - **Where does the upload button live relative to the Browse Panel?** The brief says "toolbar or context menu" — these are very different interaction models. Context menu on *what*? Local tracks aren't shown in MixBoard currently (it's a cloud player). If you're uploading from a file picker, the context menu option makes no sense. - **Can the user cancel an in-progress upload?** Not mentioned anywhere. - **What's the feedback when upload succeeds but beets hasn't finished?** See point #1 above. - **What if the user closes MixBoard during upload?** Non-goal says no persistence, but does the upload just silently die? Should there be a "uploads in progress" warning on quit? ## 4. What's the minimal viable version that would be useful? **Single file upload, blind accept, synchronous beets, manual refresh.** 1. Button in toolbar → NSOpenPanel (single file) → POST raw binary to `/api/upload` with filename header 2. Server saves to staging → runs `beet import` synchronously (yes, blocking — it's a single user, they can wait 15 seconds) 3. Server runs rescan → returns success/failure 4. Client shows spinner → "Done! Refresh your library to see it." with a refresh button 5. No dedup, no multi-file, no progress percentage (just a spinner) This is probably a weekend of work. The shaped brief as written is 2-3 weeks. **For a personal project between two friends, ship the simple thing and iterate.** ## 5. Are there simpler alternatives? - **rsync + beets trigger:** A shell script on the server that watches a directory. MixBoard just `scp`s (or POSTs raw) to that directory. Beets + rescan runs via fswatch/inotify. Zero server code changes. **[CHECK THIS]** — verify that SBCL/Woo can be bypassed entirely for the upload path. - **Use the existing Telegram bot path:** If chatikbot already handles imports, can MixBoard just send to the same endpoint/queue? Don't build a second import pipeline. - **Nginx upload module:** Let nginx handle the multipart upload and write to disk directly, then hit a Lisp endpoint to trigger beets+rescan. Avoids the entire "can Woo handle 100MB multipart" question. **This is probably the right answer for the server architecture concern.** ## Tension and Contradictions - **AC #1 says "one or more files"** but multi-file upload dramatically increases complexity (progress per file, partial failure handling, queue management). Prior reviews correctly flagged this as scope violation. - **AC #4 (dedup) contradicts the non-goal of keeping v1 simple.** Dedup is feature #4 out of 5 acceptance criteria — that's a lot of weight for something that barely matters to a single user. - **"Beets is blocking" + "progress is visible"** creates tension. If beets blocks, you need async job tracking, which is a significant server-side addition. If you accept synchronous beets (viable for single user), progress becomes trivial (spinner). ## Hallucination Check - SF Symbol names not referenced in this artifact — N/A. - `beet import --move --quiet` — **[CHECK THIS]**: `--quiet` in beets may not do what's expected. Beets' `--quiet` flag suppresses prompts but may *skip* non-confident matches rather than importing them. You may want `--quiet --timid` or just `--quiet` with a known config. Verify actual beets behavior on no-match. - Track ID = MD5(file path), Album ID = MD5(artist + year + title) — **[UNVERIFIED]** based on prior review context; plausible for chad-music but should be confirmed against actual source. - "chatikbot Telegram plugin" — **[UNVERIFIED]** — referenced as existing import path but not verifiable from provided context. - Woo described as "libev" based — this is correct; Woo uses libev for async I/O. - `with-user` macro for auth — **[UNVERIFIED]** — plausible but source-dependent. --- ## Bottom Line The brief is well-structured but **over-scoped for v1** and **under-specified on the hard problem** (async processing status). Cut dedup, cut multi-file, decide whether beets runs sync or async (sync is fine for single user), and spend the complexity budget on handling beets failures gracefully — that's what will actually frustrate the user.
Session State **Blocking findings**: - Async processing status completely unaddressed in acceptance criteria — user has no way to know if beets succeeded or failed after upload completes - Beets `--quiet` behavior on no-match is unverified and could cause silent data loss (upload "succeeds" but track never appears) - Multi-file (AC #1) + dedup (AC #4) + async beets + progress tracking = scope violation for v1 appetite **Advisory findings**: - Cut dedup from v1 — wrong problem for single-user, unreliable implementation as designed - Cut multi-file from v1 — single file dramatically simplifies every layer - Consider synchronous beets for v1 (single user can wait 15s) - Consider nginx upload module to sidestep Woo/Clack multipart concerns - Investigate reusing chatikbot import pipeline instead of building a second one - Define accepted file formats explicitly - Add cancel-upload and quit-during-upload behavior - Clarify upload button placement (toolbar button + NSOpenPanel is the obvious choice) **Decisions**: - Recommend redefining v1 as: single file, blind upload, synchronous processing, spinner, success/failure result - Defer dedup, multi-file, async job tracking to v2 **Open Questions**: - What does `beet import --quiet` do on no MusicBrainz match? Skip or import as-is? - Can the chatikbot import path be reused? - Does nginx upload module eliminate the need for Clack multipart handling? - Who owns file permissions on the staging/music directories? - What formats does the existing chad-music taglib parser support? **Key Entities**: `beet import --quiet`, `/api/upload`, `ChadMusicAPIClient`, `NSOpenPanel`, `chad-music`, `MixBoard`, `Woo`, `Clack`, `SBCL`, `nginx`, `chatikbot`, `with-user`, `rescan()`, `beets`, staging directory