WorkOS membership reconciliation
ENG-2809 transfers the nightly
WorkOS membership repair from Rails’ SyncWorkOSMembershipsJob to API2’s
maintenance BullMQ queue. It changes recurring ownership, not the user-facing
authentication or invitation contract.
Contract
- At 03:00 UTC, list every Rails organization with a WorkOS organization ID.
- For each organization, request active, inactive and pending memberships in descending 100-row pages. Stop after the same 1,000-page safety ceiling.
- Find only kept local memberships by the durable WorkOS membership ID. Ignore missing local rows and reject a row whose local organization differs from the organization being scanned.
- Update only a valid WorkOS status or a changed primary role. Prefer the primary role slug and retain the legacy roles-array fallback.
- Resolve existing organization or shared environment roles by WorkOS slug or local key. If an organization role is absent, import its scope and mapping IDs and add only permissions present in Rails’ generated assignable-permission list. Environment roles remain read-only in this repair path and must already exist from the separate authorization sync. Existing role permissions are not removed, matching Rails.
- Isolate errors between memberships and organizations. Record either failure count and fail the aggregate BullMQ attempt; successful work is idempotent and may safely repeat during three bounded retries (four total attempts).
- Never log account identity, email, WorkOS user IDs, credentials or payloads. Structured diagnostics use local organization/membership IDs and counts.
The Rails cron entry is removed in the same candidate release. Retain the Rails job and model implementation for already-enqueued work and code-revert rollback. The separate WorkOS event processor and 03:15 authorization role/permission sync remain Rails-owned.
Verification
Focused unit tests cover page cursors, outcome totals, page-cap failure, role slug compatibility and continuation after membership or organization failures. PGlite tests exercise the production-shaped public Rails tables and sequences, ordinary status/role changes, organization-role import, shared environment-role reuse and exclusion of non-assignable WorkOS permissions. API2 typecheck and changed-file Rails lint are required before push.
Rollout and rollback
Deploy the API2 worker and Rails cron removal together. Confirm the deterministic BullMQ scheduler in US and EU and the absence of a newly registered Rails recurring entry. Observe an actual 03:00 run in each region, including plausible organization/page totals, successful retry or retained failure visibility, membership/role parity and no unresolved Sentry event. Require 24 hours after that run before marking ownership complete.
Rollback removes the BullMQ scheduler first, lets any active attempt finish or drain under the compatible worker, then restores the Rails cron by code revert. Do not manually replay memberships or run both recurring owners. Any production data repair, queue mutation or provider change requires separate authorization.