Context. surface studio · domain generation · route /studio/generation/music · auth signed-in + creator (proxy-gated; not anonymous, not search-indexed) · source apps/oshun/web/src/app/studio/generation/music/page.tsx
Last walked. 2026-06-03 automated runtime probe (Playwright headless) — prod build + mock BFF; render, HTTP 200, server-rendered <h1>, 0 page errors verified. Evidence: WALKTHROUGH/results/walkthrough-runtime-probe-2026-06-03.md
Purpose#
The approved music generation surface (§24.6 ambient-audio / §24.8). Renders the
rich MusicCard bound to the server's approved music workflow classes
(GET /v1/isis/music/workflow-classes) and submits through
POST /v1/isis/music/generate. The backend runs the workflow-class envelope
guard + ambient-audio entitlement, then enqueues — fail-closed 503 until a music
provider is configured. The client mirrors the same envelope guard
(evaluateMusicRequestEnvelope) so out-of-envelope requests are surfaced before
submit; every outcome is reported honestly.
Entry points#
- Direct URL / bookmark —
/studio/generation/music(proxy-gated to authed creators;robots: noindex/nofollow/noarchive/nosnippet) - Studio shell nav —
active="studio"(shortcut6), generation sub-area - Sibling generation surfaces — cross-linked from curated-cards / nyx-3d / living-scene
Layout regions#
- Header: shell header (
ShellLayout active="studio") - Main:
<section data-music-studio><h1>Music Generation</h1>— page heading, rendered in all three states (error → insidedata-music-studio-error; loading → insidedata-music-studio-loading; populated → top of the studio section), plus a lede<p>"Generate ambient and contemplative music bound to the approved workflow classes. Inputs are envelope-guarded; outputs are provenance-tracked."- Consent panel (
.panel,data-music-consent) — single rights/terms confirmation checkbox; gates theconsentIdpassed to the card MusicCard(viaCardShell) — workflow-class picker + music inputs + live envelope verdict + preflight meta + Generate- Status line (
.panel,data-music-status) after a submit attempt
States#
- Loading —
data-music-studio-loadingpanel "Loading approved music workflow classes…" while the GET is in flight, or when the response lacks acard/provenance - Empty — N/A: the approved workflow-class set is server-authoritative
- Populated — consent panel +
MusicCardwith the approved workflow classes filtered againstMUSIC_WORKFLOW_CLASSES - Envelope deny (client) — when the current inputs violate
WORKFLOW_CLASS_ENVELOPES[workflowClass], adata-card-issueslist shows the violations and Generate is disabled (inputsValid === false) - Entitlement-denied / feature-unavailable — handled by
CardShell(data-card-status), as on curated-cards - Submit accepted — "Accepted — queued for music generation."
- Submit rejected (503) — "Music generation provider is not configured for this deployment yet."
- Submit rejected (403) — "Not available:
" - Submit rejected (422) — "The request is outside the approved envelope for this workflow class."
- Error (load failed) —
data-music-studio-error"Music generation is unavailable right now:" - Offline — load GET fails → error panel; submit POST → "Submission failed."
- Gated — proxy (creator) + ambient-audio entitlement (server)
- Standalone PWA — renders within shell
Interactions#
- Rights/terms consent (checkbox,
data-music-consent-checkbox)- Function: toggles
consentAccepted; setsconsentIdtoconsent-music-<userId>when accepted (empty otherwise), and builds theConsentConfirmationfor preflight - Keyboard: Space toggles; first in tab order
- Screen reader: announces "I confirm I have rights to any reference inputs and accept the curated-generation terms."
- Function: toggles
MusicCard inputs (all re-evaluate the envelope verdict live):
- Workflow class (approved) (select,
data-field="workflowClass") — options filtered to the intersection of the server's approved classes andMUSIC_WORKFLOW_CLASSES; selecting one resets tempo/duration defaults to the class envelope mid-points - Mood (select,
data-field="mood") — calm / reflective / invitational / cinematic / devotional - Tempo (BPM) (number,
data-field="tempoBpm") —min/maxbound to[envelope.minTempoBpm, envelope.maxTempoBpm]; label shows the envelope - Duration (s) (number,
data-field="durationSeconds") — bound to[envelope.minDurationSeconds, envelope.maxDurationSeconds] - Loopable (checkbox,
data-field="loopable") — disabled unlessenvelope.loopable === 'optional'; label shows the envelope rule - Key signature (select,
data-field="keySignature") — C-major / A-minor / D-major / G-major / E-minor / F-major / D-minor - Direction (textarea) — regenerate steer
- Generate (button,
data-card-action="generate")- Function:
submit(request)→POST /v1/isis/music/generatewith the fullMusicGenerationRequest(workflowClass,mood,tempoBpm,durationSeconds,loopable,keySignature,tenantId,userId,consentId) - Disabled when: envelope deny / preflight blocked / no consent / busy
- Function:
- Regenerate with direction, Variant compare, Send to editorial,
Takedown — shared
CardShellcontrols (the last two are() => undefinedhere)
Data & contracts#
- Reads:
GET /v1/isis/music/workflow-classes→{ workflowClasses: MusicWorkflowClass[], creatorTier, card: CardSpec | null, provenance: ProvenancePreview | null } - Writes:
POST /v1/isis/music/generate—MusicGenerationRequest; fail-closed 503 until a provider is configured (Suno is the registered provider whenOSHUN_SUNO_API_KEYis set) - Realtime: None.
- Caching: client fetch on mount
- Auth/role check: proxy (creator) + ambient-audio entitlement (server) +
workflow-class envelope guard (
evaluateMusicRequestEnvelope) client + server - Contracts:
@oshun-web/isis-music-generation(MUSIC_WORKFLOW_CLASSES,WORKFLOW_CLASS_ENVELOPES,evaluateMusicRequestEnvelope,MusicGenerationRequest,MusicWorkflowClass),@oshun-web/isis-curated-cards(CardSpec,ConsentConfirmation,ProvenancePreview,PENDING_COST_COEFFICIENTS)
Cross-references#
- Parent:
../studio-overview.md - Siblings:
studio-generation-curated-cards.md,studio-generation-nyx-3d.md,studio-generation-living-scene.md - Component sources:
apps/oshun/web/src/components/studio/generation/MusicStudio.tsx,MusicCard.tsx,CardShell.tsx
Open questions / known gaps#
-
tenantIdis''anduserIddefaults to'me'inMusicStudio— confirm both are meant to resolve from the session/proxy. -
onSendToEditorial/onTakedownare() => undefined; editorial routing from this surface is not wired.