---
path: /operator/metis
surface: operator
domain: operator (metis — institutional dashboard)
auth: signed-in
source: apps/oshun/web/src/app/operator/metis/page.tsx
status: walked
last_walked:
  '2026-06-25 Metis stable-anchor addendum —
  page/header/KPI/review-row/side-panel anchors, full row-cell assertions,
  OneRoster sync card, tutor telemetry, Themis flags, and anonymous
  redirect-before-render covered in
  apps/oshun/web/e2e/metis-byom-ingest.spec.ts. Evidence:
  WALKTHROUGH/results/v1-real-infra-run-2026-06-22.md §87; 2026-06-26 Themis
  appeal review addendum — live queue/read/resolve UI covered in
  apps/oshun/web/e2e/operator-metis-themis-review.spec.ts; 2026-06-27 OneRoster
  live-card addendum — real apply -> /operator/metis card read-back covered in
  apps/oshun/web/e2e/tenant-oneroster-metis-card.spec.ts'
---

# Operator · Metis · institutional

## Purpose

The institutional Metis dashboard for school/tenant operators: lesson review
queue, OneRoster sync, tutor telemetry, and Themis academic-integrity flags.
"The schoolroom, behind the curtain." Robots-noindex. Backed by
`getMetisOperator()` and rendered by `MetisOperator` (defined at metis.tsx:940
in the ~1115-LOC Lilith design-system room).

## Entry points

- **From `/operator`** (index) — surface card
- **Profile menu → Operator → Metis**
- **Direct URL** — session-gated only (`proxy.ts` redirects sessionless →
  `/welcome?redirect=`); no operator-role gate in the proxy
- **OneRoster sync alerts** — auto-deep-link from notification → here
- **Themis flag notifications** — academic integrity issues route here

## Layout regions

The component uses Lilith primitives (`LGraphPaper`, `LOperatorBar`, `LEyebrow`,
`LCode`, `L`, `SERIF_L`) and exposes stable `data-operator-metis-*` anchors for
the page, header, KPI grid, review queue, side panel, OneRoster card, tutor
card, and Themis card.

- **Top**: `LOperatorBar` — product="Oshun Studio", section="Metis",
  breadcrumb="Authoring · institutional"
- **Header band** (`data-operator-metis-header`):
  - Eyebrow accent "Authoring · Metis · institutional"
  - Garamond 28 line: "The schoolroom, behind the curtain."
  - Right-aligned `LCode` muted: "tenant · concord public library · 14 schools"
- **KPI grid** (`data-operator-metis-kpi-grid`) — 5-column, inline-borders,
  paper background, italic Garamond 32 values; values of "✓" render in `L.ok`
  (success green). Each cell carries `data-operator-metis-kpi-cell` and
  `data-operator-metis-kpi-value`.
- **Two-column section** (1.5fr 1fr):
  - **Left (1.5fr)**: "Generated lessons · review queue" — table with columns
    `Course / Lesson / Sources / Quality / State`, dotted row dividers, row
    anchors for course/state, and cell anchors for course, lesson, sources,
    quality, and state.
  - **Right (1fr)** (`data-operator-metis-side-panel`): OneRoster sync card
    (fixture fields for normal signed-in renders, live BFF `Last commit`,
    `Tenants`, `Users`, and `Enrollments` fields for admin renders), tutor
    telemetry 24-hour sparkline with `tutor · 1,820`, Themis flags
    (`Originality < 0.6`, `Tutor wrote answer`, `Quotation unverified`), and the
    live Themis appeal review panel.
- **Themis appeal review** (`data-operator-metis-themis-appeal-panel`) — client
  panel fetches `GET /v1/metis/integrity/appeals` with operator scope, renders
  each real appeal workspace row with `data-operator-metis-themis-appeal-row`,
  `data-operator-metis-themis-appeal-id`, status, subject, claimant, evidence
  count, timeline deadline summary, due-process summary, resolution note field,
  and a resolve submit region with
  `data-operator-metis-themis-appeal-submit-state`.
- Total page padding `24px 28px 60px`

## States

- [x] **Loading** — source-verified: no local `loading.tsx`; `page.tsx` awaits
      `getMetisOperator()` server-side before rendering.
- [x] **Anonymous user** — redirected to `/welcome?redirect=` by `proxy.ts`
      before `[data-operator-metis-page]` or review rows render (Playwright,
      2026-06-25)
- [x] **Signed-in user** — renders (session-gated only; no operator-role gate in
      the proxy)
- [x] **Tenant set + healthy** — KPI grid populated; all five queue rows visible
      with stable row/cell anchors (Playwright, 2026-06-25)
- [ ] **Empty review queue** — table without body rows
- [x] **Themis flags present** — side panel shows all three fixture flag rows
      (Playwright, 2026-06-25)
- [x] **Themis appeal review present** — when the BFF contains an academic-
      integrity appeal, the operator panel renders the live dispute row,
      evidence count, timeline deadline summary, due-process summary, and
      resolve controls (Playwright, 2026-06-26).
- [x] **OneRoster sync status** — sync card surfaces fixture last-sync,
      class/student deltas, and zero conflicts for regular signed-in renders
      (Playwright, 2026-06-25).
- [x] **Live OneRoster projection** — with a tenant-console admin cookie,
      `getMetisOperator()` reads `GET /v1/admin/lms/oneroster`; after a real
      apply the card exposes BFF source, aggregate counts, and the committed
      tenant summary (Playwright, 2026-06-27).
- [ ] **OneRoster out of sync warning** — no warning variant is present in the
      current fixture.
- [ ] **`getMetisOperator()` fails** — no local segment `error.tsx` is present;
      failure boundary remains unverified.
- [ ] **Different tenant context** — page re-renders with new institution name +
      KPIs

## Interactions

### Header band

- [x] **Tenant code** (`LCode`) — non-interactive label; Playwright verifies the
      exact tenant label via `data-operator-metis-tenant-code`

### KPI grid

- [x] **Each KPI cell** — non-interactive numeric display; Playwright verifies
      all five fixture labels and values via `data-operator-metis-kpi-value`.
      `✓` renders in success green.
- [ ] **Keyboard / SR**: cells are part of natural reading order; consider a
      list semantic if these are status-bearing

### Review queue table

- [x] **Column headers** (`Course / Lesson / Sources / Quality / State`) —
      non-interactive
- [x] **Each row** — all five fixture rows render as non-link, non-button rows;
      the separate `MetisByomDecisionPanel` handles approve/request/reject for
      the first pending item.
- [x] **Sources column** — citation-count plus source-quality label verified for
      all rows.
- [x] **Quality column** — Sophia-style band plus decimal score verified for all
      rows.
- [x] **State column** — `LDecisionChip` text verified as `approved`, `review`,
      and `changes`.

### OneRoster sync card

- [x] **Sync status** — `Last sync`, `Classes`, `Students`, and `Conflicts` rows
      verified through stable field anchors.
- [x] **Live projection read-back** — a real OneRoster apply is visible through
      `data-operator-metis-oneroster-source="bff"` and per-tenant row anchors.
- [ ] **Retry sync** action — no retry action is present in the current
      component.

### Themis flags

- [x] **Flag rows** — academic-integrity flags for originality, tutor-written
      answers, and unverified quotations verified through stable field anchors.
- [x] **Appeal review read** — `MetisThemisAppealReviewPanel` loads the live
      operator queue from `GET /v1/metis/integrity/appeals`; the row renders the
      canonical dispute, evidence, timeline, and due-process profile.
- [x] **Resolve appeal** — operator enters a resolution note; blank notes keep
      the submit region blocked, valid notes call
      `POST /v1/metis/integrity/appeals/:appealId/resolve`, and the row updates
      to `status=closed` with the returned note.
- [ ] **Dedicated uphold / dismiss / escalate verdict actions** — distinct
      verdict-class adjudication controls remain outside the current component;
      teacher override/upheld verdict classes are covered by direct real-BFF
      HTTP in `integrity-appeal.spec.ts`.

### Tutor telemetry

- [x] **Tutor metrics** — 24-hour sparkline plus `tutor · 1,820` label verified
      through stable anchors.

## E2E coverage

- `apps/oshun/web/e2e/metis-byom-ingest.spec.ts` covers signed-in
  `/operator/metis`, the page/header anchors, tenant label, all KPI labels and
  values, review table headers, all five review rows cell-by-cell, state-chip
  distribution, OneRoster sync card, tutor telemetry sparkline, Themis flag
  fields, and anonymous redirect-before-render.
- `apps/oshun/web/e2e/metis-byom-decision.spec.ts` covers the real
  approve/request-changes/reject decision island over
  `/v1/admin/metis/byom/decision`.
- `apps/oshun/web/e2e/operator-metis-themis-review.spec.ts` seeds a real
  academic-integrity verdict + appeal through the BFF, opens `/operator/metis`,
  verifies the live appeal row, and resolves it through the browser panel.
- `apps/oshun/web/e2e/tenant-oneroster-metis-card.spec.ts` commits a clean
  OneRoster import through the real BFF, opens `/operator/metis`, and verifies
  the live roster card source/count attributes plus the committed tenant summary
  row.

## Data & contracts

- **Reads**: `getMetisOperator()` from `@/lib/lilith-data/metis-depth` returning
  `MetisOperatorData` (kpi, queue, OneRoster status). It falls back to the
  static fixture for non-admin/missing-BFF renders, and for tenant-console admin
  renders adapts `GET /v1/admin/lms/oneroster` into the OneRoster card. The live
  Themis appeal panel additionally reads `GET /v1/metis/integrity/appeals` from
  the BFF.
- **Writes**: BYOM approval/request-changes/reject through
  `MetisByomDecisionPanel`; Themis appeal resolution through
  `POST /v1/metis/integrity/appeals/:appealId/resolve`. OneRoster retry is not
  present in the route component.
- **Realtime**: Themis flags may stream in; verify Psyche channel
- **Caching**: server-rendered; no SW caching for operator routes
- **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)
- Sibling operator routes:
  - [`operator-admin.md`](./operator-admin.md)
  - [`operator-studio.md`](./operator-studio.md)
  - [`operator-tenant.md`](./operator-tenant.md)
- Customer-side Metis: [`../customer/08-metis/`](../customer/08-metis/)
- Themis is referenced in V1/features.md as the academic-integrity adjudication
  substrate for Metis assessments
- Component source: `apps/oshun/web/src/components/lilith/metis.tsx` (line 940:
  `MetisOperator`)
- Feature spec: [`V1/features.md`](../../V1/features.md#metis)
- Architecture: [`V1/ARCHITECTURE.md`](../../V1/ARCHITECTURE.md#metis)

## Open questions / known gaps

- [x] Walk the bottom half of `MetisOperator` to enumerate the OneRoster sync
      card, tutor telemetry, and Themis flag list (2026-06-25)
- [x] Confirm the relationship between this operator surface and the Themis
      adjudication backend; document the contract (2026-06-26)
- [ ] Document the OneRoster sync action flow (retry, force-resync, reset)
- [ ] Verify tenant-context switching is wired (the
      `tenant · concord     public library · 14 schools` label suggests a
      multi-tenant view; no switcher visible in the fixture)
- [ ] An operator-role gate appears absent — `proxy.ts` only redirects
      sessionless users to `/welcome`; no role check in the proxy or page
      component (possible gap vs intended scope)
