---
path: /operator/admin
surface: operator
domain:
  operator (house — incidents, citation drift, tenant requests, support routing)
auth: signed-in
source: apps/oshun/web/src/app/operator/admin/page.tsx
status: walked
last_walked:
  '2026-07-03 Operator admin inbox decision addendum — page/header, filter-chip,
  queue row-cell, detail-card, drift-source, decision-panel, reject/retract
  rationale gate with BFF readback, incident-action, and anonymous
  redirect-before-render coverage in apps/oshun/web/e2e/incident-triage.spec.ts
  and apps/oshun/web/e2e/operator-editorial-review-approval.spec.ts. Evidence:
  WALKTHROUGH/results/v1-real-infra-run-2026-06-22.md §88'
---

# Operator · Admin · inbox

## Purpose

The operator admin inbox — "Lilith Admin · House · Inbox" in the plate-of-record
register. A single page that surfaces incidents, citation drift, tenant
requests, and support routing as a triage queue with a detail pane.
Robots-noindex.

## Entry points

- **From `/operator`** (the index) — surface link routed via
  `getOperatorIndex()`
- **Profile menu → Operator → Admin**
- **Direct URL** — session-gated only (`proxy.ts` redirects sessionless →
  `/welcome?redirect=`); no operator-role gate in the proxy
- **Auto-routed incidents** — alerts/auto-generated INC tickets deep-link here

`robots: { index: false, follow: false }` keeps it out of public SEO.

## Layout regions

The page uses Lilith design primitives directly (`LGraphPaper`, `LOperatorBar`,
`LCard`, `LCode`, `LChip`, `LEyebrow`, `LDataCell`, `LSeverityPill`, `L`,
`SERIF_L`, `SANS_L`) rather than `ShellLayout`.

- **Top**: `LOperatorBar` — product="Lilith Admin", section="House",
  breadcrumb="Inbox"
- **Header band**:
  - Eyebrow accent "Admin · inbox"
  - H1-like (Garamond 28) "_6_ open · 1 critical" (italic open count)
  - Right-aligned ghost link: `Audit log` → `/operator/audit`
- **Admin inbox console** (`data-operator-admin-console`) — client island grid
  (2fr / 1fr) with filterable queue and selected-incident detail.
  - **Filter chips** (`data-operator-filter`) — real buttons: `All`,
    `Editorial`, `Tenant`, `Auto`; active state is recorded in
    `data-operator-active-filter`.
  - **Items column (2fr)** (`data-operator-inbox-list`) — list rows, each laid
    out as `110px 90px 1fr 130px 100px` (code / severity / title+sub / assignee
    / when):
    - `LCode` ticket code
    - `LSeverityPill level={sev}` — S1 → "critical", S2 → "degraded", anything
      else → "advisory"
    - Title (Garamond 17 medium) + italic subtitle (Garamond 13 muted)
    - `LDataCell mono` assignee
    - `LDataCell mono` time, right-aligned
    - Each row exposes `data-operator-item`, category/selected attributes, and
      cell anchors for `code`, `severity`, `title`, `subtitle`, `assignee`, and
      `when`.
  - **Detail card (1fr)** (`data-operator-incident-detail="<INC>"`) —
    accent-bordered, aligned `start`:
    - `LCode` header, e.g. "INC-2041 · 14:22 · auto · Lilith"
    - Garamond 22 selected title
    - Italic selected subtitle
    - Severity pill, optional public-scene deep-link for abuse-derived INCs, and
      optional provenance handoff for citation-drift/generated-artifact INCs
    - **Drift sources** list — `LEyebrow` heading + rows mapped from
      `data.driftSources` only for the citation-drift incident (each
      `[source, when, weight]`: italic source + `LCode` when + weight)
    - **`AdminInboxDecisionPanel`** (`incidentId="INC-2041"`) — three verdicts:
      `approved-republish` / `request-rereview` / `rejected-retract`, keyed to
      the currently selected incident.
    - **`AdminInboxIncidentActions`** — acknowledge, assign, escalate, snooze,
      and resolve controls against the selected incident.
    - **`AdminEvidenceQueuePanel`** (`data={data.evidenceReview}`) renders below
      the grid (operator.tsx:661)

## States

- [x] **Loading** — source-verified: no local `loading.tsx`; `page.tsx` awaits
      `getAdminInbox()` server-side before rendering.
- [x] **Anonymous user** — redirected to `/welcome?redirect=` (`proxy.ts`)
      before `[data-operator-admin-page]`, rows, or the decision panel render
      (Playwright, 2026-06-25)
- [x] **Signed-in user** — full inbox renders (session-gated only; no
      operator-role gate in the proxy)
- [x] **Inbox with items + selection** — selected row has accent ring; detail
      card and decision/action panels match the selected incident
- [ ] **Inbox with no items** — no full-page empty fixture is available; filter
      empty state copy exists in `AdminInboxQueue` but is not reachable from the
      seeded all/editorial/tenant/auto categories.
- [ ] **`getAdminInbox()` fails** — no local segment `error.tsx` is present;
      `getAdminInbox()` fail-softs live abuse-report fetches back to the static
      fixture.
- [ ] **Offline** — write-heavy surface; queued or disabled (verify)
- [ ] **Different operator personas** — items filtered by role

## Interactions

### Filter chips

- [x] **`All` chip** (accent active) — shows all six seeded INC rows
- [x] **`Editorial` chip** — filters to the two editorial-class rows
- [x] **`Tenant` chip** — filters to the two tenant-class rows
- [x] **`Auto` chip** — filters to the two auto-generated rows

Filter state is component-local. It updates `data-operator-active-filter` and
`data-operator-inbox-count`; it does not write URL state.

### Items list

- [x] **Each row** — click selects; selected row paints with
      `data-operator-item-selected="true"`; detail card, decision panel, and
      incident actions swap to the selected incident.
  - Keyboard: row is `role="button"` and Enter/Space selects; arrow-key roving
    focus is not implemented.
  - Touch ≥ 44×44 px by row padding/grid height.
- [x] **Row cells** — all six fixture rows are asserted cell-by-cell (code,
      severity, title, subtitle, assignee, when) via `data-operator-item-cell`.
- [x] **`LSeverityPill`** — non-interactive; conveys severity via shape + label,
      not color alone
- [x] **`LCode`** ticket code — non-interactive display

### Detail card

- [x] **`AdminInboxDecisionPanel`** verdict picker — three radio-style verdicts
      (`approved-republish`, `request-rereview`, `rejected-retract`); posts to
      the real audited BFF decision route in E2E and renders
      `data-decision-audit-id`. The reject/retract branch now proves no/short
      rationale leaves the action placeholder-disabled with no browser POST;
      valid rationale records and BFF GET rehydrates the same rejected decision,
      rationale, and audit id.
- [x] **Drift-sources list** — non-interactive source / when / weight rows,
      visible for the default citation-drift incident and removed when a
      non-drift row is selected.
- [x] **Provenance handoff** — citation-drift rows can render
      `data-operator-incident-provenance-link="<INC>"`; `INC-2041` links to
      `/operator/admin/isis/provenance?inc=INC-2041` and is covered by
      `citation-drift-resolution.spec.ts`.
- [x] **`AdminEvidenceQueuePanel`** — evidence review queue below the grid;
      Playwright covers source/retrieval inspection, citation validation, and
      grounded-report promotion.
- [x] **Incident lifecycle actions** — acknowledge, escalate, and resolve are
      covered through real BFF writes; server rationale floors are covered by
      pure-HTTP tests.
- [ ] **Cross-reference link** to fixture artifact codes like `TR-047` — not
      present for the static drift fixture. Abuse-derived rows can expose a
      public-scene "Open offering" link.
- [x] **Audit-trail visible** — the page exposes an `Audit log` entry point and
      decision writes render the real audit id.

## E2E coverage

- `apps/oshun/web/e2e/incident-triage.spec.ts` covers signed-in
  `/operator/admin`, page/header/audit-entry anchors, filter chips, all six
  fixture rows cell-by-cell, detail-card selection, drift-source rows,
  decision-panel rationale gates and real audited BFF writes, incident lifecycle
  actions with an opt-in BFF harness reseed, server-side rationale-floor
  failures, and anonymous redirect-before-render.
- `apps/oshun/web/e2e/operator-editorial-review-approval.spec.ts` covers the
  shared decision panel's editorial review approve/reopen path, reject/retract
  rationale gate with audited BFF readback, and local rehydrate behavior.
- `apps/oshun/web/e2e/operator-admin-evidence-queue.spec.ts` covers the evidence
  review queue below the inbox grid.
- `apps/oshun/web/e2e/citation-drift-resolution.spec.ts` covers the
  citation-drift row's "Open provenance" handoff from the selected incident
  detail into `/operator/admin/isis/provenance?inc=INC-2041`.

## Data & contracts

- **Reads**: `getAdminInbox()` from `@/lib/lilith-data/operator-depth` returning
  `AdminInboxData`. It prepends live public-abuse reports from
  `/v1/admin/abuse-reports` when present and otherwise falls back to the static
  fixture baseline. Rows may carry `shortCode` for scene review or
  `provenanceHref` for provenance inspection handoff.
- **Writes**: `AdminInboxDecisionPanel` posts to
  `/v1/admin/operator-inbox/:id/decision` and `.../reopen`;
  `AdminInboxIncidentActions` posts to `/v1/admin/operator-incidents/:id/<verb>`
  for acknowledge, assign, escalate, snooze, and resolve.
- **Realtime**: incidents stream in via `oshun-sw-update` or similar push
  channel (verify against shell/05-notifications.md)
- **Caching**: server-rendered; SW excludes operator routes from runtime cache
  (verify)
- **Auth/role check**: session-gated only (`proxy.ts` redirects sessionless →
  `/welcome?redirect=`); no operator-role gate in the proxy

## Cross-references

- Parent: [`operator-overview.md`](./operator-overview.md)
- Shell: [`shell/04-auth-session.md`](../shell/04-auth-session.md) — role gates
- Sibling operator routes:
  - [`operator-studio.md`](./operator-studio.md) — editorial review queue
  - [`operator-tenant.md`](./operator-tenant.md) — tenant console
  - [`operator-metis.md`](./operator-metis.md) — institutional dashboard
  - [`operator-admin-isis-provenance.md`](./operator-admin-isis-provenance.md) —
    provenance inspector (sub-route)
- Component sources:
  - `apps/oshun/web/src/components/lilith/operator.tsx` (line 486: `AdminInbox`)
- Feature spec: [`V1/features.md`](../../V1/features.md)
- Architecture:
  [`V1/ARCHITECTURE.md`](../../V1/ARCHITECTURE.md#operator-surfaces)

## Open questions / known gaps

- [x] Audit the detail-card action set in current source (acknowledge, assign,
      escalate, snooze, resolve) and cover acknowledge/escalate/resolve in
      Playwright (2026-06-25)
- [x] Confirm filter chips wire to a real handler (component-local filter state,
      no URL state)
- [ ] Document the realtime channel (Psyche topic) that feeds new incidents to
      this view
- [ ] Confirm SW caching policy for `/operator/*` (likely network-only,
      audit-stamped writes)
- [ ] An operator-role gate appears absent — `proxy.ts` only redirects
      sessionless users to `/welcome`; no role check in the proxy or page
      components (possible gap vs intended scope)
