- Walked: 2026-05-29 at
9455bc53ad. The retained result reported a passing route walk through an ephemeral/tmpscript; it did not preserve the script, raw report, browser trace, request bodies, or persistence evidence. - Reconciled: 2026-07-20 against the alias and proxy behavior, BYOM and operator fixtures, live ingest controls, deterministic outline pipeline, ingest and decision snapshot stores, proposal persistence tiers, account deletion/export composition, and all four named Playwright specs. This was source review, not a fresh application run.
- Verdict: partial / deep for the shipped independent seams — current automation deeply covers the legacy alias, deterministic text ingest, honest URL/PDF pending states, owner-scoped job read-back, an admin-scoped decision record, and a learner-owned course proposal. It does not prove that any one source travels through safety, rights, Sophia grounding, approval, course build, publication, lesson, tutor, or assessment.
- Current authority:
WALKTHROUGH/journeys/metis-byom-ingest-to-course.mddefines the same three independent seams and partial outcome.
Result at a glance#
| Interaction | Proof · limit |
|---|---|
Legacy /metis/upload entry |
Proof: four browser cases cover a signed-in raw 308, repeated-query preservation, canonical browser landing, 390 px standalone launch, and anonymous 307 gating to Welcome before the alias page runs. Limit: this proves routing into /metis/byom; it says nothing about source acceptance or course creation. |
| BYOM page and guide preview | Proof: eight of the 21 BYOM-suite cases pin the heading, four material rows, rights/status cells, drop copy, live Browse/Paste controls, five-row preview, provenance card, and action presence. Limit: getMetisByom() returns getMetisByomFixture() directly. The roster, generated guide, citations, lesson count, and action buttons are authored specimens, not projections of the caller's ingest jobs. |
| Text and Markdown ingest | Proof: real browser and HTTP cases post actual text; the BFF requires at least 60 trimmed characters, derives a deterministic outline, records a completed owner-scoped job, and returns headings, summaries, reading time, key terms, and prompts derived from that text. Limit: the output is an outline record, not a MetisLearningSourceBundle, safety result, grounded source, or course build. |
| URL and PDF references | Proof: real HTTP and browser cases retain a locator or filename as awaiting_source_content, with outline: null and an explicit deploy-bound reason. Limit: the URL is not fetched and PDF bytes are not uploaded. Browse/drop turn Markdown or plain-text bytes into kind: text; EPUB and DOCX are not live ingest types. |
| Ingest persistence and ownership | Proof: the route awaits an ordered snapshot commit before returning 202; publication follows persistence, production requires the durable sink, owner-filtered reads return 403 on mismatch, and unit plus PostgreSQL integration evidence covers restart, failed-write rollback, export, erasure, and a hashed recreation fence. Limit: the four named application suites do not restart the deployed backing tier, and the snapshot abstraction does not join the job to a source bundle. |
| Operator decision | Proof: seven cases cover approve, request changes, reject, state mapping, newest-first filtered read-back, a 12-character summary gate, 401/403, UI reflection, and non-null audit ids in the test environment. The review row commits before 201; production requires its durable sink, and operator deletion pseudonymizes it for retention. Limit: the page source id is synthetic fixture text, audit mirroring is best-effort, and no approval gate or bundle mutation follows. |
| Learner course proposal | Proof: real browser and HTTP cases create an owner-scoped awaiting-syllabus-review draft and read it back on /metis; a short title returns 422. PostgreSQL mode also dual-writes an empty canonical awaiting-triage proposal. Limit: the proposal request has no ingest-job or decision id, named automation keeps one process, fallback file persistence is best-effort, and no reviewer transition or build-spawn route follows. |
| Published course and learner reads | Proof: none in these four specs. Limit: no runtime join composes the three records, invokes safety/rights/PII or Sophia/knowledge-graph work, applies ByomSourceApprovalGate, promotes the canonical proposal, creates a CourseBuild, or makes the ingested source available to lesson, tutor, assessment, or library consumers. |
Evidence map#
The vertical map preserves reading order at narrow widths. Solid edges are exercised by the named suites. Dashed edges are the joins the current runtime does not make; the stacked operator and proposal records are separate inputs, not later stages of the ingest job.
Proven observations#
The historical pass is a dated route observation#
- The old result proves that
/metis/ingestand/metis/courses/newrendered at the walked commit, that both endpoints accepted a happy-path request, and that/metis/uploadredirected. Its zero-error and zero-failure statements cannot be reproduced from the repository because the/tmpwalker and its artifacts were not retained. - Current evidence is stronger but changes the grade. The four named files contain 37 cases: four alias, 21 BYOM/operator/proposal browser, five real ingest/proposal HTTP, and seven decision HTTP/UI cases. Their breadth proves multiple shipped seams; it also exposes that those seams do not call one another.
- The current result therefore does not reuse the old generic
pass. A route existing is weaker than the journey title's former ingest-to-course promise.
The canonical entry lands on a fixture with a live ingest island#
/metis/uploaduses Next'spermanentRedirect, producing the tested raw308for an authenticated request. It preserves scalar and repeated query parameters. The application proxy gates an anonymous request to/welcomefirst, so the redirect component never renders for that request./metis/byomcallsgetMetisByom(), which returnsgetMetisByomFixture()without a BFF read. Four books/notes, page counts, rights chips, parse states, the five-section Stoic outline, “11 lessons · 6 weeks,” and the provenance paragraph are authored data.- The page is not wholly static.
MetisByomIngestControlsis a client island that posts to the real ingest route. Its Browse and drop paths read Markdown or plain-text bytes locally and send them as text; PDF sends only the file name; Paste URL sends only the locator. - The “Generate guide,” “Edit outline,” and “Begin week 1” cases deliberately
assert presence without clicking. Those controls have no handler or target in
MetisBYOM. Likewise, fixture copy saying every claim is cited and lessons were reviewed is not evidence that the current caller's source received either treatment. - The client enforces one 50 MB per-file check. The BFF does not repeat that byte limit, the controls take only the first dropped/selected file, and the displayed 200 MB aggregate limit has no live quota implementation.
Text ingest produces a real, deterministic outline record#
POST /v1/metis/ingestis origin-, CSRF-, and bearer-authentication guarded. Its shared auth pre-handler does not require a Metis-specific scope. Supported kinds are exactlytext,url, andpdf; text shorter than 60 trimmed characters, a non-HTTP(S) URL, an empty PDF filename, and an unknown kind are rejected before a job is created.deriveStudyOutlineoperates only on supplied text. It merges short paragraphs, caps output at eight segments, derives a heading and bounded summary per segment, estimates reading time at 200 words per minute, extracts repeated non-stopword terms, and constructs study prompts. It is heuristic and deterministic, not a model call.- The route returns
202withstate: completedand the derived outline. The owner can read the stored job throughGET /v1/metis/ingest/:jobId; an unknown id returns404, while an existing job requested by another owner returns403 job_owner_mismatch. - The activity route can project a completed ingest job into the member's activity feed. That read-back still points generally to Metis; it does not make the outline a lesson or establish a source-bundle lifecycle.
URL and PDF outcomes are honest references, not extraction#
- A URL job stores the URL string and says URL fetch plus extraction is
deploy-bound. A PDF job stores the filename and says parsing is deploy-bound.
Both return
awaiting_source_content,outline: null, and no invented segments. - The dedicated
/metis/ingestform's PDF picker also retains only a filename. The BYOM island accepts.pdf,.md, and.txt; it does not accept the EPUB and DOCX kinds shown in the fixture roster. - This is a sound non-fabrication boundary, not a queue. There is no worker,
callback, source-byte attachment route, or status transition that moves an
awaiting reference to
completedlater.
Ingest acknowledgment is now durability-bound and privacy-composed#
wireDurableMetisIngestJobshydrates themetis-ingest-jobssnapshot before traffic, whilerequireDurableMetisIngestJobsmakes an unwired deployable fail closed. The snapshot includes minimized jobs and SHA-256 deletion fences.- The deployable route uses
createDurably(): it clones the candidate map, validates per-subject and total capacity, awaits the snapshot sink, and only then publishes the job and returns202. Persistence failure returns503without exposing the candidate; a previously erased subject receives410. - Raw pasted text is not retained. Completed text jobs keep only the derived outline, while URL locators are minimized and PDFs keep a normalized filename. This is durable derived state, not a retained upload body.
- The
saved_claim_passageexport includes the caller'smetis_ingest_jobs. Deletion removes the exact owner's jobs, writes the fence in the same ordered snapshot, and blocks resurrection. Unit and live-PostgreSQL integration tests cover these storage properties; the four named browser/HTTP suites still do not recycle a deployed process.
The operator panel stores a verdict against synthetic fixture text#
/operator/metisgets its KPI strip, five generated-lesson rows, quality scores, rights/source counts, Themis flags, and tutor telemetry fromgetMetisOperatorFixture(). Only the OneRoster card can be adapted from a live BFF read.- Each
MetisByomDecisionPanelreceives a source id assembled as${course}::${lesson}from a fixture row. The decision route checks admin scope and validates a non-empty source id, one of three decision verbs, and a trimmed summary of at least 12 characters. It never resolves that string to an ingest job or learning-source bundle. - Approve maps to
approved, request changes tochanges-requested, and reject torejected. Each submission creates another row; list reads are newest-first and may filter by exact source id. “Amend decision” merely returns the local panel to its input state; the next submit appends a new row rather than changing the old one. - The authoritative review row commits first through
recordDurably(). The route then attempts to append and flush a searchable admin-audit mirror and durably link its id; mirror failure is swallowed, leaving a complete review row withauditEventId: null. The HTTP cases prove non-null ids only in their configured test environment. - Production requires the decision snapshot sink. Mutations are serialized,
persisted before publication, and mapped to
503on persistence failure. Account deletion pseudonymizes matching operator ids and free-text rationales, fences recreation, and retains the internal review package until the canonical compliance window permits purge. It is intentionally not customer export data. The named seven-case spec does not restart the BFF. - The three UI cases intercept the panel request and reissue it to the real BFF
with a test-authored
admin:metisbearer. They prove the genuine route and UI state mapping, but not that an ordinary browser session carries a real operator credential end to end. - The panel labels an approved row “cleared for course build” and changes requested as “returned to editor.” Those are display phrases only: no build or editor record is changed by the route.
The learner proposal persists on a different plane#
POST /v1/metis/coursesrequires a bearer-resolved member, origin, and CSRF context, but no Metis-specific scope; rejects titles shorter than four characters; normalizes domain text to uppercase; and accepts an in-range 1–26 week count or falls back to six. It creates an owner-scoped legacy proposal inawaiting-syllabus-reviewwith a fixed three-day review estimate./v1/metis/coursesand/v1/metis/roomlist only the caller's proposals. The browser case submits a draft, visits/metisin the same process, and finds that exact title at the head of the learner's room.- The proposal store chooses PostgreSQL when available, a per-user development JSON file outside tests, and memory in tests without a persistence path. File writes are best-effort and corrupt/unreadable files start empty. The named browser and HTTP cases do not prove a restart at any of those tiers.
- In PostgreSQL mode, creation sequentially inserts the legacy proposal and a
canonical
v1_metis_course_proposalpeer inawaiting-triage. The canonical row starts with empty seed sources, review chain, and spawned-build ids. The two inserts are not wrapped in a transaction, and current list reads still use the legacy row. - No named request contains an ingest job, decision, source bundle, or canonical proposal id as an upstream reference. The result card's promise that Metis returns within three days with an outline is not backed by a scheduler or reviewer route.
- The current account-deletion composition registers no course-proposal eraser and the default export bundle does not collect proposals. Operator decisions follow a different internal-governance lifecycle: exact operator deletion pseudonymizes identity and rationale, retains a digest and purge schedule, and excludes the review package from the customer bundle.
Boundaries and gaps#
- A query-preserving alias is not ingest. It proves navigation to the canonical page, not source validation or a stored job.
- A fixture material row is not an ingest job. The four materials and five
outline rows render for every caller and are not read from
metisIngestJobStore. - Fixture provenance copy is not provenance evidence. No runtime citation, verbatim-quotation, or source-only review record backs that preview.
- A 50 MB client check is not a server quota. The BFF does not enforce the byte limit, and the displayed 200 MB aggregate has no live counter.
- A PDF filename is not an uploaded PDF. No file bytes reach the route, so parsing cannot begin.
- A URL locator is not fetched content. Awaiting state has no worker or later content-attachment transition.
- A deterministic outline is not a grounded learning source. It is a heuristic projection over caller-supplied text with no safety, rights, PII, Sophia, embedding, or knowledge-graph receipt.
- Authentication is not Metis authorization. The ingest and proposal seams resolve a valid bearer but do not enforce a Metis-specific domain scope.
- Write-before-ack durability is not lifecycle linkage. Ingest jobs and review rows now commit before success, but neither record becomes a source bundle, approval-gate input, or course-build trigger.
- A same-process reload is not a restart proof. The named application specs never recycle the configured BFF/database tier.
- One composed privacy partition is not universal coverage. Ingest jobs are exported, erased, and fenced; operator reviews are pseudonymized and retained. Course proposals still have no registered export or account-deletion path.
- A synthetic source id is not source existence. The decision route accepts any non-empty string and does not resolve it to a job or bundle.
- A mapped
approvedstate is not approval-gate execution. Shared unit tests coverapplyByomSourceApprovalDecision; the BFF/UI never calls it. - An audit attempt is not an audit guarantee. Failure is explicitly
swallowed and the decision may persist with
auditEventId: null. - A test-authored admin bearer is not production operator authentication. The browser decision cases bridge customer page traffic to the real BFF with their own admin token.
- “Cleared for course build” is not a build trigger. The decision panel
changes its local display after a stored row; no
CourseBuildis created. - An awaiting-triage row is not a review workflow. No actor-bound triage, grounding, Veritas, rejection, or promotion transition is wired here.
- A dual-write is not atomic or canonical read authority. Legacy and canonical proposal inserts are sequential, and customer reads remain on the legacy table.
- A learner draft is not a course. It has no source ids, approved bundle, lessons, tutor state, assessment, or publication receipt.
- Action presence is not interaction coverage. Generate guide, Edit outline, and Begin week 1 are intentionally presence-only assertions.
- Deep seam coverage is not a complete journey. Thirty-seven cases can be green while the safety-to-publication spine remains entirely absent.
- Source reconciliation is not execution. None of the four cited application specs was rerun for this documentation audit.
Re-run evidence#
Run the four focused specs sequentially against one immutable commit and one controlled web/BFF environment:
pnpm --dir apps/oshun/web exec playwright test -c playwright.config.ts e2e/metis-upload-alias.spec.ts --project=chromium --workers=1
pnpm --dir apps/oshun/web exec playwright test -c playwright.config.ts e2e/metis-byom-ingest.spec.ts --project=chromium --workers=1
pnpm --dir apps/oshun/web exec playwright test -c playwright.config.ts e2e/metis-ingest-bff.spec.ts --project=chromium --workers=1
pnpm --dir apps/oshun/web exec playwright test -c playwright.config.ts e2e/metis-byom-decision.spec.ts --project=chromium --workers=1
Retain the commit and environment mode, web/BFF reports, source request bytes or references, authenticated learner/operator subjects, generated job and decision ids, audit ids, storage tier, pre/post snapshots, and process-restart read-back. A journey-complete follow-up must also retain the validated source bundle id, ordered safety/rights/PII receipts, Sophia and knowledge-graph anchors, approval-gate input/output, linked canonical proposal review entries, atomic promotion/build receipt, published course id, and lesson/tutor/assessment reads of that exact source.
Source trail#
- Current journey contract
- Legacy alias route
- BYOM fixture source
- BYOM page and operator surface
- Live BYOM ingest controls
- Dedicated ingest form
- Deterministic outline pipeline
- Ingest job store
- Ingest and proposal routes
- Proposal persistence adapter
- Decision panel
- Decision route
- Decision store
- Uninvoked BYOM approval contract
- Alias browser evidence
- BYOM/operator/proposal browser evidence
- Ingest/proposal HTTP evidence
- Decision HTTP/UI evidence
Cross-references#
- Metis proposal contract result — canonical initial write, review-chain, atomicity, and promotion boundaries.
- Metis lesson completion result — existing lesson, assessment, appeal, and tutor surfaces; they do not consume this journey's new ingest job.
- Editorial review and approval result — a separate approval plane, not the missing Metis BYOM gate.
- Operator Metis view and customer BYOM view — the two fixture-led surfaces.
- Results convention — status vocabulary and evidence rules.
Open questions#
- Which service turns an ingest job into a typed, tenant-scoped
MetisLearningSourceBundle, and how are source bytes retained or referenced? - What ordered operation records safety, rights, PII, Sophia ingestion, embeddings, knowledge-graph anchors, and retraction-aware evidence?
- How will an operator decision resolve an existing bundle, invoke
ByomSourceApprovalGate, reject stale/conflicting state, and retain an audit-guaranteed receipt? - What transaction or outbox links an approved bundle and reviewed proposal to a
CourseBuildwithout split legacy/canonical writes? - Which read proves the exact published source reaches a lesson, tutor turn, assessment, citation, and learner library record?
- What cross-instance concurrency contract prevents competing whole-snapshot ingest or decision writers from overwriting one another?
- How will course proposals participate in customer export, account deletion, erasure fencing, and retention alongside the already-composed ingest and internal-review partitions?
- Which BYOM preview claims and action labels should remain hidden until their cited, reviewed, and publishable states are backed by runtime evidence?