V1 Web PWA · Surface walkthrough

Metis · lesson (course reader)

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

walked
8sections4 minread

On this page

Context. surface customer · domain metis · route /metis/lesson · auth signed-in · source apps/oshun/web/src/app/metis/lesson/page.tsx

Last walked. 2026-05-29 automated runtime walk (Playwright headless) — render, /v1 data (2xx), console/page-errors, expected content, screenshot verified; live screen-reader, touch, offline, and telemetry-delivery checks pending a manual AT pass. Evidence: WALKTHROUGH/results/runtime-sweep-2026-05-29.md; content re-verified 2026-06-03 against current source; Trust & Safety report affordance target metadata + dialog verified on 2026-06-26; interactive quiz, durable grade result, footer nav, and course-card query entry re-verified on 2026-06-27 by metis-lesson-completion.spec.ts

Purpose#

Course reader for one lesson — passages, sources rail, and a check-your-understanding quiz. Fixture: PHL.102, Stoic ethics, § 4 of 11 — "What is in our power, and what is not." (14 minutes; resumes at line 22).

Entry points#

  • Metis schoolroom — "Continue lesson →" primary button on metis.md
  • Direct URL / bookmark — yes
  • BYOM — verify whether "Generate guide" on metis-byom.md navigates here with a generated lesson

Layout regions#

page.tsx is a thin server component: await getMetisLesson()<MetisLesson data={data} />.

Inside MetisLesson (metis.tsx, line 247):

  • Web shell topLCustomerNav active="library"
  • Header row (gridTemplateColumns: '1fr auto'):
    • Left — eyebrow "PHL · 102 · Stoic ethics · § 4 of 11", LDisplay "What is in our power, and what is not."
    • RightLCode "14 minutes · resumes at line 22" and the shared TrustSafetyFlagButton targeting metis://lesson/phl-102/stoic-ethics/section-4
  • Two-column body (gridTemplateColumns: '1.6fr 320px'):
    • Left — Reading column — long-form serif body with a 86 px accent drop-cap (E), three paragraphs and one blockquote that highlights "In our power are opinion, movement towards a thing, desire, aversion" (line 319); citation row "— Discourses · I.1 · trans. Long"
    • Left — Check your understandingMetisLessonQuiz client component in an LCard with eyebrow "Check your understanding", LCode "1 of 3"; quiz prompt "Which of these would Epictetus place 'in our power'?"; option rows from data.quiz; choosing toggles aria-pressed, Submit transitions data-metis-quiz-phase from choosing to submitted, shows "N of 4 correct", reveals kept/missed/incorrect pills, persists to oshun.metis.lesson-quiz.v1, and Try again resets.
    • Left — Footer nav← § 3 · The dichotomy ghost button, autosave code, and § 5 · Three disciplines → primary button. Both nav buttons are display-only in V1; no href or handler is wired.
    • Right column — Sources rail — static source rows plus the generated content disclosure card.

States#

  • LoadedgetMetisLesson() resolves before render; covered by metis-lesson-completion.spec.ts.
  • Quiz pristine — all options initially unselected and the quiz phase is choosing; no answer key is pre-revealed.
  • Quiz partial selection — option clicks toggle aria-pressed, visual selection, and localStorage-backed picked state.
  • Quiz submitted — Submit reveals score and per-option correctness; covered by choose → submit → Try again and reload-durability tests.
  • Reduced motion — composes static Lilith primitives; the page has no route animation to disable.
  • Resume positionLCode says "resumes at line 22"; no query-driven line scroll is implemented, so ?line= remains documentary only.

Interactions#

  • LDisplay — non-interactive.
  • Report (TrustSafetyFlagButton) — opens the shared Trust & Safety category/rationale dialog; the focused user-flag-to-review browser spec verifies this button's stable Metis lesson target kind/id/label metadata and dialog controls. The shared live submit → admin moderation read-back path is covered through the Nisaba flag in the same spec.

Reading column#

  • Drop-cap E — non-interactive decorative letter.
  • Highlighted span — visual emphasis only; no lexicon link is wired.
  • Blockquote citation — visual citation only; no Discourses I.1 link is wired on this reader surface.

Check your understanding#

  • Option rows (per data.quiz)
    • Function: multi-select; user clicks change selection state while choosing. data.quiz[].ans is used only for grading after submit, not for pre-marking.
  • Footer eyebrow "Pick all that apply · multi-select" — non-interactive guidance.
  • Submit / Try again — Submit grades the answer; Try again resets the quiz to choosing.
  • ← § 3 · The dichotomy (LBtn kind="ghost") — visible display-only button in V1; no navigation handler is wired.
  • → § 5 · Three disciplines — visible display-only button in V1; no navigation handler is wired.

Right column (Sources rail)#

  • Source citation row — static source metadata; Nisaba opening is not wired from this rail.
  • Lilith excerpt — static generated-content disclosure; no copy/save affordance exists in V1.

Data & contracts#

  • Reads: getMetisLesson() from @/lib/lilith-data/metis-depth — returns MetisLessonData (verify shape: lesson metadata, prose blocks, blockquote, quiz { k, t, ans }[], sources rail entries)
  • Writes: TrustSafetyFlagButton submits customer reports through /api/trust-safety/flagsPOST /v1/user-reports; verify quiz submission + progress-save endpoints
  • Realtime: None.
  • Caching: server fetch
  • Auth/role check: shell middleware

Cross-references#

Open questions / known gaps#

  • Quiz interactivity is confirmed: MetisLessonQuiz is a client state machine with choosing/submitted phases, exact score, per-option correctness, Try again, and localStorage durability.
  • Lesson resume line contract is currently display-only: the header says "resumes at line 22"; the page does not consume ?line=.
  • Footer section buttons are display-only in V1; no route transition, progress write, or section id contract is implemented here.
  • Sources rail save-to-library / save-to-Nisaba is not implemented on the lesson reader; source follow-through is covered on the tutor citation chips instead.