Journey at a glance
ActorsSigned-in learner or teacherMetis operatorCourse proposerAuditor
Systems exercised
metis-byom.mdmetis-upload.mdmetis.mdoperator-metis.mdAutomation4 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/byomcan 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/ingestandGET /v1/metis/ingest/:jobIdare available. The deployable requires themetis-ingest-jobssnapshot sink, and the write path acknowledges only after its ordered candidate snapshot commits.- The operator has
admin:*,admin:metis, oradmin:operatorfor 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/ingestor the Browse/drop controls on/metis/byomwith a Markdown or text file. - The browser posts actual text to
POST /v1/metis/ingestwith authenticated origin and CSRF headers. deriveStudyOutlinedeterministically 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
completedjob 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_contentwith no fabricated outline. EPUB and DOCX parsing are not wired through these controls.
4. Read the ingest job back#
GET /v1/metis/ingest/:jobIdreturns the job only to its owner.- An unknown job returns
404; a different owner receives403and 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
503without 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. MetisByomDecisionPanelposts{ sourceId, decision, summary }toPOST /v1/admin/metis/byom/decision.- A summary must contain at least 12 characters. The BFF maps the decision to
approved,changes-requested, orrejected, 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: nullwithout undoing the complete review record. - The route does not resolve the source id to an ingest job, run
ByomSourceApprovalGate, or mutate aMetisLearningSourceBundle.
6. Submit a separate course proposal#
- Open
/metis/courses/newand submit title, domain path, weeks, and optional rationale. POST /v1/metis/coursesreturns202 state=awaiting-syllabus-reviewand an owner-scoped course id./metisreads 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
MetisLearningSourceBundlefrom 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-reviewand 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_mismatchwithout returning the record. - Ingest snapshot unavailable — returns
503; the candidate is not published. - Ingest recreated after account erasure — returns
410behind 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#
apps/oshun/web/e2e/metis-upload-alias.spec.tscovers/metis/uploadquery-preserving redirect, signed-in browser/PWA landing, and anonymous gating.apps/oshun/web/e2e/metis-byom-ingest.spec.tscovers the BYOM specimen, live text/URL/Browse/drop requests, rendered ingest outcomes, operator fixture, and independent course-proposal read-back.apps/oshun/web/e2e/metis-ingest-bff.spec.tscovers real HTTP owner-scoped ingest jobs, completed versus awaiting-source states, course proposals, and validation boundaries.apps/oshun/web/e2e/metis-byom-decision.spec.tscovers all three decision verbs, summary and scope gates, UI read-back, and audit-event ids when the audit sink succeeds.libs/metis/integrations/src/byom/unit tests cover deep approval-gate rules; they do not prove the BFF/UI invokes those rules.
Per-view files touched#
customer/08-metis/metis-byom.md— specimen roster and live ingest controls.customer/08-metis/metis-upload.md— legacy alias.operator/operator-metis.md— fixture queue and durable decision panel.customer/08-metis/metis.md— independent draft proposal read-back.- The lesson, tutor, and assessment views are future consumers, not current post-conditions of this journey.
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.tsandmetis-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
MetisLearningSourceBundleand 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
ByomSourceApprovalGatebefore recordingapproved. - Define and implement the approved-bundle →
CourseBuildpublication 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.