Skip to content

Sonnance — Roadmap & Product Source of Truth

This is the single source of truth. Where any other doc (incl. 0118) conflicts with this file, this file wins. Last re-aligned: 2026-07-04.


1. Vision

Sonnance is the creative hub for the music industry — one place where artists, producers, managers and engineers collaborate on audio from idea to release: versioned files, time-synced feedback, approvals, and sharing, instead of the usual sprawl of final_v3_REALLY_final.wav across Dropbox / WeTransfer / WhatsApp / email.

Brand: the product is Sonnance. Ctrl Audio / ctrl-audio (repo + folder names) and WAVIC (legacy mobile doc) are legacy aliases; use Sonnance everywhere new. (Owner to confirm before App Store / public-facing launch.)


2. Who it's for — and the one-dashboard decision

The platform serves many roles — artist, producer, manager, engineer, visual artist, A&R — but we deliberately do not build a different home screen per role.

Decision: one app, one dashboard, role-driven content. Access is controlled by the existing Role model (Owner / Admin / Editor / Viewer) applied per resource, which cascades ArtistSpace → Project → Track. Everyone sees the same surface; what they can do is gated by role.

"Per-user-type homes" and bespoke layouts become a post-MVP customization / templates layer, not a launch requirement. Details in 20-UI-SIMPLIFICATION.md. The large actor-driven UX exploration in 17-UX-ARCHITECTURE-ACTOR-DRIVEN.md is reference for that future layer, not the active spec.


3. Platform strategy

iOS-first → web parity → public API.

  1. iOS (native SwiftUI, iOS 26 Liquid Glass) — ship first. It's the most compelling way to put Sonnance in front of people in conversations, and the review/approve loop is inherently mobile. MVP scope in 19-IOS-MVP-REVIEW-APPROVE.md.
  2. Web (existing Next.js app) — brought to the same look & feel and feature parity after the iOS wedge lands. Shared Liquid-Glass design language (tokens in doc 19).
  3. Public API — later, for DAW / distributor integrations.

Supersedes: the architecture diagram in 01-PLATFORM-VISION.md shows "React Native" for mobile. That is superseded — mobile is native Swift/SwiftUI for full Liquid Glass fidelity. Both clients talk to the same NestJS REST + WebSocket API.

┌──────────────┬───────────────┬──────────────┐
│  iOS (Swift) │  Web (Next.js)│  API (future)│
│  ★ first     │  parity       │              │
└──────┬───────┴───────┬───────┴──────┬───────┘
       └───────────────┼──────────────┘

              NestJS  (REST + WebSocket)

        MongoDB Atlas · Azure Blob · Front Door CDN

4. What already exists (don't rebuild)

Backend (ctrl-audio-back) — live & production-shaped:user (JWT + Google OAuth), artistSpace, project, track (versions, attachments), file (Azure/S3 abstraction, signed URLs, Sharp image variants), comment (timestamp/section, replies, resolve), section, collaboration (roles + invites), link (share links w/ expiry), notification (SSE), subscription (Stripe), search, trashSpace (soft-delete recovery).

Web (ctrl-audio-front) — built on the Isomorphic Furyroad template: single ArtistSpace dashboard, waveform track editor (wavesurfer.js), file library, collaboration/share modals, billing, onboarding, storage/usage, trash, search.

Infra (infrastructure): ARM templates for ACR (sonnanceacr), Container Apps (prod/dev) and Front Door CDN; provisioning scripts; CI/CD. Live Azure resources are currently torn down — the ARM templates are the reproducible record. See CLAUDE.md.


5. MVP definition — the Review & Approve loop (iOS)

The minimum lovable slice, targeting the consumption/feedback side (the proven mobile wedge, à la Highnote). A user opens the app (or a share link), listens to a track, leaves time-synced comments, and approves or requests changes — with push notifications closing the loop.

In scope: sign in (incl. Apple) · recent + shared tracks · track + versions · waveform playback · time-synced comments (+ replies, sections) · approve / request changes · open share link as guest · push notifications.

Out of scope for MVP: creating spaces/projects, uploading large files, attachments management, billing, full search, trash, analytics. (These stay on web; come to iOS in parity phase.)

First-release scope upgrade (2026-06-07). Review feedback showed a review-only app isn't a shippable first release — you must be able to create and edit on the phone. The backend already exposes full CRUD for the whole hierarchy, so first release now targets the create → review → approve loop: CRUD for Artist / Project / Track / metadata / comments built as iOS UI over existing endpoints, with audio upload staged as its own slice and Shared parked pending the actors/Roster decision. Scope + sequence: 25-IOS-CRUD-FIRST-RELEASE.md.

Release-vision shift (2026-07-03). Stakeholder feedback: users need features available, not a half-done product — the first public release framing upgrades from "MVP" to a Release vision: the create → review → approve loop (doc-25) plus two headline featuresContact ID (53-CONTACTID-RELEASE.md) and Discovery / Events (52-DISCOVERY-EVENTS.md). §5 above stays the loop's definition; the headline features are specified in their own docs (UX reference study: doc-51, Apple Invites).

Full screen→endpoint mapping: 19-IOS-MVP-REVIEW-APPROVE.md.


6. Backend gaps to close for the MVP

Verified against the controllers on 2026-05-31:

GapWhy it's neededStatus
Approval / "request changes" endpointThe core verb of the MVP. track.releaseStatus fields exist but no state-transition route did.✅ done — PATCH /track/:id/release-status
Apple Sign IniOS-first launch expectation; only Google + email before.✅ done — POST /user/auth/apple
Refresh tokensMobile needs a refresh flow (Keychain).✅ done — POST /user/refresh-token (+ refreshToken on all auth responses; access token unchanged for web compat)
APNs push + register-deviceCloses the review loop; only SSE before.✅ done — POST /notification/register-device + APNs sender auto-fired from every notification (no-op until APNS_* env set)
GET /mobile/dashboard aggregateReduce mobile round-trips (recents + notifications + storage).🟡 optional
GET /track/:id/stream (Range)Nice-to-have; Azure Blob already supports HTTP Range on signed URLs.🟡 optional
Cursor paginationInfinite scroll on lists.🟡 optional

7. Phased roadmap

  • Phase 0 — AI-native foundation & alignment(this engagement) Workspace CLAUDE.md/AGENTS.md, this roadmap, the iOS MVP + UI-simplification specs, infra docs reconciled.
  • Phase 1 — iOS MVP (Review & Approve) Backend gaps (§6) → native SwiftUI app (new ctrl-audio-ios repo) → TestFlight.
  • Phase 2 — Web parity & shared look/feel Consolidate to one layout/dashboard (doc 20), apply Liquid-Glass-aligned design tokens.
  • Phase 3 — Creation on mobile + industry tools Mobile capture/upload, then time tracking, contracts, royalty splits, distribution, public API.
  • Release headline features (elevated 2026-07-03, stakeholder-confirmed)Contact ID (doc-53) + Discovery / Events (doc-52) — graduated from deferred ideas to named Release features (UX reference study: doc-51, Apple Invites). Sequenced within Phases 2–3 as the Release cut is drawn with the stakeholder.

8. Document map (what to read, what's archived)

DocRole now
00-ROADMAP.md (this)Source of truth
57-PLAYER-AS-PRODUCT.md▶ Envisioning LOCKED (owner-raised 2026-07-05; every fork + §9 item owner-confirmed 2026-07-06 — "good to go"; P0 ✅ built 2026-07-06, P1+ ride doc-56 S2 + doc-30 Phase 2) — the player grows from review surface to product: handoff = browse ≠ play ✅ at track and version altitude (opening track B — or switching version — while A sounds loads it paused with a prominent play; the mini player/lock screen always show what's sounding, incl. the sounding version #; explicit play is the only switch — the loaded-paused deck is doc-56's prewarmed item) · queue = first-class ✅ (sources: album/project play via Track.order — cheapest, leads · favorites — dedicated Favorite collection, track-level hearts v1 → audioFinal, recency · single/stop-at-end; modes repeat off/one/all + shuffle; named playlists deferred) · release-preview parity (lock-screen artwork via MPMediaItemArtwork = P0, hours-sized, gate-free; motion artwork player-side incl. iOS 26 MPMediaItemAnimatedArtwork full-screen lock-screen preview, Project/ArtistSpace-scoped per Apple's EP/album/artist-page rule, stored in the doc-45 asset cloud ✅; line-synced lyrics ✅ structured collection + tap-along editor + Apple-style scrolling view + TTML/LRC export — explicitly not a doc-32 approval dimension) · Musixmatch: replicate, don't integrate ✅ (artists own their lyrics; Apple ← TTML via distributor, Spotify/IG ← Musixmatch Pro). Full-screen artwork/lyrics = a separate explicitly-entered "listening mode" — the doc-22/24 waveform-centric lock stands. Slices P0–P4; P1+ gated behind doc-56 S2 + doc-30 Phase 2. Defers to this file
56-FIRST-PLAY-PIPELINE.md▶ Greenlit — S1 + S2 ✅ built 2026-07-06 (S1: backend b419748 + iOS; S2: route-enforced TrackRoute(track:)/.coldOpen + TrackLink/push prewarm + likelihood-prewarm w/ cellular guard + seeded transport + playImmediately; Front Door spike pending §7 Q4; S3–S4 remain) — first-play made structural + measured: the June prewarm/seed fix decayed (opt-in per call site; 3 of 7 player entries cold, incl. the MVP approval rows — dashboard queries don't populate audio). Fix = Intent → Warm → Paint → Sound: TrackRoute(track:) sole entry (seeds/prewarm by construction), likelihood-prewarm of the top-2 dashboard candidates, seeded transport (play pressable before /track/:id), fileId-keyed LRU disk cache; renditions/Front Door as data-driven forks — locked by a doc-38 playback_first_audio TTFA metric (warm P50 ≤ 300 ms · fresh-install Wi-Fi P50 ≤ 1.5 s). Slices S1–S4; S1 small (cover the cold surfaces + record TTFA — hits the owner's literal scenario). Defers to this file
55-TENANCY-PERSONAS.md▶ Envisioning (owner-raised 2026-07-04, doc-54 greenlight round)Workspace = tenant; one person, many hats. Tenants never talk directly; three sanctioned crossings: migration/move (subtree + grants + asset re-key, doc-45 files-ride-the-node) · the person layer (Contact ID graph, messaging, Discovery/Events relate persons above tenants, doc-52/53) · link/guest principals (doc-26/44). The owner's "sub-user/subaccount" need resolved as one User + one Contact ID + WorkspaceMembership(user, workspace, role, facet) — the doc-44 workspace-scope grant enriched with a persona facet (artist/producer/booking-manager; doc-43 preset catalog); the card wears the hats with per-facet visibility lenses (doc-53 §3.2) + per-context display identity (INV-15 precedent); sub-accounts rejected (fragments the graph). Billing per workspace; facets free. Open: default hat visibility · multi-facet per workspace · guest→member upgrade · workspace-switcher = the reserved Home top-left (doc-54 §8.2). Defers to this file
54-UX-REVIEW-START-SIMPLE.md▶ Consolidation / UX-UI review (owner + stakeholder, 2026-07-04)"start simple, many ways of working." Locks principles 7–8 into doc-47 §7 (7 start almost empty — the minimum that provides basic/common value as a starting point, grow as you work; empty structure never renders · 8 many ways of working — structure offered, never demanded). Per-surface: Home v2 = NON-SCROLLABLE one screen (fixed tile budget: Approvals · Notes · Activity · context-aware Resume, + condensed pipeline strip; See-all = the only scroll escape; AX Dynamic Type re-enables scroll — closes the doc-47 §11 field feedback) · new Contact tab (5th, extreme right; naming open) = Contact ID + account + Discovery entry top-right (absorbs the avatar drawer; realizes doc-47 §10.3) · Portals start almost empty (resolves doc-49 §7 fork 4: no-content zones don't render, one "+" foot affordance) · LEGO quick-add "+" = the primary authoring over minimal presets, free-tier preset picks (sharpens doc-43) · progressive Configure onboarding (doc-48; no wizard) · mgmt.ctrl view modes confirmed as ways-of-working (no doc-42 change). Event = card+sheet (Portal-compatible), no v1 Portal; contact↔event cross-refs (attending · registered · attended → docs 52/53). Housekeeping: reference_assets/ committed as-is (doc-51) · handle sonnance.com/@username approved (doc-53 §6 Q1). Build order proposed (Home v2 → Contact tab → portal empty-state → LEGO "+"), owner greenlights. Defers to this file
53-CONTACTID-RELEASE.md▶ Release headline (owner-raised 2026-07-03, stakeholder-confirmed)Contact ID = the identity layer: a living card (identity · tiles/Linktree-style link-hub INV-16 · QR + Apple Wallet .pkpass) + the identity privacy philosophy (owner-private by default · opt-in visibility per field · the outsider view is a configured lens — extends the doc-38/26 posture to people) + messaging gated by the contacts graph + "who has me" visible only to me + opt-in graph-derived stats. Supersedes doc-16's framing (its card-design exploration stays reference); account = own corner stands (doc-47 §10.3). Defers to this file
52-DISCOVERY-EVENTS.md▶ Envisioning / Release headline (owner-raised 2026-07-03, stakeholder-confirmed)Discovery & Events: the Event = a timespan-bound node with momentum stages (draft → announced → approaching → live → afterglow → archive) that leads with a request-for-action (RSVP, INV-02); timespan + role lenses (Upcoming/Past ÷ Hosting/Attending); dual-mode inviting (broadcast link w/ approve-gate + one-time per-guest links) riding doc-26 links; per-event privacy config (doc-48 apply-at-node). First dedicated Discovery doc — consolidates + supersedes the doc-17/15 fragments (Event Organizer actor · DiscoveryFeed/MapView · Maps · Discovery Agent). Audiences: musicians · studios · organizers · venues · more. Defers to this file
51-INVITES-UX-REFERENCE.mdReference / UX study (owner-captured 2026-07-03)Apple Invites: all 38 screenshots in reference_assets/ indexed + 20 stable patterns INV-01…INV-20 (Adopt/Adapt/Skip verdicts) feeding docs 52/53 — card anatomy, pinned RSVP, timespan lenses, dual-mode inviting, link expiry/rotation/kill-switch, QR-as-peer-of-link, tiles link-hub, asymmetric visibility, per-object privacy panel, minimal contact consent, pre-permission pitches. Note: reference_assets/ ≈ 77 MB, untracked — git handling (LFS / commit / local-only) flagged to owner. Defers to this file
50-UX-PARADIGM-OPTIONS.md▶ Envisioning / decision-support (owner-raised 2026-06-29) — a deliberate step back before investing in the doc-47/49 build: is Object/Spatial-primary the optimal center of gravity? Frames 6 paradigms (A Object/Spatial current · B Workflow/Board · C Activity/Feed · D Command/Search · E Document/Canvas · F Player/Media) — not exclusive; the choice is the primary (home + default verbs), others become lenses. Finding: today is A-primary (open into the catalog tree) but the MVP is Review & Approve + the differentiator is workflow (doc-46) → the heart is move-work/decide (B/C) + craft (F), with A as substrate, not the daily entry. Recommendation: keep the fractal-Portal backbone (doc-49) but make the primary action-first (B/C) — promote doc-37 Home from sibling to protagonist + a light work-board; ⌘K (D) as accelerator; Canvas/LEGO (E) as post-PMF ceiling. Invalidates none of the backbone; re-frames sequencing. §6 = the center-of-gravity choice to confirm. Defers to this file
49-PORTAL-SYSTEM.md▶ Envisioning / redesign (owner-raised 2026-06-29) — "the Project Portal is messy + portals aren't fractal" = one root cause: no Portal system. Audit: Portal is a bare sheet+VStack; ProjectPortalContent flat-stacks ~11 sections (no hierarchy/disclosure); Artist/Project hand-duplicate sections; Track/Workspace/Roster have no Portal → the doc-47 §6 fractal principle is unbuilt. Fix: one Portal anatomy = 8 ordered zones (Identity · Health · Content · Workflow/Config · Relationships · Notes · Personal · Manage/Danger) with progressive disclosure; only Content varies by altitude → a true fractal. Full ladder locked: Workspace ("cloud root space") → Artist → Project → Track → File (Roster = tag/lens, no own portal; Workspace = tenant root gets the root portal). Shared PortalScaffold + zone components kill the per-entity duplication (doc-47 §6 in code). Build: scaffold → redesign Project Portal (reference, fixes the mess) → roll to Artist+Track → Workspace root → File. UI-layer over mostly-existing endpoints. §7 forks: sheet-vs-fullscreen · Workspace portal home · Track portal vs player · disclosure aggressiveness. Defers to this file
48-CONFIG-ACROSS-HIERARCHY.md▶ Envisioning (owner-raised 2026-06-29) — unifies two owner uncertainties (workflow across Roster→Variant · LEGO attributes across the hierarchy) into one model: define-at · apply-at · resolve (cascade + override). Finding: authoring is workspace-scoped (templates) but application is Project-only — no cascade. Real ladder = Workspace → ArtistSpace → Project → Track; Roster = tag/lens, Variant = predicate (not nodes). Resolve = most-specific-wins up the ladder (same rule as doc-44 perms / doc-41 status). Priority locked = studio-wide workflow cascade (resolveWorkflow(project) = project.workflow ?? artist default ?? workspace default ?? system) so new projects inherit the studio process with zero setup; attributes (doc-43) ride the same rails; surfacing split Configure(define) / Browse+Manage(apply+view). §8 forks: where the default lives (Workspace vs Artist) · cascade depth · override granularity · build Workspace container now vs interim. Build backend-first post-confirmation. Defers to this file on conflict
47-PRODUCT-NORTH-STAR.md▶ Consolidation / north-star (owner-raised 2026-06-28) — unifies the whole product into three zones + one navigation grammar, so surfaces stop being bolted on. 2 decisions locked: (1) nav model = Browse (catalog + fractal Portals at every altitude, roster included) · Manage (top-down x-ray = mgmt.ctrl/doc-42 + analytics/doc-38) · Configure (the missing control room = workflow templates/doc-46 · attributes/doc-43 · teams-roles/doc-44 · account); (2) captured as this doc first, then build. Front door ("what needs me", doc-37) = role-driven landing over the zones, not a 4th zone. Invents nothing — places every prior doc (reconciliation map §8) + actors×actions matrix §4. The real gap = Configure has no surface today. Defers to this file on conflict. §10 forks all resolved 2026-06-28: zone switcher = tab bar · front door = sibling (not merged) · account = own corner (bigger "Contact ID" identity phase deferred) · Workspace tenant root confirmed (w/ doc-43/44) · MVP = front door + Browse loop now, Configure thin shell (workflow templates first), later phases slotted. Model settled; build iOS-first (§11). Owner sitting with the framing before slice 1
19-IOS-MVP-REVIEW-APPROVE.mdActive — iOS MVP spec
20-UI-SIMPLIFICATION.mdActive — one-dashboard / one-layout
21-DATA-ARCHITECTURE-AND-IDENTITY.mdActive — data source-of-record, ownership ladder, Apple bundle-id scheme
22-IOS-DESIGN-SYSTEM.mdActive — iOS design SoT: theming (light/dark/system), tokens, version colors, Liquid Glass, Portals; grounded in canva-refs/
23-IOS-PHASE2-IA.mdActive — Phase 2 navigation/IA: Artist→Project→Track browse, shared inbound/outbound, Portals everywhere (post-MVP)
24-UX-BACKLOG.mdActive — running list of iOS UX/polish refinements (tagged quick / P2 / P3), so review feedback isn't lost
25-IOS-CRUD-FIRST-RELEASE.mdActive — first-release scope upgrade: review-only MVP → create/review/approve; CRUD over existing endpoints, audio upload staged, Shared parked pending the actors/Roster decision
26-IDENTITY-SHARING-NAVIGATION.mdActive — build phase started 2026-06-12 ("go doc-26"): "one graph, many lenses" model + §Build plan (Wave 1 backend contracts → Wave 2 iOS slices → Wave 3 lenses/saved views). The B2B center of gravity per doc 31
27-TESTFLIGHT-BACKEND-DEPLOY.mdActive runbook — deploy the API to Azure Container Apps on api.sonnance.ff8.dev so TestFlight builds reach it (data tier already cloud; bash + az, reuses setup-azure.sh). API now live; TestFlight running.
28-RECORDINGS-VARIANTS-RELEASES.md▶ Option C BACKEND BUILT 2026-06-28Track gained variantType (original/radioEdit/explicit/clean/instrumental/remix/live) + mirror-written relations (alternateOf/remixOf/sameRecordingAs) + secondary appearsInProjects[]; endpoints GET/POST/DELETE /track/:id/relations · POST/DELETE /track/:id/appearances · variantType via PUT; jest+e2e green. Remaining = iOS (variant chips + group-by/cross-link lenses, doc-26) + Option B (formal Release/UPC) if distribution formalizes. Unblocks doc-46 per-variant. Envisioning below
29-VERSION-SCOPED-APPROVAL.mdActive — approval bound to the exact audio version: File.reviewStatus (+by/at), per-file PATCH, final-version mirror to releaseStatus.version, new-upload → Under review. Implemented end-to-end 2026-06-11
30-GLOBAL-PLAYBACK.mdEnvisioning/phased — Sonnance as a music player: Phase 1 ✅ background audio + lock-screen transport; Phase 2 PlaybackManager + floating mini player (zero-chrome when idle); Phase 3 favorites queue / loop / continuous play. Player-as-product layer (handoff behavior · favorites model · release-preview parity) extended by doc-57
31-PRODUCT-VISION-360.mdActive — brand/business layer (owner-locked 2026-06-11): professional-led audience (B2B center of gravity, solo pros as adoption engine), monetization deferred, brand pillars = craft/reverence + professional calm (fair-credit commitment later), north star = collaboration OS → creator business hub
32-APPROVAL-MODEL.mdEnvisioning (owner-raised 2026-06-13) — approval as a dimension, not a screen: generalizes doc-29 audio-version approval to cover / lyrics / tracklist / metadata / master + production-phase gates + a Project-level release-gate rollup; surfaced in Portals (doc 26) not only the Wave. Folds in the cover thread (crop→future-AI, hero-display wiring, history-CRUD, cover-approval home = Project/CD). Envision now, build after the doc-26 Shared waves; three cheap ride-alongs flagged
34-IMAGE-DELIVERY.mdActive — slow cover loads, low-cost fix (no CDN). Backend DONE. Cover uploads route through uploadImageWithVariants; right-sized WebP variants persisted + auto-signed: thumb 150 / thumb2x 300 / medium 600 / original on File.*Url + ArtistSpace/Project.image* (300² added 2026-06-19 for high-density tiles). Backfill script written (dry-run-gated). Remaining = iOS only: client-side variant selection by rendered px (thumb ≤150 · thumb2x ≤300 · medium ≤600 · original) + durable disk cache — the actual user-facing fix; queued in .agent/next-brief.md. CDN deferred as a scale lever
33-NOTES-ANNOTATIONS.mdEnvisioning (owner-raised 2026-06-13) — Notes = node-anchored Comments, not a new entity: Comment already carries author/timestamp/thread/resolve and anchors to project/version, so the single Track.notes string generalizes into authored, dated, threaded notes; cross-hierarchy surfacing via tags[] (doc-26 lens); status is opt-in resolve (not a default), elevating to a doc-32 approval only for true sign-offs. Envision now, rides the Portals-everywhere phase; cheap early taste = add tags[] to Comment
35-REALTIME-SYNC.mdEnvisioning (owner-raised 2026-06-19) — live collaboration sync: comments/status/uploads/edits should appear at publish with no manual refresh. Finding: the Socket.IO gateway is already built (websocket module — JWT auth, user:/track:/project:/artist: rooms, emitTo* helpers, @Global export) but wired nowhere. Work = gate join:* by accessdone 2026-06-19 (security gap closed first), then emit typed events from domain services, iOS connects/joins/merges + catch-up on reconnect. Envision now, build as its own phase (track-comments emit is next; track-comments first)
36-INTERACTION-PATTERNS.mdEnvisioning (owner-raised 2026-06-20, off doc-33 roll-up) — how touch behaves: a reference (rolled-up comment, note, search hit) carries its provenance (roster › artist › project › track › V#) and is a portal to its source — navigate via peek-then-go (tap expands → "Open in {location} →", not a blind jump). Plus touch-text select/copy/edit-in-place, the Roster Portal (Roster joins Portals-everywhere), and "view"/seen events (read-receipts, distinct from status). Backend provenance shipped; rest envisioned
46-WORKFLOW-ENGINE.mdEnvisioning (owner-raised 2026-06-28, off doc-45) — the adaptable, actor-aware workflow engine. Reframes "release readiness" → model your own process: the fixed Title·Cover·Version·…·Metadata checklist is too release-centric/technical and ignores the creative/recording lifecycle (re-record · mic/studio/featured-artist · test-pressing · splits · contracts) where most actors + work live. Generalizes the shipped doc-45/32 readiness seed (a flat profile) into Stages → Steps. 3 decisions locked: (1) shape = ordered Stages → Steps (lifecycle pipeline, not flat list / not state machine); (2) templates + full custom steps (opinionated workflow templates as starting points + owner-defined custom steps in their own language); (3) studio/workspace templates → project instance (+ per-variant doc-28 overrides). Step = type (approval / task / asset / decision / review) + ownerRole/assignee + checkpoint + assetSlot; the release gate = "all required steps satisfied" (generalized doc-45 rollup). Hub = mgmt.ctrl Board + Portals; config engine = doc-43 LEGO; actors = doc-12/17; gating = doc-44. User-friendly = template-first, calm-by-default, their-language. Post-MVP, built incrementally on the seed
45-RELEASE-PIPELINE-ASSETS.mdEnvisioning, POST-MVP (owner-raised 2026-06-28) — the release pipeline as a configurable, variant-aware checklist over a project asset space. Seed: motion/animated covers (Apple-Music-style) stored · previewed-as-the-store-renders · approved, inside a project "cloud"/asset space (lift attachments from Track up to Project/Artist + documents), and the realization that release-readiness has more & configurable elements than Title·Cover·Version, varying per project/album + per variant (remix/radio-edit/live/instrumental). Largely a synthesis of doc-32 (approval dimensions) + doc-28 (variants) + doc-43 (LEGO — applied to the checklist itself) + doc-34 (images → motion media) + track attachments; net-new = motion-cover media + per-node asset space + configurable readiness profiles. Hub = mgmt.ctrl Board + Portals. Full configurability depends on doc-43; motion covers + asset space are separable, can land earlier. Deepen post-MVP
44-RBAC-TEAMS-PERMISSIONS.mdEnvisioning (owner-raised 2026-06-27) — the authorization redesign; supersedes RBAC-DESIGN.md. Surfaced by the doc-42 mgmt.ctrl bulk write (first feature mutating many aggregated nodes — can't lean on ID-obscurity). Finding: enforcement is inconsistent (JWT authN fine; reads safe by query-scoping; but writes are partial — updateReleaseStatus gates, track.update does not → default-allow gap; Team exists but fans out to per-resource collaborators[], not a live permission; no central resolver). 6 decisions locked: (1) one central PermissionService.can() + Nest guard, default-deny (callable from services too); (2) computed-live + cache (ReBAC/Zanzibar) — resolve from team membership + scope assignments + grants at access time, stop materializing per-resource roles; (3) unified Grant = (principal, role, scope), principal ∈ User · Team · Link/Guest (teams = scaling, direct grants = solo/one-off, links = guests); (4) cascade with override (most-specific scope wins → highest role → deny>allow); (5) two role axes kept separate (team-governance vs access role); (6) shadow-mode migration (backfill grants from collaborators[]+TeamShare, compare-only, then flip). Action-matrix is data-shaped (→ doc-43 custom roles); audit rides doc-41; teams=premium (doc-31); guest principals (doc-38). First consumer = doc-42 bulk write; mgmt.ctrl reads already safe. Foundational — sequence before further mutation surface
43-CONFIGURABLE-ATTRIBUTES.mdEnvisioning (owner-raised 2026-06-27, split from doc-42 §8) — the "LEGO" configurable-attribute engine: entities carry different attribute sets, the same attribute can live at different hierarchy levels, catalogs per-workspace definable — the composable data substrate under mgmt.ctrl (doc-42) + consolidation of every deferred customization thread (doc-17 Lego OS · doc-20 §4 · doc-41 per-workspace catalog · doc-31 monetization). Finding: net-new, no foundation (fixed @Prop schemas; main.ts forbidNonWhitelisted rejects unknown fields; only seeds = unused File.metadata + tags[]; lifecycle-status.ts is the precedent to generalize; no workspace container — ArtistSpace is the de-facto tenant root). 3 decisions locked: (1) typed attributes bag + per-workspace AttributeDefinition catalog ("EAV-lite", service-layer validated — not full per-value EAV / dynamic schemas); (2) global presets + workspace override (ship default catalogs, customers tweak/extend/retire, inherit down — matches doc-41); (3) authoring = both (inline quick-add in mgmt.ctrl + full Workspace Settings catalog editor). Premium/post-MVP layer (doc-31 gate on subscription); default presets can ship early as fixed catalogs (cheap, like lifecycle-status); does NOT block doc-42. Envisioning only — no build this pass
42-MANAGEMENT-CTRL.mdEnvisioning (owner-raised 2026-06-27) — management.ctrl = Management Command Center: a Power-BI-grade eagle-view over the whole catalog (roster → artist → project → track → version) for managers/A&R/admins/execs — status · pending approvals · release-readiness · notes · listening engagement — without drilling node-by-node, plus read+WRITE incl. bulk (inline edit · per-row decisions · bulk actions wrapping existing endpoints). Revives & supersedes doc-17 §5 "Hierarchical Management Control" (the parked /manage vision). Extends doc-38 (its analytics engine — heatmap/engagement/privacy); the Portals top-down synthesis (vs per-node Portals). Sibling of, not, doc-37 Home (Home = "what needs my attention"; this = "the whole catalog's state"). Hero = cross-catalog Activity Command Timeline (each row a provenance portal, doc-36). Platform-agnostic (calm cards → dense grid). Role-gated manager lens (doc-20). Finding: no cross-workspace aggregation exists (only /mobile/dashboard, single-user) → net-new /management/* aggregate + bulk endpoints; activity hero's hard dep = the doc-38 event log. "LEGO" configurable attributes = companion, out of scope → proposed doc-43. Build after doc-37 + first-byte perf + doc-38 backbone
41-RECORDS-AUDIT-STATE-STATUS.mdEnvisioning (owner-raised 2026-06-26) — records discipline: audit + state + status (+ i18n) — the data foundation under doc-40. Finding: half exists (Mongoose timestampscreatedAt/updatedAt everywhere; owner ≈ createdBy; soft-delete isDeleted ≈ Active/Inactive; approval releaseStatus). Real gaps = explicit modifiedBy/createdBy, a lifecycle statusCode (Draft/Published/Archived…, fixed defaults now, customizable later — distinct from approval), i18n (user.timezone/locale, store UTC/render-local, status as stable keys not labels). Four orthogonal axes (audit · state · lifecycle · approval) — don't overload one "status." Powers doc-40 filters/sorts/defined-views + extends search to comments/notes. Slice 1 (audit) is cheap/no-fork
40-SORT-FILTER-SEARCH.mdEnvisioning, slice 0 shipped 2026-06-26 (owner-raised) — per-view Sort / Filter / Search across the app, with persisted per-view prefs. Finding: a filter foundation exists (LensBar + doc-26 SavedViews) but is Artists-only, filter-only — no Sort, no Search, grouping was hardcoded-automatic. Model = a per-view view-config (Filter chips · Sort menu · .searchable), two persistence grains: display prefs = local @AppStorage (the roster toggle = first instance), named Saved Views = server (doc-26). Each axis to its idiomatic iOS home; default stays calm (doc-20). Slice 0: roster grouping off-by-default + saved toggle, redundant section titles/count removed, roster margin fixed. Generalize alongside each view — not a blocking phase
39-SAMPLE-SHARING.mdEnvisioning (owner-raised 2026-06-26) — share a clip, not the whole track: a sample/teaser (e.g. the hook) for curiosity-without-exposure or focused feedback. Extends doc-26 sharing (a link + {clipStart, clipEnd} window); inherits doc-38 guest analytics (hook-rate for free). Load-bearing fork: (A) playback-window link (client-enforced, ~zero infra — recommended for teaser/feedback) vs (B) server-trimmed artifact (ffmpeg, hard leak-protection — only if the master must never be downloadable). Authoring = waveform drag-select. Build after doc-37 + the doc-38 backbone
38-ANALYTICS-INSIGHTS.md▶ BACKEND BUILT 2026-06-27 (owner chose telemetry-first) — analytics module: append-only Event store + TTL · POST /analytics/events (batch ingest + engagementBand classify) · GET /analytics/track/:id/insights (waveform heatmap + plays/listeners/listen-through/hook-rate) · GET /analytics/workspace (opens/minutes/top tracks) · doc-42 GET /management/insights tie-in. Remaining = clients emit (iOS/web heartbeat + session emitters, guest/link ingest path, identified opt-in role-gating). Below = the original envisioning. Envisioning, all decisions locked 2026-06-26 (stakeholder-raised) — usage + engagement metrics: opens / minutes / "most-listened part." Finding: no telemetry exists (Track.lastAccessedAt is one overwritten timestamp, not an event log); "most-listened part" forces an append-only events backbone + aggregation, not counters. Locked: build both owner-BI and in-product actor metrics on one unified pipeline (also feeds doc-36 seen/view). Hero surface = heatmap on the existing waveform; Workspace Insights role-gated (doc-20); Home gets a pulse card only. Capture-first sequence. 5 detail decisions: own-store + mirror-BI-later · classify plays by depth (impression/sample/listen/complete, don't discard short ones → hook/skip rate) · identified-opt-in gated via Teams/Role · aggregate-on-read (no rollup job; heatmap histogram = lone watch-point) · guest plays = identifiable-by-link (timestamp + coarse location, optional self-ID; store city not raw IP). Build after doc-37 + first-byte perf
37-HOME-DASHBOARD.md▶ ACTIVE phase (owner-locked 2026-06-21) — implements doc-20 §4.2: iOS home becomes a role-driven "what needs my attention" dashboard (needs-approval · rolled-up unresolved notes · activity · recents · roster grid demoted). One dashboard, role-gated content — NOT per-user-type homes (deferred). Mostly assembly of shipped features (roll-up doc-33, approval doc-32, realtime doc-35, notifications, recents — all endpoints verified). Only new backend = optional GET /mobile/dashboard aggregate (kills the home-open round-trips). Slices: iOS shell → wire+role-gate → aggregate → web parity
01-PLATFORM-VISION.mdReference (mobile=React-Native bit superseded; see §3)
12-ACTORS-WORKFLOWS.md, 17-UX-ARCHITECTURE-ACTOR-DRIVEN.mdReference for the future customization/templates layer — §5 "Hierarchical Management Control" revived/superseded by doc-42; the "Lego OS" custom-attribute thread → proposed doc-43
13-MOBILE-CONSIDERATIONS.mdReference — API-for-mobile patterns (refresh tokens, push, streaming, delta sync)
0211, 1416, 18Reference/archive — defer to this file on conflict (16's ContactID framing superseded by doc-53; its card-design exploration stays reference)

9. North-star metrics

Activation: a new user leaves a comment or an approval within their first session. Loop health: comments per track, approvals per week, push→open rate. Business: Free→Pro conversion, MRR, churn.

Ctrl-Audio Platform Documentation