---
path: /studio/generation/nyx-3d
surface: studio
domain: generation
auth: signed-in + creator (proxy-gated; not anonymous, not search-indexed)
source: apps/oshun/web/src/app/studio/generation/nyx-3d/page.tsx
status: walked
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'
---

# Studio · Generation · Nyx 3D Briefing

## Purpose

The approved Nyx 3D sky-event briefing surface (§24.6 / §24.8). Renders the
`Nyx3DBriefingCard` against upcoming, ephemeris-derived sky events
(`GET /v1/isis/nyx-3d/events`) and submits through
`POST /v1/isis/nyx-3d/generate`. The 3D pipeline class is server-authoritative —
the client only sends the chosen `eventId`; the backend runs the real sky-event
validation + entitlement, then enqueues (Meshy is the registered provider when
`OSHUN_MESHY_API_KEY` is set), fail-closed 503 otherwise.

## Entry points

- **Direct URL / bookmark** — `/studio/generation/nyx-3d` (proxy-gated to authed
  creators; `robots: noindex/nofollow/noarchive/nosnippet`)
- **Studio shell nav** — `active="studio"` (shortcut `6`), generation sub-area
- **Sibling generation surfaces** — curated-cards / music / living-scene
- **Nyx domain bridge** — conceptually downstream of the customer Nyx sky
  surfaces (`/nyx/tonight`, `/nyx/sky-almanac`); the events here are the same
  ephemeris events surfaced for briefing

## Layout regions

- **Header**: shell header (`ShellLayout active="studio"`)
- **Main**: `<section data-nyx-3d-studio>`
  - **`<h1>Nyx 3D Briefing</h1>`** — page heading, rendered in all four states
    (error → inside `data-nyx-3d-error`; loading → inside `data-nyx-3d-loading`;
    empty → inside `data-nyx-3d-empty`; populated → top of the studio section),
    plus a lede `<p>` "Generate a 3D briefing for an upcoming sky event. The
    pipeline class is server-authoritative; outputs are provenance-tracked."
  - **Consent panel** (`.panel`, `data-nyx-3d-consent`) — single terms checkbox
  - **`Nyx3DBriefingCard`** (via `CardShell`) — sky-event picker +
    pipeline-class readout + preflight meta + Generate
  - **Status line** (`.panel`, `data-nyx-3d-status`) after a submit attempt

## States

- [ ] **Loading** — `data-nyx-3d-loading` panel "Loading upcoming sky events…"
      while the GET is in flight, or when the response lacks `card` /
      `provenance`
- [ ] **Empty** — `data-nyx-3d-empty` panel "No upcoming sky events are
      available for a 3D briefing right now." when `events.length === 0` (honest
      empty — no fabricated events)
- [ ] **Populated** — consent panel + `Nyx3DBriefingCard` with the event select
      defaulted to the first upcoming event
- [ ] **Entitlement-denied / feature-unavailable** — handled by `CardShell`
- [ ] **Submit accepted** — "Accepted — queued for 3D briefing generation."
- [ ] **Submit rejected (503)** — "The 3D sky-briefing provider is not
      configured for this deployment yet."
- [ ] **Submit rejected (403)** — "Not available: <message>"
- [ ] **Submit rejected (404)** — "That sky event is no longer upcoming."
- [ ] **Submit rejected (422)** — "This briefing is outside the approved
      envelope."
- [ ] **Error (load failed)** — `data-nyx-3d-error` "The Nyx 3D briefing surface
      is unavailable right now: <message>"
- [ ] **Offline** — load GET fails → error panel; submit POST → "Submission
      failed."
- [ ] **Gated** — proxy (creator) + entitlement (server)
- [ ] **Standalone PWA** — renders within shell

## Interactions

- [ ] **Terms consent** (checkbox, `data-nyx-3d-consent-checkbox`)
  - Function: toggles `consentAccepted`; sets `consentId` to
    `consent-nyx3d-<userId>` when accepted
  - Screen reader: announces "I accept the curated-generation terms for this 3D
    sky briefing."
- [ ] **Sky event** (select, `data-field="eventId"`)
  - Function: chooses the `SkyEventBriefing`; options are
    `availableEvents[].eventLabel`; selecting one shows its approved pipeline
    class (`<code>{approvedPipelineClassId}</code>`)
  - `inputsValid` is true only when a listed event is selected
- [ ] **Direction** (textarea) — regenerate steer
- [ ] **Generate** (button, `data-card-action="generate"`)
  - Function: `submit({ eventId, pipelineClassId })`; **only `eventId` is sent**
    on the wire (`POST /v1/isis/nyx-3d/generate` body `{ eventId }`) — the
    pipeline class is resolved server-side
  - Disabled when: no event selected / preflight blocked / no consent / busy
- [ ] **Regenerate with direction**, **Variant compare**, **Send to editorial**,
      **Takedown** — shared `CardShell` controls (last two `() => undefined`)

## Data & contracts

- **Reads**: `GET /v1/isis/nyx-3d/events` →
  `{ events: SkyEventBriefing[], creatorTier, card: CardSpec | null, provenance: ProvenancePreview | null }`
  where `SkyEventBriefing = { eventId, eventLabel, approvedPipelineClassId }`
- **Writes**: `POST /v1/isis/nyx-3d/generate` — `{ eventId }` only;
  pipeline-class server-authoritative; fail-closed 503 until provider configured
- **Realtime**: _None._
- **Caching**: client fetch on mount
- **Auth/role check**: proxy (creator) + entitlement (server) + sky-event
  validity (server, "no longer upcoming" → 404)
- **Contracts**: `@oshun-web/isis-curated-cards` (`CardSpec`,
  `ConsentConfirmation`, `ProvenancePreview`, `PENDING_COST_COEFFICIENTS`);
  `SkyEventBriefing` defined inline + in `Nyx3DBriefingCard.tsx`

## Cross-references

- Parent: [`../studio-overview.md`](../studio-overview.md)
- Siblings:
  [`studio-generation-curated-cards.md`](./studio-generation-curated-cards.md),
  [`studio-generation-music.md`](./studio-generation-music.md),
  [`studio-generation-living-scene.md`](./studio-generation-living-scene.md)
- Customer Nyx surfaces:
  [`../../customer/06-nyx/nyx-tonight.md`](../../customer/06-nyx/nyx-tonight.md),
  [`../../customer/06-nyx/nyx-sky-almanac.md`](../../customer/06-nyx/nyx-sky-almanac.md)
- Component sources:
  `apps/oshun/web/src/components/studio/generation/Nyx3DStudio.tsx`,
  `Nyx3DBriefingCard.tsx`, `CardShell.tsx`

## Open questions / known gaps

- [ ] `tenantId` is `''` and `userId` defaults to `'me'` in `Nyx3DStudio` —
      confirm session/proxy resolution.
- [ ] Confirm the `eventId` set here is sourced from the same ephemeris that
      feeds `/v1/nyx/tonight` and `/v1/nyx/sky-almanac`, so a customer-visible
      event and its 3D briefing always agree.
