V1 Web PWA · Journey

Journey: Metis BYOM ingest, operator decision, and draft proposal

A source-reconciled journey record for V1 Web PWA — shipped behavior, state boundaries, failure modes, and the automation evidence available today.

automated · 4 specs verdict: partial· 2026-05-29
9sections6 minread

On this page
Journey at a glance
ActorsSigned-in learner or teacherMetis operatorCourse proposerAuditor
Automation4 E2E spec(s) · verdict: partial· 2026-05-29

Journey flow#

Generated from the authored steps below — click a node to jump to that section.

flowchart TD s1["1. Orient on the BYOM specimen page"] s2["2. Submit real text"] s3["3. Submit a URL or PDF reference"] s4["4. Read the ingest job back"] s5["5. Record an operator decision"] s6["6. Submit a separate course proposal"] s7["7. Stop at the real boundary"] s1 --> s2 --> s3 --> s4 --> s5 --> s6 --> s7 click s1 href "#1-orient-on-the-byom-specimen-page" click s2 href "#2-submit-real-text" click s3 href "#3-submit-a-url-or-pdf-reference" click s4 href "#4-read-the-ingest-job-back" click s5 href "#5-record-an-operator-decision" click s6 href "#6-submit-a-separate-course-proposal" click s7 href "#7-stop-at-the-real-boundary" s7 --> v(["verdict: partial"]) click v href "../results/metis-byom-ingest-to-course.html" style v stroke:#d6a531,color:#d6a531

Metis currently ships three useful but independent seams: deterministic text ingest, an operator decision record attached to a source id, and a learner-owned course proposal. This journey walks all three without claiming the missing linkage—there is no runtime path from an ingest job through Lilith/Sophia safety and grounding into an approved bundle, published CourseBuild, lesson, tutor, or assessment.

Personas#

  • Signed-in learner or teacher — submits text, a URL, or a file reference and inspects the resulting ingest state.
  • Metis operator — records approve, request-changes, or reject against a source id in the fixture review queue.
  • Course proposer — submits a learner-owned draft that appears in that same learner's schoolroom.
  • Auditor — needs to distinguish stored decisions from a verified BYOM approval gate and published-course transition.

Pre-conditions#

  • The customer has a signed-in session and a verified bearer. The ingest and proposal routes do not currently enforce a Metis-specific domain scope.
  • /metis/byom can render its authored fixture of four materials and a five-row outline. Those rows are specimen data, not the caller's ingest-job list.
  • POST /v1/metis/ingest and GET /v1/metis/ingest/:jobId are available. The deployable requires the metis-ingest-jobs snapshot sink, and the write path acknowledges only after its ordered candidate snapshot commits.
  • The operator has admin:*, admin:metis, or admin:operator for the BYOM decision route.
  • Course proposals are a separate authenticated resource under /v1/metis/courses.

Steps#

1. Orient on the BYOM specimen page#

  • Open /metis/byom.
  • The heading, materials table, rights labels, parse-state pills, drop zone, and generated outline come from getMetisByomFixture().
  • Treat the displayed 50 MB/file and 200 MB/total limits as interface copy. The current ingest request accepts text or source references and does not enforce that complete multi-file quota model.

2. Submit real text#

  • Use /metis/ingest or the Browse/drop controls on /metis/byom with a Markdown or text file.
  • The browser posts actual text to POST /v1/metis/ingest with authenticated origin and CSRF headers.
  • deriveStudyOutline deterministically segments the supplied text, derives headings and summaries, estimates reading time, extracts repeated key terms, and builds study prompts.
  • The BFF stores an owner-scoped completed job and returns its real outline.

3. Submit a URL or PDF reference#

  • Paste URL posts a source locator without fetching it.
  • A PDF selection supplies a filename reference but not PDF bytes.
  • Both result in awaiting_source_content with no fabricated outline. EPUB and DOCX parsing are not wired through these controls.

4. Read the ingest job back#

  • GET /v1/metis/ingest/:jobId returns the job only to its owner.
  • An unknown job returns 404; a different owner receives 403 and cannot read the record.
  • Source-level tests prove restart restoration, failed-write rollback, export, account erasure, and a hashed recreation fence. A deployable without its sink fails closed; a persistence failure returns 503 without publishing the job.

5. Record an operator decision#

  • Open /operator/metis. The KPI strip and generated-lessons queue are fixtures; the OneRoster card may be adapted from a live BFF read.
  • MetisByomDecisionPanel posts { sourceId, decision, summary } to POST /v1/admin/metis/byom/decision.
  • A summary must contain at least 12 characters. The BFF maps the decision to approved, changes-requested, or rejected, then commits the authoritative review row before returning success.
  • A searchable admin-audit mirror is attempted only after that commit. Audit failure is explicitly best-effort and can leave auditEventId: null without undoing the complete review record.
  • The route does not resolve the source id to an ingest job, run ByomSourceApprovalGate, or mutate a MetisLearningSourceBundle.

6. Submit a separate course proposal#

  • Open /metis/courses/new and submit title, domain path, weeks, and optional rationale.
  • POST /v1/metis/courses returns 202 state=awaiting-syllabus-review and an owner-scoped course id.
  • /metis reads the caller's stored proposals and renders the new row as a draft at the head of the schoolroom course list.
  • The proposal request contains no ingest-job or operator-decision reference; its appearance does not mean a BYOM bundle was approved or published.

7. Stop at the real boundary#

  • No route composes a MetisLearningSourceBundle from the ingest job.
  • No runtime call invokes Lilith safety classification, rights review, Sophia ingestion, embedding, or knowledge-graph anchoring for this request.
  • No operator decision triggers a CourseBuild.
  • The shared lesson, assessment, and tutor pages therefore cannot read this newly ingested material as a published course.

Post-conditions#

  • A text request can produce an owner-scoped, reproducible ingest job and outline.
  • URL and PDF references remain honestly pending until source content exists.
  • An operator can persist a decision record for a supplied source id.
  • An owner can export and erase ingest jobs; deletion durably fences recreation.
  • Operator-account deletion pseudonymizes internal review identity and rationale for compliance retention rather than exporting that review package.
  • A learner can persist a separate course proposal in awaiting-syllabus-review and see it on /metis.
  • None of those records is linked into a safety-reviewed, grounded, published BYOM course.

Failure modes#

  • Anonymous ingest or proposal — returns 401.
  • Empty, unsupported, or malformed ingest — returns a validation error; no outline is invented.
  • URL/PDF without bytes — remains awaiting_source_content.
  • Cross-user job read — returns 403 job_owner_mismatch without returning the record.
  • Ingest snapshot unavailable — returns 503; the candidate is not published.
  • Ingest recreated after account erasure — returns 410 behind the durable subject fence.
  • Short proposal title — returns 422 course_title_too_short.
  • Decision without admin scope — returns 401/403.
  • Short decision rationale — returns 400 invalid_payload.
  • Unknown decision source id — the current route still accepts it; source existence is not validated.
  • “Approved” interpreted as publishable — incorrect: the decision record is not applied to a bundle gate.
  • Fixture material or queue row interpreted as stored user data — incorrect: both page rosters are authored specimens.

E2E coverage#

Per-view files touched#

Cross-references#

  • apps/oshun/bff/src/metis/ingest-pipeline.ts — deterministic outline derivation.
  • apps/oshun/bff/src/metis/ingest-job-store.ts — owner-scoped durable job boundary.
  • apps/oshun/bff/src/routes/admin-metis-byom-decision.ts and metis-byom-decision-store.ts — independent operator record.
  • apps/oshun/bff/src/routes/domain-stubs.ts — independent course proposal.
  • libs/metis/integrations/src/byom/index.ts — richer uninvoked bundle and approval-gate contract.
  • metis-lesson-completion.md — current fixture lesson and assessment flow.

Open questions#

  • Link an ingest job to a typed MetisLearningSourceBundle and expose its real lifecycle rather than a fixture material row.
  • Wire ordered safety, rights, PII, Sophia ingestion, and knowledge-graph evidence with tenant-scoped audit receipts.
  • Validate operator decisions against a real source and invoke ByomSourceApprovalGate before recording approved.
  • Define and implement the approved-bundle → CourseBuild publication trigger and downstream lesson/tutor/assessment reads.
  • Enforce actual upload size, aggregate quota, MIME parsing, and license declaration rather than presenting those limits as copy only.
  • Add course-proposal export, account erasure, retention, and recreation fencing; ingest jobs and internal review packages now have their distinct lifecycle paths.