2026-03-17-221109-Claude-Opus-4-6-requirements.md 8.7 KB

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 scps (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