---
path: /studio/notification-center
surface: studio
domain: collaboration
auth: signed-in + studio entitlement
source: apps/oshun/web/src/app/studio/notification-center/page.tsx
status: walked
last_walked:
  '2026-05-29 automated runtime walk (Playwright headless) — render, /v1 data
  (2xx), console/page-errors, expected content, screenshot verified; live
  screen-reader, touch, offline, and telemetry-delivery checks pending a manual
  AT pass. Evidence: WALKTHROUGH/results/runtime-sweep-2026-05-29.md; content
  re-verified 2026-06-03 against current source'
---

# Studio · Notification Center

## Purpose

Operate the notification center as a live, admin-scoped BFF console. The
workspace (`StudioNotificationCenterWorkspace`) wires a single **Notification
Routing Lane** to the real notification-routing engine via
`/v1/admin/studio/notification-routing`: during quiet hours it suppresses
anything below the minimum priority; otherwise it digests normal/low priority
notifications and delivers high-and-above immediately. This is a real domain
computation, not a simulation.

## Entry points

- **Studio root** (`/studio`) — discoverable as a collaboration workspace
- **Quick-action links** from sibling workspaces (activity/change feeds,
  real-time collaboration, commenting/annotation, review/approval,
  audit/compliance surfaces, enterprise tenant isolation, RBAC/permission policy)
- **Direct URL / bookmark** — yes

## Layout regions

`page.tsx` mounts `ShellLayout active="studio"`, renders
`StudioNotificationCenterWorkspace`, then a single `panel` containing 7 sibling
`quickAction` links. There is no Route Map panel on this page.

- **Shell header** — from `ShellLayout`
- **Workspace `<h1>`** — "Studio Notification Center" (`WorkspaceHeading`),
  followed by a `data-notification-center-summary` paragraph describing the
  routing
- **Notification Routing Lane** (`<h2>` `data-nr-lane-heading` "Notification
  Routing Lane") — the catalog/loading/unauthorized/error states, the route
  form, and the result
- **Quick-actions panel** — `panel` with 7 `quickAction` links to sibling
  workspaces (no self-link, no Route Map)

## States

- [ ] **Loading** — `data-nr-loading` "Loading notification router…" while the
      GET catalog request is in flight (`outcome === null`)
- [ ] **Unauthorized** — `data-nr-unauthorized` "Access restricted." on 401/403
      (admin scope required); shows the BFF message or "Studio admin scope
      required to route notifications."
- [ ] **Error** — `data-nr-error` "Could not load the notification router." on
      any non-OK catalog response or network failure
- [ ] **Ready (form)** — catalog loaded; `data-nr-enums` shows
      `{priorities.length} priorities · {routes.length} routes`, and the
      `data-nr-form` form renders
- [ ] **Result** — after a successful POST, `data-nr-result` renders the routing
      headline + per-notification table
- [ ] **Route validation error** — `data-nr-route-error` for invalid JSON,
      network unavailable, or a non-200 POST (shows the POST `detail`)

## Interactions

### Notification Routing Lane form

- [ ] **Notifications** (`textarea` `data-nr-payload`, `aria-label="notifications
      json"`) — JSON `{ context{ withinQuietHours }, policy{
      quietHoursMinPriority, digestLowPriority }, notifications[{ notificationId,
      priority, channel }] }`; seeded with a default payload
- [ ] **Route notifications** (`button` `data-nr-submit`, `type="submit"`) —
      `submitRoute` parses the JSON (rejecting invalid JSON with
      `data-nr-route-error`) and POSTs to
      `/v1/admin/studio/notification-routing/route`

### Result

- [ ] **Headline** (`data-nr-headline`) — `<data-nr-count="deliver_now"> ·
      <data-nr-count="digest"> · <data-nr-count="suppressed">`
- [ ] **Notifications table** (`data-nr-notifications`) — one
      `data-nr-notification-row` per notification with `data-notification-id`,
      `data-route`, `data-priority`, plus a `data-nr-notification-route` cell

### Quick actions

- [ ] Open Activity and Change Feeds workspace
- [ ] Open Real-Time Collaboration Substrate workspace
- [ ] Open Commenting and Annotation System workspace
- [ ] Open Review and Approval Workflows workspace
- [ ] Back to Audit and Compliance Surfaces workspace
- [ ] Back to Enterprise Tenant Isolation workspace
- [ ] Back to RBAC and Permission Policy UX workspace

## Data & contracts

- **GET catalog**: `/v1/admin/studio/notification-routing` — fetched on mount
  with `buildBffAuthHeaders()` and `cache: 'no-store'`; returns `{ priorities[],
  routes[] }`
- **POST verb**: `/v1/admin/studio/notification-routing/route` with body `{
  context{ withinQuietHours }, policy{ quietHoursMinPriority, digestLowPriority },
  notifications[{ notificationId, priority, channel }] }` → `{ result: {
  notifications[], summary{ deliverNowCount, digestCount, suppressedCount } } }`
  (200) or a `detail` error
- **Realtime**: none — request/response only
- **Client fetch**: direct `fetch` with `cache: 'no-store'`; no client-side
  caching
- **Auth/role check**: admin-scoped, fail-closed at the BFF (`admin:*` |
  `admin:studio`; 401/403); reached through the signed-in + studio route gate

## Cross-references

- Parent: [`../studio-overview.md`](../studio-overview.md)
- Sibling:
  [`studio-activity-change-feeds.md`](./studio-activity-change-feeds.md),
  [`studio-review-approval-workflows.md`](./studio-review-approval-workflows.md)
- Shell: [`../../shell/05-notifications.md`](../../shell/05-notifications.md)
- Component:
  `apps/oshun/web/src/components/studio/StudioNotificationCenterWorkspace.tsx`
- BFF route: `apps/oshun/bff/src/routes/admin-studio-notification-routing.ts`
- BFF store: `apps/oshun/bff/src/studio/notification-routing-store.ts`

## Open questions / known gaps

- [ ] Confirm whether routed notifications persist or dispatch to real channels,
      or whether the lane stays a stateless router
- [ ] Document the `quietHoursMinPriority` comparison (priority rank ordering)
      and the digest-batch flush cadence
