---
status: reconciled-partial
coverage_depth: deep-for-shipped-independent-seams
last_walked: 2026-05-29
last_reconciled: 2026-07-20
specs:
  - apps/oshun/web/e2e/metis-lessons-session.spec.ts
  - apps/oshun/web/e2e/metis-lesson-completion.spec.ts
  - apps/oshun/web/e2e/metis-assessment-submit.spec.ts
  - apps/oshun/web/e2e/metis-tutor-session.spec.ts
source: WALKTHROUGH/journeys/metis-lesson-completion.md
---

# Journey result: An authored Metis lesson specimen, not learner completion

- **Walked**: 2026-05-29 at `9455bc53ad`. The retained result named an ephemeral
  `/tmp/claude-oshun-lt-2-walkthrough/walk.mjs` script but preserved no script,
  trace, request log, storage snapshot, or completion record.
- **Reconciled**: 2026-07-20 against the current schoolroom, lesson backlog and
  session pages, shared lesson reader, quiz and assessment storage, integrity
  and gradebook stores, tutor specimen and queue, separate tutor-memory route,
  production fixture guard, and all four named Playwright files. This was source
  review, not a fresh application run.
- **Verdict**: **partial / deep for the shipped independent seams** — 36
  Playwright cases combining browser and API evidence deeply cover the dev/test
  lesson backlog, schoolroom entry, authored reader and tutor anatomy, local
  quiz and assessment replay, a real durability-bound integrity response, Nisaba
  citation navigation, and a separately created durable tutor memory appearing
  in the schoolroom. They do not prove that a selected lesson is resolved,
  completed, persisted for an account, projected into Home or the schoolroom, or
  saved to a learner library.
- **Current authority**:
  [`WALKTHROUGH/journeys/metis-lesson-completion.md`](../journeys/metis-lesson-completion.md)
  now defines the same independent seams and partial outcome.

## Result at a glance

| Interaction                       | Proof · limit                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Home and schoolroom entry         | **Proof:** the lesson-completion suite opens Home's Metis continuation, retains its domain/provenance parameters, then verifies the signed-in `/metis` room's first-run lesson action, curated courses, honest no-assessment branch, and disclosed tutor card. **Limit:** Home opens `/domains/metis`, not this lesson chain; `/v1/metis/room` has no lesson-play cursor, always reports zero lesson/assessment progress, and combines learner-owned draft proposals with a curated catalog.                                                                      |
| Lessons index and today's session | **Proof:** four cases read the real dev/test BFF endpoint, render three rows grouped into two courses, select the first assessment-ready item, expose a two-row queue, verify 44 px links, and keep both pages free of horizontal overflow at 390 px standalone. **Limit:** `/v1/metis/lessons` is an unauthenticated, production-guarded fixture: it returns the same three rows in development/test and `503 not_configured` in production. Both pages silently turn a failed read into an empty state.                                                         |
| Course and lesson identity        | **Proof:** schoolroom, index, and session links retain `course`, `courseId`, or `id` in the destination URL. **Limit:** `MetisLessonPage` accepts no search parameters and `getMetisLesson()` returns `getMetisLessonFixture()` directly. Every id opens the same hard-coded PHL.102 section; the page title, passage, sources, and next/previous labels do not come from the selected row.                                                                                                                                                                       |
| Lesson quiz                       | **Proof:** Playwright chooses an answer, submits it, sees deterministic per-option grading, retries, and reloads a submitted score. **Limit:** the only record is `{ picked, phase }` under the single browser key `oshun.metis.lesson-quiz.v1`; it has no learner, tenant, course, lesson, attempt, timestamp, server acknowledgment, or completion event. Previous and next controls are inert buttons.                                                                                                                                                         |
| Assessment editor                 | **Proof:** four cases verify the 180–220 word and citation gates, post a clear or suspicious answer, render the returned Themis evidence, reopen a draft, replay the submitted view after reload, and follow an appealable verdict to its audit page. **Limit:** prompt and rubric are fixtures, the client hard-codes `courseId: phl-102`, and reload replays the BFF response from localStorage rather than reading a submission from the server. The browser test replaces the request with a test-authored Metis bearer before forwarding it to the real BFF. |
| Integrity and gradebook response  | **Proof:** `POST /v1/metis/assessment-submissions` authenticates Metis scope, deterministically detects signals, and appends an ordered operation whose Themis verdict and gradebook consequence publish only after the shared snapshot commits. Production hydration, restart tests, the `evidence_pack` export, subject erasure/fencing, compliance retention, and notification reconciliation are present. **Limit:** the editor still replays local JSON after reload and never performs server read-back.                                                    |
| Tutor specimen and composer       | **Proof:** 19 cases pin six fixture turns, three disclosed assistant turns, two Nisaba citation links, four context rows, the end note, composer input, modifier shortcut, local queued acknowledgement, and the absent voice control. **Limit:** the displayed session number, “11 turns,” transcript, grounding claim, and “saved to library” note are authored copy. Send appends to a browser-local queue and has no consumer, tutor POST, stream, generated reply, transcript mutation, end-session handler, or library write.                               |
| Separate tutor memory             | **Proof:** one browser case posts a real tutor-session-memory record and verifies that `/v1/metis/room` reflects its topic and session count. The route awaits candidate persistence; production requires and hydrates the snapshot, while export, erasure fencing, and scheduled retention cover memory plus mastery state. **Limit:** the fixture tutor page never calls that route, and the named browser case proves only same-process read-back rather than a deployed restart.                                                                              |
| Learner completion                | **Proof:** none in these four files. **Limit:** no action writes lesson completion, section position, quiz attempt, assessment completion, tutor transcript, library state, or Home continuation to one account-scoped authority. The schoolroom remains at zero lessons and assessments after the local/browser interactions.                                                                                                                                                                                                                                    |

## Evidence map

The map is deliberately vertical so the four authority planes remain readable on
a narrow Docs Center rail. Solid edges are exercised seams; dashed edges name
the joins that the current runtime does not make.

```mermaid
flowchart TB
    A[Schoolroom: real proposals plus curated zero-state] --> B[Dev/test lesson backlog fixture]
    B --> C[Links carry course and lesson ids]
    C --> D[Shared authored reader ignores every id]
    D --> E[Browser-local quiz and assessment snapshot]
    E --> F[Durable integrity ledger and projected gradebook]
    F -. no completion projection .-> G[No account progress, Home, or library update]
    G ~~~ H[Fixture tutor plus browser-local queue]
    H -. does not call .-> I[Separate tutor-memory route]
    I --> J[Durable tutor memory can feed schoolroom]
```

## Proven observations

### The historical pass is not reproducible evidence of completion

- The old record established that visible Metis routes rendered at the walked
  commit. Its walker lived under `/tmp`; there is no repository artifact that
  can reproduce its zero-error or zero-failure claims.
- The current four Playwright files contain **36 cases**: four lesson-index and
  session cases, nine schoolroom/reader/quiz cases, four assessment cases, and
  19 tutor cases. That is deep interaction evidence, but it is evidence for
  several independent surfaces rather than the lifecycle promised by the old
  result title.
- The former 2026-07-02 reconciliation called the lesson “query-param driven,”
  assessment reload “durable,” and the whole journey a pass. Current source
  contradicts each unqualified claim: ids are ignored by the reader, reload is
  localStorage replay, and no completion mutation exists.

### The schoolroom has an honest empty progress model

- `/metis` calls `getMetis()`, which reads `GET /v1/metis/room` and falls back
  to an explicit unavailable model rather than an embedded successful fixture.
  The route prepends caller-owned course proposals to three curated catalog
  entries when a member is resolved.
- `buildMetisRoom` intentionally returns a zero resume cursor, zero lessons,
  zero assessments, zero streak, and no open assessment. That is accurate: there
  is no learner lesson-play store to read.
- A separately created tutor memory is the one live learner signal the room
  consumes. It changes the tutor nudge and session count, not lesson progress.
  The tutor card itself remains display-only, so the nudge cannot open that
  memory or its resume URL.
- The Home continuation test proves a domain-shell handoff with provenance. It
  does not prove that local quiz or assessment activity updates Home, because
  neither component publishes a continuation record.

### The new index and session pages are production-guarded specimens

- `/metis/lessons` and `/metis/session` both server-read `/v1/metis/lessons`. In
  development/test, that endpoint returns the same three authored rows, spanning
  PHL.102 and AST.110 with exactly one `assessmentReady` flag.
- The index groups rows by course and links each course and lesson. The session
  page chooses the first assessment-ready row, then the first two remaining
  rows, yielding a deterministic 58-minute specimen. “Today's” is therefore a
  selection over fixture order, not recency, due date, mastery, or a learner
  cursor.
- `guardedFixtureRoute` blocks this endpoint in a production runtime with
  `503 not_configured`; it is not an account-scoped backlog. The endpoint also
  has no auth pre-handler in development/test. The signed-in shell tested by
  Playwright does not change the response's authority.
- Both pages replace a null/non-2xx BFF read with `lessons: []`. A production
  fixture refusal is rendered as “No active lessons,” which is visually honest
  about absence but does not distinguish an empty account from unavailable
  infrastructure.

### Id-bearing navigation terminates at one fixture reader

- The schoolroom emits `?course=`, the lessons index emits `?courseId=` and
  `?id=`, and today's session emits `?id=`. The current browser suites prove
  these exact hrefs and one clicked schoolroom destination.
- `MetisLessonPage` declares no `searchParams` input. It calls
  `getMetisLesson()`, which immediately returns `getMetisLessonFixture()`.
  Consequently the selected id never reaches a repository, adapter, or BFF.
- The rendered heading, section number, four passage paragraphs, five sources,
  four quiz rows, generated-content disclosure, and previous/next section labels
  are static component or fixture values. Even the AST lesson row opens the
  Stoic ethics text.
- Previous and next are `LBtn` controls without an href or handler. The tested
  presence of those buttons is not section navigation, completion, or progress.

### Quiz reload is one unscoped local record

- `MetisLessonQuiz` hides correctness before submit, permits multi-selection,
  computes exact row parity on the client, exposes per-option results, and
  supports a clean retry. The nine-case journey suite exercises the meaningful
  grading loop rather than only checking that controls exist.
- Every instance defaults to `oshun.metis.lesson-quiz.v1`. Only `picked` and
  `phase` are stored. There is no schema version beyond the key suffix, result
  timestamp, score record, server call, or storage error surfaced to the user.
- Because the lesson page ignores identity and the storage key is global, all
  course/id URLs in one browser profile share the same quiz. Another signed-in
  user reusing that profile can also inherit it.
- “Auto-saved · 14:08” beside the footer is fixed display copy. It is not
  derived from the localStorage write, and ignored quota/private-mode failures
  leave the claim visible.

### Assessment submission reaches a real durability-bound integrity engine

- The assessment prompt, due label, rubric percentages, provisional originality,
  tutor-turn count, and citation count are authored specimen data. The real
  client island counts words and citation-like syntax, stores the draft, and
  refuses submit outside 180–220 words or without one citation.
- On submit the editor posts `assessmentId`, a hard-coded `phl-102` course id,
  the answer, and a timestamp. The BFF requires `domain:metis`, constructs a
  submission/verdict id, derives deterministic signals, adjudicates through
  `MetisThemisCanonicalBridge`, and emits a gradebook value before returning
  `201`.
- The assessment browser suite intercepts this request and forwards it with a
  dev token authored inside the test. This joins the genuine UI payload to the
  genuine BFF route, but does not prove that the unmodified browser session
  supplies production Metis authorization end to end.
- The editor reduces the response to a local `SubmittedAssessmentResult` and
  writes it beside the answer under
  `oshun.metis.assessment.draft.v1.<assessmentId>`. Reload reads that object; it
  does not call the audit, submission, or gradebook read route.
- A suspicious answer proves the browser handoff in one running BFF: after the
  custom forwarder also authenticates integrity reads, the browser opens the
  canonical appeal page and reads the signal rationale and excerpt. Separate
  durability tests, not this browser case, prove the same authority can
  rehydrate.

### Integrity, appeals, gradebook consequences, and privacy share one ledger

- `AcademicIntegrityAppealStore` is now an ordered operation authority. Every
  adjudication, appeal open/resolution/decision, gradebook consequence, and
  notification intent is replayed into a candidate runtime, persisted under
  `metis-academic-records`, and published only after the sink acknowledges.
- Production calls `requireDurableMetisAcademicRecords`, hydrates the ledger
  before traffic, and reconciles its idempotent notification outbox afterward.
  The externally referenced gradebook singleton is a projection replaced from
  the persisted ledger, not an independent restart authority.
- Unit tests cover exact verdict/appeal/gradebook/outbox restoration and failed-
  write non-publication. A live PostgreSQL integration proves restart read-back,
  deletion, and compliance purge. Those tests strengthen the server boundary;
  they do not make the editor reload from it.
- The default `evidence_pack` collector exports the learner's verdicts, audits,
  appeals, gradebook history, and only that subject's authored actions. Exact
  subject deletion removes owned evidence, pseudonymizes roles in retained
  adjacent cases, fences recreation, and registers the two-year evidence-pack
  retention/purge path.

### The displayed tutor and durable tutor memory are different seams

- `/metis/tutor` calls `getMetisTutor()`, which returns six fixture messages.
  The component adds fixed session/turn/save labels, four fixed context rows, a
  disclosure, and a fixed end note. The named suite accurately pins the anatomy
  and both real Nisaba link navigations.
- Transcript and **End session** have no handler. The shared **Report** action
  is live and covered by the separate user-flag journey, but it reports static
  Metis target ids; it does not make the transcript a stored tutor session.
- `MetisTutorComposer` stores the draft under
  `oshun.metis.tutor.draft.v1.default` and the last 50 submitted texts under the
  adjacent `.queue` key. Send waits 200 ms, clears the draft, and says the tutor
  will pick it up. No code reads that queue for synchronization or response.
- The separate `POST /v1/metis/tutor-session-memories` route accepts a modeled
  understanding turn, updates mastery, and gives `/v1/metis/room` a real nudge.
  Its store serializes mutations, persists the memory and mastery candidate
  before publication, and maps an unavailable sink to a service error.
- Production requires and hydrates this snapshot before traffic. The
  `continuity_state` export includes the subject's memory, mastery states, and
  diffs; deletion removes that partition behind a hashed recreation fence; and
  startup plus a scheduled task purge expired sessions under the canonical
  continuity rule. The fixture browser tutor still never calls the route.

## Boundaries and gaps

- **A signed-in page is not an account-scoped backlog.** The lessons endpoint is
  a shared dev/test fixture without an auth pre-handler and fails closed in
  production.
- **A fixture row is not a learner assignment.** Its week, readiness, and
  duration are authored values with no enrollment or progress source.
- **A “today” selection is not scheduling.** It chooses fixture order and one
  boolean; it reads no date, due time, mastery, or learner cursor.
- **An id-bearing href is not identity resolution.** The shared reader ignores
  `course`, `courseId`, and `id`.
- **A shared reader is not a course.** Every selected backlog row produces the
  same PHL.102 section and quiz.
- **A scored quiz is not an attempt record.** Its one localStorage value lacks
  account, lesson, attempt, timestamp, and server authority.
- **Same-browser reload is not cross-device durability.** Quiz and assessment
  restoration replay browser-local JSON.
- **“Auto-saved” is not acknowledged persistence.** localStorage failures are
  swallowed while fixed save copy remains visible.
- **A fixture rubric is not an instructor grade.** The percentages are static,
  and the returned gradebook score is based on integrity class rather than the
  four displayed rubric dimensions.
- **A test-authored bearer is not production browser authorization.** The
  assessment suite rewrites the browser request before it reaches the BFF.
- **A durable Themis response is not assessment read-back.** The server
  authority survives restart, but the editor reloads its reduced localStorage
  copy and never verifies the submission, verdict, or latest gradebook
  projection.
- **A local submitted card is not server read-back.** Reload never asks the BFF
  whether the submission or verdict still exists.
- **A citation link is not dynamic grounding proof.** The two tutor citations
  and grounding disclosure come from the authored fixture.
- **A local queue is not tutor delivery.** It has no consumer, retry worker,
  receipt, generated turn, or transcript append.
- **“Saved to library” is not a library write.** It is component copy with no
  save action or read-back.
- **A separately posted tutor memory is not browser tutor integration.** The
  test creates it through the API outside the fixture composer.
- **A durable tutor-memory API is not browser tutor integration.** Its awaited
  commit, export, deletion fence, and retention path remain disconnected from
  the fixture transcript and local composer queue.
- **Composed academic privacy is not lesson privacy.** Integrity/gradebook and
  tutor-memory records now participate in export and deletion; the quiz,
  assessment draft, and submitted-card replay remain browser-local and unscoped.
- **Thirty-six deep cases are not lesson completion.** No named case asserts a
  completion write, restart-safe progress, cross-device read, or exact
  Home/schoolroom update for the lesson it opened.

## Re-run evidence

The 2026-07-20 work was source reconciliation and Docs Center verification; it
did not execute these application suites. A fresh journey run should use the
repository's real web/BFF harness and retain reports instead of another `/tmp`
walker:

```bash
pnpm --dir apps/oshun/web exec playwright test -c playwright.config.ts e2e/metis-lessons-session.spec.ts --project=chromium --workers=1
pnpm --dir apps/oshun/web exec playwright test -c playwright.config.ts e2e/metis-lesson-completion.spec.ts --project=chromium --workers=1
pnpm --dir apps/oshun/web exec playwright test -c playwright.config.ts e2e/metis-assessment-submit.spec.ts --project=chromium --workers=1
pnpm --dir apps/oshun/web exec playwright test -c playwright.config.ts e2e/metis-tutor-session.spec.ts --project=chromium --workers=1
```

Retain the commit and environment mode, web/BFF reports, authenticated subject
and tenant, selected course/lesson ids, pre/post browser storage, assessment
request/response, verdict and gradebook ids, snapshot backing tier, and a
process-restart read-back. A completion-grade follow-up must also retain an
account-scoped attempt/completion id, server commit receipt, exact lesson
read-back, and the resulting schoolroom, Home, library, and second-device
projections.

## Source trail

- [Current journey contract](../journeys/metis-lesson-completion.md)
- [Schoolroom page data read](../../apps/oshun/web/src/lib/lilith-data/metis.ts)
- [Schoolroom room model](../../apps/oshun/bff/src/metis/room.ts)
- [Schoolroom and depth components](../../apps/oshun/web/src/components/lilith/metis.tsx)
- [Lessons index](../../apps/oshun/web/src/app/metis/lessons/page.tsx)
- [Today's session page](../../apps/oshun/web/src/app/metis/session/page.tsx)
- [Production-guarded lessons fixture](../../apps/oshun/bff/src/routes/domain-stubs.ts)
- [Shared lesson route](../../apps/oshun/web/src/app/metis/lesson/page.tsx)
- [Authored depth fixtures](../../apps/oshun/web/src/lib/lilith-data/metis-depth.ts)
- [Lesson quiz state](../../apps/oshun/web/src/components/lilith/MetisLessonQuiz.tsx)
- [Assessment editor state](../../apps/oshun/web/src/components/lilith/MetisAssessmentEditor.tsx)
- [Integrity and gradebook routes](../../apps/oshun/bff/src/routes/metis-integrity.ts)
- [Durable academic operation ledger](../../apps/oshun/bff/src/metis/academic-integrity-appeal-store.ts)
- [Gradebook projection](../../apps/oshun/bff/src/metis/gradebook-store.ts)
- [Tutor composer queue](../../apps/oshun/web/src/components/lilith/MetisTutorComposer.tsx)
- [Separate tutor-memory route](../../apps/oshun/bff/src/routes/metis-tutor-memory.ts)
- [Tutor-memory snapshot store](../../apps/oshun/bff/src/metis/tutor-session-memory-store.ts)
- [Lessons/session browser evidence](../../apps/oshun/web/e2e/metis-lessons-session.spec.ts)
- [Schoolroom/lesson/quiz browser evidence](../../apps/oshun/web/e2e/metis-lesson-completion.spec.ts)
- [Assessment browser evidence](../../apps/oshun/web/e2e/metis-assessment-submit.spec.ts)
- [Tutor browser evidence](../../apps/oshun/web/e2e/metis-tutor-session.spec.ts)

## Cross-references

- [Metis BYOM result](./metis-byom-ingest-to-course.md) — independent ingest,
  decision, and proposal seams that do not feed this lesson reader.
- [Themis assessment appeal result](./themis-assessment-appeal.md) — the wider
  appeal lifecycle behind the assessment handoff.
- [Lessons view](../customer/08-metis/metis-lessons.md),
  [session view](../customer/08-metis/metis-session.md),
  [lesson view](../customer/08-metis/metis-lesson.md),
  [assessment view](../customer/08-metis/metis-assessment.md), and
  [tutor view](../customer/08-metis/metis-tutor.md) — per-route interaction
  records.
- [User flag result](./user-flag-to-review.md) — separate coverage for the
  lesson and tutor **Report** actions.
- [Results convention](./README.md) — status vocabulary and evidence rules.

## Open questions

- What authenticated course, enrollment, lesson, and assignment contracts will
  replace the production-guarded backlog fixture?
- Which route will resolve every course/lesson id and refuse mismatched or
  unauthorized identity rather than silently rendering one fixture?
- What attempt schema will scope quiz state by tenant, learner, course, lesson,
  assessment, version, and retry while retaining timestamps and scoring input?
- Which write defines “lesson complete,” and how is its commit projected into
  the schoolroom, Home continuation, library, notifications, and other devices?
- Which authenticated submission GET will let the editor replace browser replay
  with the durable verdict and latest gradebook projection after reload?
- What cross-instance concurrency protection governs the whole-snapshot academic
  and tutor-memory stores beyond their in-process mutation queues?
- Will the browser tutor create/resume the existing tutor-memory model, stream
  generated turns, append citations, and prove the same transcript after a
  restart?
- Which labels should be hidden or qualified until they are true: “today,”
  “auto-saved,” “saved to library,” “grounded in four sources,” and “the tutor
  will pick this up”?
