V1 Web PWA · Surface walkthrough

Operator · Metis · institutional

A per-surface walkthrough of the V1 Web PWA operator surface: layout, states, interactions, data, and cross-references.

walked
9sections6 minread

On this page

Context. surface operator · domain operator (metis — institutional dashboard) · route /operator/metis · auth signed-in · source apps/oshun/web/src/app/operator/metis/page.tsx

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

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#

  • Loading — source-verified: no local loading.tsx; page.tsx awaits getMetisOperator() server-side before rendering.
  • Anonymous user — redirected to /welcome?redirect= by proxy.ts before [data-operator-metis-page] or review rows render (Playwright, 2026-06-25)
  • Signed-in user — renders (session-gated only; no operator-role gate in the proxy)
  • 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
  • Themis flags present — side panel shows all three fixture flag rows (Playwright, 2026-06-25)
  • 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).
  • OneRoster sync status — sync card surfaces fixture last-sync, class/student deltas, and zero conflicts for regular signed-in renders (Playwright, 2026-06-25).
  • 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#

  • Tenant code (LCode) — non-interactive label; Playwright verifies the exact tenant label via data-operator-metis-tenant-code

KPI grid#

  • 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#

  • Column headers (Course / Lesson / Sources / Quality / State) — non-interactive
  • 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.
  • Sources column — citation-count plus source-quality label verified for all rows.
  • Quality column — Sophia-style band plus decimal score verified for all rows.
  • State columnLDecisionChip text verified as approved, review, and changes.

OneRoster sync card#

  • Sync statusLast sync, Classes, Students, and Conflicts rows verified through stable field anchors.
  • 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#

  • Flag rows — academic-integrity flags for originality, tutor-written answers, and unverified quotations verified through stable field anchors.
  • Appeal review readMetisThemisAppealReviewPanel loads the live operator queue from GET /v1/metis/integrity/appeals; the row renders the canonical dispute, evidence, timeline, and due-process profile.
  • 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#

  • 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#

Open questions / known gaps#

  • Walk the bottom half of MetisOperator to enumerate the OneRoster sync card, tutor telemetry, and Themis flag list (2026-06-25)
  • 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)