# Shell: Admin app shell

Source: `apps/oshun/admin/src/app/layout.tsx`,
`apps/oshun/admin/src/components/AdminShell.tsx`,
`apps/oshun/admin/src/components/AdminSidebar.tsx`,
`apps/oshun/admin/src/components/AdminHeader.tsx`,
`apps/oshun/admin/src/components/AdminCommandPaletteProvider.tsx`,
`apps/oshun/admin/src/components/AdminAssistantPanel.tsx`,
`apps/oshun/admin/src/components/AdminBreadcrumbProvider.tsx` (via
`@/lib/admin-breadcrumb-context`),
`apps/oshun/admin/src/lib/use-admin-density.ts`,
`apps/oshun/admin/src/app/error.tsx`, `apps/oshun/admin/src/app/loading.tsx`,
`apps/oshun/admin/src/app/not-found.tsx`

Global chrome for every admin workspace. Walk this once per release.

## Root layout (`app/layout.tsx`)

Minimal — no providers, just metadata and global CSS:

- [ ] **Title template** — `'%s · Oshun Admin'` (default `Oshun Admin`)
- [ ] **Description** — "Operator cockpit for Oshun review, governance, and
      operations."
- [ ] **`robots` metadata** — `index: false, follow: false` (with `googleBot`
      same) — admin must never be indexed
- [ ] **`viewport.themeColor`** — `#0f172a` (admin's dark theme color)
- [ ] **`width=device-width, initialScale=1`** — present
- [ ] **`<html lang="en">`** — set
- [ ] **No font loading** at the layout level (admin uses system fonts +
      whatever AdminShell.module.css declares)
- [ ] **No PWA bootstrap, no service worker, no manifest** — admin is not a PWA

## Composition order

The root layout renders only `{children}`. Each routed page composes the shell
itself by wrapping its body in `<AdminShell>`. Inside `AdminShell`:

1. `AdminBreadcrumbProvider` (outermost)
2. `AdminCommandPaletteProvider`
3. Shell `<div className={styles.shell} data-admin-density={density}>`
4. Skip link (`<a href="#admin-main">Skip to main content</a>`)
5. `AdminSidebar` — workspace nav
6. Main column:
   - `AdminHeader` — operator label, density toggle, assistant trigger
   - `<main id="admin-main" tabIndex={-1}>` — page body
7. `AdminAssistantPanel` — overlay; opens via shortcut or header button

## States

- [ ] **First paint** — no FOUC; shell CSS loads before first render
- [ ] **Density toggle** — `useAdminDensity` reads + persists;
      `data-admin-density` attribute set on `<html>`; toggle in `AdminHeader`
      flips it
- [ ] **Assistant guard allowed** — `evaluateAssistantInvocationGuard` returns
      `allowed: true` for `admin-web.assistant-panel`; header shows enabled
      assistant trigger
- [ ] **Assistant guard blocked** — header trigger disabled / shown blocked
      state; clicking still records guard decision
- [ ] **Active workspace** — sidebar item for `currentWorkspaceId` is
      highlighted; aria-current="page"
- [ ] **Operator name visible** —
      `operator?.displayName ?? session.session.payload.userId` rendered in
      header

## Sidebar (`AdminSidebar`)

- [ ] **Workspace list** — pulled from `listOshunAdminWorkspaceDefinitions()`
- [ ] **Grouped per `definition.group`** — `governance`, `safety`, `content`,
      `operations` (icon tokens per definition)
- [ ] **Access filter** — sidebar respects `canEnterAdminWorkspace(scopes, id)`
      — inaccessible workspaces are visible but indicated
- [ ] **Current workspace highlight** — visual + `aria-current="page"`
- [ ] **Keyboard navigation** — Tab/Shift+Tab; Enter activates link
- [ ] **Screen reader** — sidebar is a nav landmark; workspace label + group
      announced
- [ ] **Density-responsive** — `data-admin-density="dense"` vs default tightens
      vertical rhythm

## Header (`AdminHeader`)

- [ ] **Operator label** — displayName fallback to userId
- [ ] **Density toggle** — button toggles `data-admin-density` on `<html>`
  - Keyboard: focusable; Enter/Space activates
  - SR: announces "Compact mode on/off"
- [ ] **Assistant trigger** — opens `AdminAssistantPanel`
  - Disabled when guard says blocked
  - Shortcut label visible (`assistantPoint?.shortcut?.label`, e.g. `⌘/`)
  - Keyboard: shortcut from
    `matchAssistantInvocationShortcut(event, 'admin', 'admin-web')` opens panel
    via `openAssistant`
- [ ] **Command palette trigger** — opens `AdminCommandPaletteProvider`
  - Keyboard: shortcut (`⌘K` / `Ctrl+K` typical; verify exact binding)
- [ ] **Workspace title / breadcrumb** — uses `AdminBreadcrumbProvider`;
      workspaces register their breadcrumb segments

## Main content area

- [ ] **`<main id="admin-main" tabIndex={-1}>`** — skip-link target; tabIndex
      allows programmatic focus
- [ ] **Workspace body** rendered as `{children}` of AdminShell
- [ ] **Page-level scroll** — overflow on the main column, not the body

## Assistant panel (`AdminAssistantPanel`)

`apps/oshun/admin/src/components/AdminAssistantPanel.tsx`

- [ ] **Open** — header trigger or shortcut; `assistantOpen` state goes true
- [ ] **Guard decision visible** — `visibleAssistantGuardDecision` mounted;
      blocked reason shown if not allowed
- [ ] **Launch intent built** — via `buildAssistantLaunchIntent`
- [ ] **Context handoff sanitized** — via `sanitizeAssistantContextHandoff`
      with:
  - `activeDomain` = currentWorkspaceId
  - `activeRoute` = `window.location.pathname` (kept in sync with `popstate`)
  - `artifact` =
    `{ kind: 'admin.workspace', id: workspaceId, label, platformShell: 'admin', path }`
  - `memoryScope: 'session'`
  - `personaIdentity.personaId: 'admin-operator-copilot'`
  - `disclosureState` — aiGenerated, memoryScopeVisible, groundingStateVisible,
    safetyDisclosureVisible all true; personaIdentityVisible,
    syntheticVoiceVisible false
  - `permittedToolGrants: [{ toolId: 'admin.queue.read', permission: 'read' }, { toolId: 'admin.rationale.draft', permission: 'write' }]`
- [ ] **Close** — `assistantOpen` false; ESC closes (verify)

## Breakpoints

- [ ] **Desktop ≥ 1280 px** — sidebar + main column layout
- [ ] **Tablet 768–1280 px** — sidebar may collapse to icons (verify against
      AdminShell.module.css)
- [ ] **Mobile ≤ 768 px** — admin is desktop-first; mobile experience may be
      limited / not in V1 scope

## Error / Loading / Not-found

`apps/oshun/admin/src/app/error.tsx`, `loading.tsx`, `not-found.tsx`

- [ ] **`error.tsx`** — segment-level error boundary; heading "Something broke
      inside the admin shell"; body mentions failure logged with `error.digest`;
      "Retry workspace" button calls `reset()`
- [ ] **`loading.tsx`** — `<main role="main">` with "Loading operations
      cockpit…" in `aria-live="polite"`; no skeleton; spinner-less
- [ ] **`not-found.tsx`** — h1 "Workspace not found"; body explains the
      workspace is not part of the canonical operations cockpit; link back to
      `/`; `metadata.title = 'Workspace not found'`

## Density and rhythm

`apps/oshun/admin/src/lib/use-admin-density.ts`

- [ ] **`useAdminDensity()`** — hook initialized once at shell mount
- [ ] **Persistence** — localStorage key; survives reload
- [ ] **`data-admin-density` attribute** on `<html>` — read by CSS for
      compact/normal rhythm
- [ ] **Toggle action in header** — calls `toggleDensity()`; SR feedback

## Cross-references

- [02-routing-layouts.md](./02-routing-layouts.md) — middleware behavior
- [03-auth-session.md](./03-auth-session.md) — session cookie + scopes
- [04-workspace-pattern.md](./04-workspace-pattern.md) — page-level pattern
- Workspace definitions: `libs/oshun/navigation/src/admin-ia.ts`
  (`OSHUN_ADMIN_WORKSPACE_MODEL`)
- Assistant invocation: `@oshun/shell-assistant/invocation-points` and
  `@oshun/shell-assistant/context-handoff`
- Auth: `@oshun/auth-client` (`OshunAdminOperatorView`)

## Open questions / known gaps

- [ ] Confirm command-palette shortcut binding (`⌘K`? `Ctrl+K`? both?)
- [ ] Document the exact set of assistant scopes / entitlements / policy grants
      required (the AdminShell sends `entitlements: ['assistant.admin']` and
      `policyGrants: ['assistant:operations']`)
- [ ] Verify mobile responsive behavior — admin is desktop-first but operators
      may use tablets
- [ ] Document the sidebar's collapse / expand state (if any)
