---
status: walked + e2e-covered
coverage_depth: deep
last_walked:
  '2026-07-17 by Codex — durable apply/commit hardening verified against an
  isolated exact-source BFF, fresh migrated PostgreSQL, restart hydration, and
  tenant-oneroster-provisioning-bff.spec.ts plus
  tenant-bulk-operations-export-bff.spec.ts.'
specs:
  - apps/oshun/web/e2e/tenant-oneroster-provisioning-bff.spec.ts
  - apps/oshun/web/e2e/tenant-bulk-operations-export-bff.spec.ts
  - apps/oshun/web/e2e/operator-lms.spec.ts
  - apps/oshun/web/e2e/tenant-oneroster-metis-card.spec.ts
source: WALKTHROUGH/journeys/tenant-oneroster-roster-sync.md
---

# Journey result: Tenant OneRoster roster sync

- **Walked**: 2026-06-25; updated 2026-06-28 by Codex against local real-BFF dev
  infrastructure; coverage reconciled 2026-07-02 by Codex against current
  real-dev-infra Playwright; durable authority/restart evidence refreshed
  2026-07-17 by Codex.
- **Verdict**: **pass / deep E2E covered for the shipped OneRoster sync spine**
  — the OneRoster API spine is covered with tenant-console scoped dry-run/apply
  behavior, conflict detection, commit read-back, periodic SIS pull
  reconciliation, duplicate rejection, malformed payload rejection, and
  fail-closed conflict behavior, including `tenant.oneroster.commit.applied`,
  `tenant.oneroster.pull.reconciled`, and
  `tenant.oneroster.pull.conflicts_detected` audit-log read-back plus
  duplicate/conflict no-phantom-audit guards. `/operator/lms` covers the
  dedicated operator diff UI through the real preview route, and
  `/operator/metis` reads the committed roster projection after a real apply.
  Apply, pull, and roster bulk commits now acknowledge only after the strict
  OneRoster snapshot is saved; exact-source restart recovery and persisted
  replay rejection were also verified.
- **Primary specs**:
  `apps/oshun/web/e2e/tenant-oneroster-provisioning-bff.spec.ts`,
  `apps/oshun/web/e2e/tenant-bulk-operations-export-bff.spec.ts`,
  `apps/oshun/web/e2e/operator-lms.spec.ts`,
  `apps/oshun/web/e2e/tenant-oneroster-metis-card.spec.ts`

## Observations

- `POST /v1/tenant/lms/oneroster/dry-run` rejects missing bearer auth and
  generic `admin:studio`, accepts `admin:workspace:tenant-console`, reports a
  clean import, reports username and missing-user conflicts, and rejects
  malformed records.
- `POST /v1/admin/lms/oneroster/apply` rejects missing bearer auth and generic
  `admin:studio`, commits a clean import, rejects duplicate commit ids, rejects
  conflict payloads without mutating the roster, and rejects malformed records.
  Failed persistence returns `503` without publishing roster/commit state or a
  success audit.
- `POST /v1/admin/lms/oneroster/pull` models the periodic SIS/LMS worker handoff
  in local dev: a clean pull updates an existing user, adds a new student and
  enrollment, records a derived `pull:{tenantId}:{pullId}` commit, and rejects
  duplicate pull ids.
- `POST /v1/admin/lms/oneroster/preview` powers the `/operator/lms` diff table:
  clean previews show user create, user update, and enrollment create rows;
  conflicting previews show blocked conflict rows; preview does not mutate the
  committed roster.
- `/operator/metis` reads `GET /v1/admin/lms/oneroster` after a real apply and
  renders the BFF-backed OneRoster card source, latest commit id, aggregate
  counts, and tenant row for the committed roster.
- A conflicting pull returns `422 has_conflicts`, audits the drift conflict, and
  leaves roster counts unchanged. Its durable audit payload contains only
  `conflictCount`, not raw username-bearing conflict strings.
- `GET /v1/admin/lms/oneroster` reads back the committed import and tenant
  roster summary through the live BFF route.
- `GET /v1/admin/audit-log/events?eventTypePrefix=tenant.oneroster` reads back
  the committed OneRoster audit event, pull reconciliation event, and pull
  conflict event, while duplicate commit/pull and apply-conflict paths do not
  mint extra success rows.
- Real PostgreSQL verification recovered exact sorted users, enrollments, and
  commit ids after restart. A live exact-source BFF restart hydrated two tenants
  and three commits and rejected a recovered duplicate with `409`.
- The roster bulk-operation executor awaits `applyDurably`; the existing
  real-HTTP bulk journey proved commit-to-projection read-back.

## Out of scope

- Tenant-admin commit controls around the now-covered operator dry-run diff
  table.
- Production scheduler ownership and stale/inactive student policy beyond the
  local-dev pull handoff.
- Distributed compare-and-swap across BFF replicas, tenant offboarding, and an
  exact customer-auth subject link for roster identities.
