Skip to content

feat(ui): migrate OrganizationProfile security panel to Mosaic#9125

Draft
alexcarpenter wants to merge 7 commits into
mainfrom
mosaic-migration-org-security
Draft

feat(ui): migrate OrganizationProfile security panel to Mosaic#9125
alexcarpenter wants to merge 7 commits into
mainfrom
mosaic-migration-org-security

Conversation

@alexcarpenter

@alexcarpenter alexcarpenter commented Jul 10, 2026

Copy link
Copy Markdown
Member

Summary

Migrates the OrganizationProfile Security panel (Enterprise SSO) from the legacy styled-system components (components/OrganizationProfile/SecuritySsoSection + components/ConfigureSSO/*) into the Mosaic machine → controller → view architecture. Legacy code is left untouched; the Mosaic panel is additive and self-gating.

The hard constraint for this migration is exact 1:1 behavioral parity with the legacy implementation.

Status: in progress (draft)

Building incrementally. This PR will grow with follow-up commits.

  • Phase 1 — Overview: status badge, activate / deactivate / remove (type-to-confirm), domain chips, tab gating shared with the shell (single source of truth so the tab never flash-hides).
  • Phase 2 — Wizard engine: 4-step machine (verify-domain → configure → test → activate) with furthest-reachable initial seat, recheck() re-seat, and deferred-advance (pendingNext). Proven 1:1 against the legacy reducer by an equivalence test.
  • Phase 3a — Verify-domain step: three concurrent mutation machines (add / prepare / remove) matching the domains-section pattern, plus the eventFlowStepMounted mount telemetry.
  • Phase 3b — Configure step: the legacy three-level nested wizard (outer / middle configure / inner per-provider SAML) collapsed into one flat configure machine. Middle+inner merged; terminal-save bubble to the outer wizard becomes a controller-forwarded NEXT, absorbed by the outer wizard's pendingNext. Preserves the create/change revalidation-race deferral, the Google mid-flow vs Okta/Custom/Microsoft terminal submit asymmetry, forward-entry select-provider force, and the connection-deleted re-seat.
  • Phase 3c — Test step: the two async actions (create-run, continue-validate) as a machine; the results list / pagination / polling stay hook-owned and thread through the controller. Preserves the armPolling distinction — creating a run arms polling (refresh({ armPolling: true })), the manual "Refresh logs" refetch does not. Continue gates on the freshly-revalidated success probe, and the successful advance bubbles to the outer wizard (bubblingNext → controller-forwarded NEXT).
  • Phase 3d — Activate step: the terminal setConnectionActive(id, true) machine with the activate telemetry fired on success (with connectionStatus: 'active', matching legacy), the active-vs-not-active copy derived from the live entity, and Skip / Done exits that return to the overview.
  • Phase 4 — parity audit: the configure equivalence test against the legacy nested-wizard seam landed, then an independent adversarial re-audit re-read all ~142 legacy flow tests (four agents, each defaulting every case to a gap unless it could cite a specific new test). It confirmed full non-view parity and surfaced four genuinely-unproven behaviors, now closed: the test-step armPolling distinction, the configure→test / test→activate / activate→overview bubble seams, the remove-domain side-effect order (updateConnection → domain.delete() → revalidate), and changeProvider failure error isolation. Everything else was view-only (approved deviation), architecturally superseded, or a cross-file false positive.
  • Overview parity gap: enrollment-role tooltip label (deferred to the view overhaul)

All four wizard steps now render their own Mosaic views; configure / test / activate own their in-step navigation, verify-domain uses the shell nav.

Testing

  • pnpm --filter @clerk/ui test security173 tests passing across machine / controller / view / equivalence layers.
  • Each layer tested per the Mosaic conventions (machine via createActor, view via fake snapshot + send, controller via mocked @clerk/shared/react).

Documented micro-differences (cosmetic; functionality identical, flagged for the parity audit)

  • Add and prepare errors use separate machine slots rendered through one shared alert.
  • The two "Add" buttons (field + suggestion) share the creating spinner.
  • Concurrent double-prepare of two expired cards serializes.
  • Status badge tones collapse onto the Mosaic palette (no success/warning tokens); labels match legacy exactly.
  • The legacy three-dot menu is flattened to inline buttons (approved: views get a later visual pass).
  • Configure step (approved deviation, view-fidelity only, not flow): the rich per-provider instructional content and the manual / file-upload SAML input modes are deferred to the view overhaul; the metadata-URL submit path is wired end to end. The full configure equivalence test against the legacy nested seam has landed (see Phase 4).
  • Test step (approved deviation, view-fidelity only, not flow): the rich results table, per-run drawer, timestamp formatting, and copy affordances are deferred to the view overhaul; the functional slice (open test URL, list runs + status, one-shot refresh, paginate, gated Continue) is 1:1.
  • Activate step: the decorative shield icon is deferred to the view overhaul; the flow (activate / skip / done, telemetry on activate success) is 1:1.

Changeset

Empty — these Mosaic components are not yet publicly exported, matching the precedent set by the other mosaic-migration changesets (no user-facing change).

…izard engine + domains step)

Migrates the Enterprise SSO Security panel from the legacy styled-system
components into the Mosaic machine -> controller -> view architecture.

- Overview: status badge, activate/deactivate/remove (type-to-confirm),
  domain chips, tab gating shared with the shell.
- Wizard engine: 4-step machine (verify-domain -> configure -> test ->
  activate) with furthest-reachable seat, recheck re-seat, and
  deferred-advance; proven 1:1 against the legacy reducer by equivalence test.
- Verify-domain step: three concurrent mutation machines (add / prepare /
  remove) matching the domains-section pattern, plus mount telemetry.

configure / test / activate render placeholders pending follow-up commits.
@changeset-bot

changeset-bot Bot commented Jul 10, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f048f08

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 0 packages

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
swingset Error Error Jul 10, 2026 9:24pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
clerk-js-sandbox Skipped Skipped Jul 10, 2026 9:24pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 1a2ec2a7-3450-404c-83a2-0a56e111526a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@pkg-pr-new

pkg-pr-new Bot commented Jul 10, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9125

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9125

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9125

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9125

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9125

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9125

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9125

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9125

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9125

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9125

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9125

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9125

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9125

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9125

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9125

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9125

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9125

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9125

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9125

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9125

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9125

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9125

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9125

commit: 2ab4674

…ovider + SAML sub-flow)

Collapses the legacy three-level nested ConfigureSSO wizard (outer / middle configure / inner per-provider SAML) into one flat configure machine. The middle+inner levels become a single machine; the terminal-save bubble to the outer wizard becomes a controller-forwarded NEXT, absorbed by the outer wizard's existing pendingNext deferral.

- Preserves the create/change revalidation-race deferral (pendingEnter), the provider asymmetry (Google submits mid-flow; Okta/Custom/Microsoft submit at the terminal step), the forward-entry select-provider force, inner-first PREV back to select-provider, and the connection-deleted recheck re-seat.
- 23 new tests (machine + view). Full security suite 144 green.

Deferred (approved, view-fidelity only, not flow): rich per-provider instructional content and the manual/file-upload SAML input modes; the metadata-URL path is wired end to end.
Close the gap between the legacy ConfigureSSO test suite and the new Mosaic
machines by porting the missing FLOW behaviors and adding the nested-seam
equivalence oracle. Scope is behavior outside the view components (views are a
deliberate, approved deviation).

Wizard engine (organization-profile-security-wizard.machine.test.ts):
- PREV abandons a parked advance (legacy goPrev clears pendingNextFrom)
- a parked advance survives a still-blocked recheck, resolves on a later one
- recheck does not yank the user forward when the current step still holds
- recheck is a provable one-shot (no second re-seat / re-notify)
- isSecurityWizardStepComplete: position-independent per-step completion

Configure step (organization-profile-security-wizard-configure.machine.test.ts):
- change-provider advance defers like create across the non-atomic swap
- per-provider save-terminal for all 4 providers + CONFIGURE_PROVIDER_STEPS
  pinned 1:1 to the legacy Saml*ConfigureSteps arrays

Configure nested-seam equivalence oracle (new .equivalence.test.tsx):
- drives the legacy inner useWizardMachine seam and the new collapsed machine
  through the same walk for every provider; asserts identical terminal-bubble
  and first-inner back-out boundary behavior.

Full security suite: 195 passing.
Independent adversarial re-audit of all ~142 legacy flow tests surfaced four
genuinely-unproven behaviors (the rest were view-only, superseded, or cross-file
false positives). Close them:

- test-step polling arm distinction: create-run arms polling
  (setPage(1) + refresh({armPolling:true}) + open URL), manual Refresh logs stays bare
- configure terminal-save bubble -> outer wizard defers -> lands on test on revalidate
- test-continue bubble -> outer wizard defers -> lands on activate on revalidate
- activate onDone -> activated -> returns to overview
- remove-domain side-effect order: updateConnection -> domain.delete() -> revalidate
- changeProvider failure error isolation (symmetric partner of the create-failure path)
…panel

The Security panel was wired into the shell but had no swingset story, so it
never appeared in the component explorer. Add one (Panels archetype): it
instantiates the panel's overview/wizard/step machines with delay-mocked async
deps and renders OrganizationProfileSecurityPanelView, mirroring the controller's
bubble/exit forwarding so the wizard is navigable. Also surface it in the
OrganizationProfile AIO story's security slot.

The security view reuses ConfigureSSO from @clerk/ui source via its internal `@/`
alias, so swingset resolves the `@/components/ConfigureSSO` subtree to the ui
source (webpack + tsconfig), distinct from swingset's own `@/components/ui`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant