# Deliberation: requirements — Claude Opus 4.6 **Date**: 2026-03-18 12:04 **Model**: Claude Opus 4.6 **Type**: requirements **Artifact**: ## Cloud Download Brief (Draft) ### Problem Cloud tracks in MixBoard playlists cannot be exported t --- # Requirements Review: Cloud Download Brief ## Are These the Right Requirements? **Mostly yes, but the brief is trying to ship two features as one, and the prior reviews have correctly flagged this.** The core user problem is clear: "I hit Export and some tracks get skipped silently." That's the problem worth solving. But the brief conflates this with "make cloud tracks behave like local tracks permanently," which is a fundamentally different feature with different complexity. Let me be specific about what I think is right and wrong. --- ## 1. The Two Usage Patterns Are Two Different Features The brief identifies export-triggered download and standalone download as "two usage patterns." They're not patterns — they're **two distinct features with different architectures.** - **Export-triggered**: Transient. Download → export → optionally clean up. The file is a means to an end. - **Standalone download**: Persistent. Download → update Track model → now it's a local track forever. This is an offline library feature. **Recommendation**: Ship pattern 1 only. Pattern 2 ("Download to Library") is marked as optional but its existence is bleeding into the acceptance criteria — specifically "Downloaded files are persisted across app restarts" — which only makes sense for pattern 2 and actively harms pattern 1 by creating orphan files and state management obligations. The prior reviews both converged on this. I agree with them. **JIT staging during export is the right v1.** --- ## 2. Acceptance Criteria Problems Let me go line by line: | Criterion | Verdict | |---|---| | Cloud tracks in a playlist can be included in DAW session export | ✅ This is the actual goal. Good. | | Download progress is visible to the user | ✅ But **where**? In the export sheet? A separate view? This is undertestable as written. | | Downloaded files are persisted across app restarts | ⚠️ **This is a scope decision disguised as an acceptance criterion.** If you're doing JIT staging, this is wrong. If you're doing standalone download, this needs storage location, naming, and cleanup rules. Either way, it shouldn't be here without a conscious decision. | | User can cancel an in-progress download | ✅ But what happens to the export? Does cancelling one download cancel the whole export? Cancel just that track? This is the partial-failure gap flagged in prior reviews and **still unresolved**. | | Export waits for all downloads to complete | ⚠️ **What if one fails?** This criterion implies all-or-nothing but doesn't say so. Spell it out: does the export proceed with available tracks, or abort entirely? Real users would probably want a choice. | | Error during download shows clear message and doesn't corrupt partial export | ✅ Good, but "doesn't corrupt" is not testable. Define what "not corrupt" means — no partial file written to export directory? Export operation rolled back? | **What's missing from acceptance criteria:** - What happens to staged files after export completes? After export fails? After app crash? - Maximum concurrent downloads (serial vs parallel) — this affects UX timing significantly - File naming strategy for downloaded files (what if two tracks have the same server filename?) - Disk space check before starting downloads - Behavior when the same cloud track appears multiple times in a playlist (download once or twice?) --- ## 3. "Track model changes — store cached/downloaded file path" **This is the most dangerous line in the brief.** Both prior reviews flagged it and I'm going to be blunt: **do not mutate `Track` for v1.** The moment you write a `cachedFilePath` to the persistent Track model, you've signed up for: - Stale path detection (file deleted but path still set) - Migration strategy when you change storage location - `hasLocalFile` semantics becoming ambiguous (originally local vs. downloaded) - Every downstream consumer of Track needing to understand the difference For JIT staging, the download service should return a temporary file URL. The exporter uses it. Done. The Track model doesn't know or care. --- ## 4. Implicit Assumptions That Should Be Explicit - **[CHECK THIS]** "No auth on file routes" — the brief states this but `ChadMusicAPIClient.streamURL(for:)` builds an HTTP URL. Does this URL contain a token as a query parameter? A signed URL? "No auth headers" ≠ "no auth." Verify this by examining the actual URL structure. - **[UNVERIFIED]** `ChadMusicAPIClient.streamURL(for:)` — mentioned in context but not confirmed to exist with this exact signature in the codebase. - **[UNVERIFIED]** `Track.isCloud`, `Track.hasLocalFile`, `Track.filePath` — these field names are used consistently across reviews but I cannot confirm they exist as named. - **Assumption**: The stream URL returns a downloadable file (not an HLS manifest or chunked stream). If `AVPlayer` is using HLS, you can't just HTTP GET the same URL and get a WAV/MP3. **This would break the entire approach.** - **Assumption**: Downloaded files are in a format the exporters can handle. The non-goal says "original format preserved," but do the exporters handle all formats the server might serve? What formats does the server serve? - **Assumption**: Files are reasonably sized. A playlist of 20 cloud tracks at lossless quality could be 2GB+. No mention of disk space handling. --- ## 5. What Would a Real User Care About MORE? A real user hitting Export with cloud tracks probably cares about: 1. **"Just make it work"** — they don't want to think about downloading. Export should handle it transparently. ✅ Pattern 1 addresses this. 2. **"How long will this take?"** — Progress with time estimate, not just a spinner. The brief says "progress visible" but not how. 3. **"What if my internet drops mid-export?"** — Retry logic is not mentioned anywhere. 4. **"Can I still use the app while it downloads?"** — Is the UI blocked? Can they edit the playlist while downloads are in flight? A real user probably does **not** care about: - Whether the file persists after export (they have the DAW session now) - Downloading to library as a separate action (they're already streaming it fine) --- ## 6. Requirements in Tension - **"Downloaded files are persisted across app restarts"** vs **Non-goal: "Cache eviction / storage management"** — You can't have persistence without eventually needing eviction. These are in direct tension. JIT staging resolves this. - **"Export waits for all downloads"** vs user expectation of fast export — If 8 of 10 tracks are cloud, this could take minutes. The brief doesn't set expectations about this UX transition (export was instant, now it might take 5 minutes). - **AudioStitcher needs `hasLocalFile`** — mentioned in context but not in the brief's scope. Is AudioStitcher in scope or not? If yes, it's a separate integration point that needs its own criteria. --- ## Bottom Line The brief correctly identifies the user problem but over-scopes the solution. Strip it down to: 1. **Export-triggered JIT download to temp directory** 2. **No Track model changes** 3. **All-or-nothing export with clear error on failure + option to export with available tracks only** 4. **Cleanup temp files after export completes/fails** 5. **Verify the stream URL returns a downloadable file, not an HLS stream** Ship that. Then decide if standalone download is worth building based on whether users actually ask for it. ---
Session State **Blocking findings**: - Persistence criterion ("persisted across app restarts") contradicts non-goal of no cache eviction — forces v1 into offline library territory without acknowledging it. Must decide: JIT staging or persistent download. - Partial failure policy still undefined after two prior reviews. "Export waits for all downloads" doesn't specify behavior when downloads fail. This is untestable. - Unverified assumption that `streamURL(for:)` returns a directly downloadable file vs HLS manifest. If HLS, the entire download approach breaks. - Track model mutation proposed without migration/staleness strategy — high irreversibility risk. **Advisory findings**: - AudioStitcher integration mentioned in context but absent from scope/criteria — clarify in/out - No retry logic for transient network failures - No disk space pre-check before downloading potentially large files - Duplicate cloud tracks in playlist should download once (deduplication) - File naming collision risk for server-side filenames - Export UX transition (instant → potentially minutes) needs design consideration - Download concurrency policy still unspecified (serial vs bounded parallel) - [UNVERIFIED] `ChadMusicAPIClient.streamURL(for:)` exact signature - [UNVERIFIED] `Track.isCloud`, `Track.hasLocalFile`, `Track.filePath` field names - [CHECK THIS] "No auth on file routes" — verify URL doesn't contain tokens/signatures **Decisions**: - Recommend JIT staging (export-triggered, temp directory) as v1 scope - Recommend no Track model changes in v1 - Recommend standalone download ("Download to Library") deferred to v2 **Open Questions**: - Does `streamURL(for:)` return a direct file URL or HLS manifest? - What audio formats does the server serve, and do all exporters handle them? - Serial or bounded-parallel downloads in v1? - On partial download failure: abort export, or offer to proceed with available tracks? - Is AudioStitcher in scope for v1? - What is the expected file size range for cloud tracks? **Key Entities**: - `DownloadService.swift` (proposed) - `Track.filePath`, `Track.hasLocalFile`, `Track.isCloud` [UNVERIFIED field names] - `ChadMusicAPIClient.streamURL(for:)` [UNVERIFIED] - `ExportSheet` - `MixExporter` - `AudioStitcher` - `CloudBrowserView` - Export formats: Audition, CueSheet, DAWProject, EDL, M3U