Skip to Content

← All archived runs

Run: admin-discriminator

run.md

Run: admin-discriminator

  • branch: claude/hopeful-ride-v1plkf
  • pr: #503

01_define/output/spec.md

Spec: Fix admin user provisioning — register the missing admin discriminator

  • slug: admin-discriminator
  • personas: admin
  • touches: packages/services/src/db/models/user.ts
  • complexity: trivial

Problem

New users are not being signed up in production. Provisioning throws MongooseError: Discriminator "admin" not found for model "user" from userService.ensureProvisionedFromClerk. When a Clerk org role doesn't map to a known system role, resolveAppUserForTenant defaults the new user to role: "admin" (apps/web/lib/resolve-app-user-for-tenant.ts:63). The user model declares discriminatorKey: "role" and registers discriminators for five of the six enum roles (customer, expert, vendor, csm, sdm) — but not admin, even though admin is a valid value in the role enum. Because the base model has discriminators registered, Mongoose rejects any create() whose role value matches no registered discriminator, so every admin-role provisioning attempt fails. This blocks new account creation — a direct hit to onboarding and the platform's ability to take on new tenants/admins.

Proposed change

Register an admin discriminator on the user model so all six roles in the role enum have a corresponding discriminator, mirroring the existing pattern (admin carries no extra fields, so its schema is empty like sdm). This makes ensureProvisionedFromClerk resolve role: "admin" to a real model and create the user without error. The other five roles are unaffected.

Acceptance criteria

  • getUserModel() registers a discriminator for every value in the role enum, including admin (six total: admin, customer, expert, vendor, csm, sdm).
  • Provisioning a new user with role: "admin" via ensureProvisionedFromClerk succeeds and persists a user row with role: "admin" — no Discriminator "admin" not found error.
  • Provisioning continues to work for the other five roles (no regression).
  • pnpm typecheck and pnpm lint pass.

Out of scope

  • Changing the default-role fallback in resolveAppUserForTenant (?? "admin") — the fallback is intended; the bug is the missing discriminator, not the default.
  • Backfilling users from the outage window — provisioning create() failed atomically, so no partial rows exist; affected users provision normally on their next sign-in once this ships.
  • Adding admin-specific schema fields — admin has no extra fields today.

Open questions

  • none

02_build/output/notes.md

Build notes: admin-discriminator

  • commits: feat: admin-discriminator — register admin discriminator on user model

What changed

  • packages/services/src/db/models/user.ts: added the IAdminUser interface and an empty AdminSchema (admin carries no extra fields, mirroring sdm), and registered the admin discriminator in getUserModel(). All six values of the role enum now have a registered discriminator.

Why it fixes the bug

The base user model declares discriminatorKey: "role". Once any discriminators are registered on it, Mongoose rejects a create() whose role value matches no registered discriminator — throwing Discriminator "admin" not found for model "user". admin was the only enum role without a discriminator, and new sign-ins with no mapped Clerk org role default to role: "admin" (apps/web/lib/resolve-app-user-for-tenant.ts), so every such provisioning attempt failed. Registering the admin discriminator makes ensureProvisionedFromClerk's User.discriminators?.[input.role] resolve to a real model and the create succeed.

Acceptance criteria status

  • getUserModel() registers a discriminator for every value in the role enum, including admin — all six (admin, customer, expert, vendor, csm, sdm) are now registered.
  • Provisioning a new user with role: "admin" via ensureProvisionedFromClerk succeeds — the admin discriminator now exists, so sub.create(payload) no longer throws.
  • Provisioning continues to work for the other five roles — no existing discriminator or schema was changed; only admin was added.
  • pnpm typecheck and pnpm lint pass — verified by CI (see below).

Verify result

  • mechanical checks (format · lint · typecheck · build) run in CI + the Vercel preview, not here. Release reads them back from the PR's check runs. No check is expected to fail.

Notes for review

  • AdminSchema is intentionally empty (new Schema<IAdminUser>({})), identical to SDMSchema — admin has no role-specific fields today. If admin gains fields later, they go here.
  • No data migration is needed: existing admin rows already carry role: "admin"; this only affects the model registration used at write time.