2026-03-18-121408-Claude-Opus-4-6-requirements.md 9.9 KB

Deliberation: requirements — Claude Opus 4.6

Date: 2026-03-18 12:14 Model: Claude Opus 4.6 Type: requirements Artifact: ## Draft: Cloud Download for DAW Export ### Problem MixBoard can browse, stream, and add music to p


Product Strategy Review: Cloud Download for DAW Export

Are We Solving the Right Problem?

Partially yes, but the scope is confused. The core problem is clear and real: "I can't export my playlist to a DAW because cloud tracks get silently skipped." That's a legitimate, frustrating user problem. But the requirements document is solving two different problems and pretending they're one:

  1. Problem A: Cloud tracks break DAW export (export blocker)
  2. Problem B: Users want offline access to cloud tracks (library management)

These have different priorities, different UX expectations, and different architectural implications. Flow B (manual download for offline playback) is a separate feature that's been smuggled into scope. Prior reviews already flagged this as a scope violation for a "Small" appetite. I agree — ship Flow A, defer Flow B.


What Would a Real User Actually Care About?

A user who hits "Export to Audition" and gets a session with missing tracks cares about one thing: "Give me my complete session." They do NOT care about:

  • Whether the download happened "on-demand" vs "pre-flight"
  • Whether a DownloadService mirrors UploadService patterns
  • Whether track.filePath gets mutated

They DO care about things this spec doesn't address:

  1. "How long will this take?" — The spec says "progress is visible" but doesn't address the elephant in the room: a playlist with 40 cloud tracks could take 10+ minutes to download before export even starts. What's the UX for this? A progress sheet? Can they keep working? This is arguably the most important UX question and it's completely unspecified.

  2. "What if my export is mostly done and one track fails?" — The silent-skip behavior is terrible, but fail-fast is also terrible if you're 35/40 tracks in. Users would want: download everything possible, tell me exactly what failed, let me export what succeeded. The spec punts on this entirely.

  3. "Why is my 2GB export folder now 4GB?" — Downloaded cloud files sitting in a local directory permanently, never cleaned up. The spec has no cleanup strategy. [CHECK THIS]: Prior reviews flagged this as a disk leak risk — it's still unaddressed.

  4. "I exported this yesterday, why is it downloading again?" — No caching strategy is defined. Does a second export re-download everything?


Implicit Assumptions That Must Be Explicit

Assumption Why It Matters
Stream URLs are stable and don't expire If URLs rotate or have TTLs, downloads during long exports will fail mid-way
Files at stream URLs are complete audio files, not chunked/containerized for streaming [CHECK THIS] — The spec says "not HLS" and "direct HTTP file URL," but is the file at the stream URL identical to the original upload? If Nginx or the backend does any processing, "preserves original format" may not hold
Auth is not enforced on file-server routes and won't be The spec notes this but treats it as permanent. If auth gets added next sprint, every download breaks. Send the Bearer token now anyway.
Network is available at export time No offline-first consideration. What if they're on a plane?
track.cloudStreamPath is always a valid, complete URL What validation exists?
Downloaded files have usable filenames Stream URLs often have UUIDs or hashes as paths. Does /streams/a3f8c2d1-... become a usable filename in a DAW session? DAW users care deeply about track names.

Contradictions and Tensions

  1. "Mirrors UploadService pattern" vs the actual need: UploadService is a fire-and-forget background operation. Export-driven download is a blocking, synchronous dependency in a pipeline. These have fundamentally different error handling, cancellation, and lifecycle requirements. Mirroring the pattern will lead you to the wrong architecture. Prior Review 2's recommendation of a pre-export preflight step is correct — this is a preparation phase, not a background service.

  2. track.filePath mutation vs data integrity: Setting track.filePath on a SwiftData @Model after download conflates "I have a cached copy for export" with "this is a local track." What happens when the cached file gets deleted? Now you have a Track that claims to be local but has no file. The isCloud flag and filePath are now out of sync. This is a data corruption bug waiting to happen. Use a separate cache lookup, not model mutation.

  3. "Cancel download is supported" during export: What does cancel mean? Cancel one track's download? Cancel the whole export? Cancel downloads but continue export with partial tracks? This is unspecified and will create confusing UX if not defined.


What's Missing

  • Preflight check: Before export starts, scan the playlist, identify cloud tracks, show the user: "3 cloud tracks need to be downloaded (47 MB). Download and export?" This solves the "how long" problem and gives informed consent.
  • Partial success policy: Define it. I'd recommend: download all possible → export all available → show a summary: "Exported 38/40 tracks. 2 tracks failed to download: [names]. [Retry] [Export without them]"
  • Filename strategy: "{trackName}.{originalExtension}" with sanitization and collision handling (append -1, -2, etc.)
  • Temp file lifecycle: Export-driven downloads should go to a temp directory and be cleaned up after export completes (or on next app launch as a fallback). Don't pollute the user's disk.
  • Disk space preflight: If Content-Length headers are available from Nginx [OPEN QUESTION from prior review, still unanswered], check available disk space before starting.
  • Retry policy: At least one automatic retry on transient network errors before surfacing failure to user.

Hallucination Check

  • UploadService.swift — PUT /api/upload: [UNVERIFIED] — stated as user-provided context, taking at face value.
  • ChadTrack / Track model distinction: [UNVERIFIED] — stated in Architecture Context, plausible but can't independently confirm the struct vs @Model split.
  • Nginx with Range support: [UNVERIFIED] — user-provided, plausible for media serving.
  • MixExporter.copyAudioFiles(): [UNVERIFIED] — specific method name, plausible but unconfirmed.
  • CloudBrowserView: [UNVERIFIED] — specific view name.
  • DAW formats (Audition, Bitwig, REAPER): These are real DAWs. No hallucination concerns.

No outright hallucinations detected, but several specific code entity names are unverifiable.


Bottom Line

The core problem is right. The scope is wrong, the architecture is risky, and the hardest UX questions are unanswered.

Ship this as v1:

  1. Pre-export preflight that identifies and downloads cloud tracks to a temp directory
  2. Explicit user consent before downloading
  3. Clear partial-success handling
  4. Temp file cleanup after export
  5. No model mutation — use a transient cache lookup

Defer to v2: manual download, offline playback, persistent caching, library-level download management.


Session State **Blocking findings**: - Ambiguous partial-failure policy: no defined behavior for download failures during export (fail-fast vs partial vs retry) - Data integrity risk: mutating `track.filePath` on SwiftData model conflates cache state with source-of-truth, creating stale-path corruption risk - Scope violation: Flow B (manual download/offline playback) is a separate feature bundled into a "Small" appetite - Untestable acceptance criteria: "Download progress is visible" and "Error messages on failure" lack specificity (visible where? what errors? what message?) - Architecture risk: coupling network I/O inside export loop creates blocking UX with no defined timeout, retry, or cancellation semantics **Advisory findings**: - Add pre-export preflight check with user consent dialog showing track count and estimated size - Define explicit partial-success UX: download all possible → export available → show summary with retry option - Use temp directory for export-driven downloads with cleanup on export completion and app-launch fallback - Send Bearer token on downloads NOW even though auth isn't enforced yet (forward-compatibility) - Define filename strategy: sanitized track name + original extension + collision suffix - Add disk space preflight if Content-Length headers are available - Add at least one automatic retry on transient network errors - Don't mirror UploadService pattern — export-driven download has fundamentally different lifecycle requirements - Verify that file at stream URL is identical to originally uploaded file (no server-side processing) **Decisions**: - Recommend v1 = export-time preflight download to temp dir, explicit error handling, no model mutation, no manual download - Recommend v2 = manual download, offline playback, persistent cache, library download management - Prior reviews' recommendations on preflight and separation of concerns are endorsed **Open Questions**: - Does Nginx provide Content-Length headers on stream URLs? (needed for progress bars and disk space preflight) - Is the file at the stream URL byte-identical to the original upload? - Are stream URLs stable/permanent or do they expire? - What is the expected p50/p95 cloud track count per playlist? (sizing the download UX) - What happens to `track.isCloud` and `track.cloudStreamPath` after a download — are they preserved? - Does the app have any existing temp file management or cache cleanup infrastructure? **Key Entities**: DownloadService, UploadService.swift, MixExporter.copyAudioFiles(), ChadTrack (Codable struct), Track (SwiftData @Model), CloudBrowserView, track.filePath, track.isCloud, track.cloudStreamPath, track.hasLocalFile