V1 Web PWA · Surface walkthrough

Operator · index

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

walked
9sections4 minread

On this page

Context. surface operator · domain operator (review, safety, support, persona/model/rights/provenance, incidents, launch) · route /operator · auth signed-in · source apps/oshun/web/src/app/operator/page.tsx

Last walked. 2026-06-25 operator-index route-state addendum — Playwright real-dev-infra coverage now proves the top operator bar product/section/breadcrumb are non-interactive, the page count/title/rule/eyebrow anchors, every surface code/title/description/open label and stable href, focused-link affordance, and the mobile one-column grid with no horizontal overflow. Evidence: WALKTHROUGH/results/v1-real-infra-run-2026-06-22.md §101; spec: apps/oshun/web/e2e/operator-surfaces.spec.ts. Previous: 2026-06-25 operator-surfaces direct-route addendum — Playwright real-dev-infra coverage for signed-in /operator render, anonymous proxy redirect, all four shipped surface links/codes/hrefs, 44 px link targets, and keyboard Enter activation into /operator/tenant. Evidence: WALKTHROUGH/results/v1-real-infra-run-2026-06-22.md §81; spec: apps/oshun/web/e2e/operator-surfaces.spec.ts.

Purpose#

The operator product's index page — "Plate of record. Four operator surfaces." Surfaces the four distinct operator products (Studio, Tenant, Admin, etc.) as cards routed from getOperatorIndex(). Robots-noindex (metadata.robots). See V1/features.md and V1/ARCHITECTURE.md's Operator surfaces section.

Entry points#

  • Profile menu → Operator — primary entry
  • Direct URL — session-gated only (proxy.ts redirects sessionless → /welcome?redirect=); no operator-role gate in the proxy
  • Operator deep link from internal docs / runbooks
  • Cross-product link when an admin needs to switch operator surfaces

Not entered from public/discovery surfaces. robots: { index: false, follow: false } ensures it never appears in external SEO.

Layout regions#

The page uses Lilith design-system primitives directly (LGraphPaper, LOperatorBar, L, SERIF_L) rather than ShellLayout. Visually it adopts the "plate of record" cream-paper-with-rules look.

  • Top: LOperatorBar — product="OSHUN", section="Operator", breadcrumb="Index"
  • Eyebrow: "Plate of record" (accent color)
  • H1-like title: "Four operator surfaces." (Garamond 36 px)
  • Main grid (max-width 960, centered):
    • Auto-fit/minmax grid of operator surface entries, drawn from data.surfaces; desktop presents as a ledger grid and mobile collapses to one column without horizontal document overflow
    • Each surface is { href, code, title, desc }; the cell renders an LCode code badge, the title, an italic desc, and an "Open ↗" eyebrow (operator/page.tsx:53-95; operator-depth.ts:30-59)

States#

  • Loading — no route loading.tsx; getOperatorIndex() is a server-side static fixture and the page blocks until it renders
  • Anonymous user — redirected to /welcome?redirect= (proxy.ts); never sees this
  • Signed-in user — full index renders (session-gated only; no operator-role gate in the proxy)
  • getOperatorIndex() fails — no route error.tsx exists under apps/oshun/web/src/app/operator; a render crash falls through to the nearest parent/global boundary
  • Offline — operator work is write-heavy and audit-stamped; offline mode disables most actions with a clear banner
  • Different operator persona (e.g., safety operator vs. persona ops) — surfaces filtered to the role's permissions

Interactions#

LOperatorBar#

The shared operator bar at the top. Walk it as part of operator chrome:

  • Product labelOSHUN; non-interactive
  • Section labelOperator; non-interactive
  • BreadcrumbIndex; non-interactive on the index
  • No back/up affordance on the index — the bar exposes no link or button target

Surface grid#

For each cell in data.surfaces:

  • code badge (LCode) — short code label (STUDIO, TENANT, ADMIN, METIS)
  • Surface title — Garamond display text
  • Surface desc — italic concise line from the index fixture
  • Surface link (whole cell is the Link)
    • Function: navigates to the operator sub-surface
    • Keyboard: Enter activates
    • SR: announces surface name
    • Touch ≥ 44×44 px
  • Surface click telemetryoperator_index_surface_clicked, surface=
  • Hover state — pulses or underlines per Lilith design
  • Focus state — keyboard focus lands on the whole-cell link and keeps a visible focus affordance; no outline: none/shadowless regression

Page chrome#

  • Eyebrow — color is set from L.accent; Playwright verifies the stable eyebrow text/region
  • Title border — 1 px bottom border L.ink; spacing correct at all widths
  • Gridauto-fit/minmax(min(100%, 360px), 1fr); Playwright proves one-column stacking and no horizontal overflow at 390 px width

E2E coverage#

  • apps/oshun/web/e2e/operator-surfaces.spec.ts — signed-in /operator render, top operator bar non-interactivity, index surface count/title/rule/eyebrow anchors, all four shipped surface links/codes/hrefs/descriptions/open labels, 44 px link target floor, visible focus affordance, keyboard Enter activation into /operator/tenant, mobile one-column/no-overflow grid behavior, click navigation into /operator/admin, incident-queue smoke, tenant directory/console detail checks, and anonymous redirect-before-render for /operator.

Data & contracts#

  • Reads: getOperatorIndex() from @/lib/lilith-data/operator-depth — returns { surfaces: Array<{...}> }. NOTE: this is a static in-process fixture (returns getOperatorIndexFixture() directly), not a BFF call — the module is headed "fixture today, BFF tomorrow"
  • Writes: none on the index
  • Realtime: none on the index
  • Caching: server-rendered; cache headers from BFF; SW excludes operator surfaces from runtime cache (verify)
  • Auth/role check: session-gated only (proxy.ts redirects sessionless → /welcome?redirect=); no operator-role gate in the proxy, and surfaces[] is not role-filtered

Cross-references#

Open questions / known gaps#

  • Enumerate the shipped /operator/* routes and author walkthrough files for each — current route files cover admin, audit, incidents, lms, metis, personas, sso, studio, tenant, tenants, and admin/isis/provenance
  • Confirm the surfaces[] returned by getOperatorIndex() at each operator role and snapshot expected counts
  • Determine whether apps/oshun/admin and apps/oshun/admin-mobile are also "operator" — if so, they need their own WALKTHROUGH branches
  • Document audit log behavior: every operator click should leave a trail
  • An operator-role gate appears absent — proxy.ts only redirects sessionless users to /welcome; no role check in the proxy, operator layout.tsx (none exists), or the page components (possible gap vs intended scope)