Skip to content

Backfill user majors from GradTrak plans - #1204

Open
ritamzico wants to merge 1 commit into
ritam/major-vocabulary-plumbingfrom
ritam/backfill-user-major-from-gradtrak
Open

ritamzico wants to merge 1 commit into
ritam/major-vocabulary-plumbingfrom
ritam/backfill-user-major-from-gradtrak

Conversation

@ritamzico

@ritamzico ritamzico commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add an idempotent migration that backfills empty user.majors and
    user.minors fields from each user's GradTrak plan
  • validate all migrated values against the canonical vocabulary introduced by
    PR Canonical degree vocabulary + user major API #1187
  • add a unique index on plans.userEmail to enforce one plan per user at the
    database layer

This PR is stacked on #1187 (ritam/major-vocabulary-plumbing); review and merge
that PR first.

Production data findings

The initial read-only audit found:

  • 2,802 plans and 47,013 users
  • 2,685 plans with non-empty majors and 792 with non-empty minors
  • no plan major/minor values outside the canonical vocabulary
  • five degree-bearing plans without a matching user
  • 11,745 users with existing majors, traced to the April 2025 PostgreSQL-to-MongoDB
    user migration; these values are intentionally never overwritten

The audit also found 18 duplicate plan.userEmail groups containing 43
superseded plans. Per Ritam's decision, the older plans were archived intact in
plans_duplicate_archive_pr2_20260909 and removed, retaining the newest plan for
each user. Production now has zero duplicate groups.

The plans.userEmail_1 unique index has already been built and verified manually
in production. No additional manual index build is required after deployment;
the schema declaration keeps the invariant in code and applies it to other
environments.

Migration behavior

The migration processes plans in batches of 500 and:

  • supports DRY_RUN=1, logging every proposed change without writing
  • copies a field only when the corresponding user field is empty, absent, or null
  • skips plans without users instead of failing the run
  • skips and reports non-canonical values
  • uses conditional writes so concurrent profile updates cannot be overwritten
  • reports plans scanned, users updated, major/minor fields backfilled, and every
    skip category

It is safe to rerun. After a successful live run, migrated users fall into the
already-set category.

Latest production dry-run snapshot (2026-09-09):

  • 2,662 plans scanned
  • 2,319 users would be updated
  • 2,161 major fields would be backfilled (4.59% of 47,090 users)
  • 783 minor fields would be backfilled
  • five plans skipped without a user
  • 338 plans skipped because the relevant user fields were already set
  • zero plans skipped for invalid values

The live backfill has intentionally not been run. It will be executed immediately
before the profile major/minor editor is enabled.

Usage

# Preview
DRY_RUN=1 npm run migrate:user-majors

# Apply
npm run migrate:user-majors

Verification

  • npm run type-check --workspace=apps/backend
  • npm run lint --workspace=apps/backend
  • npx tsc -p packages/common/tsconfig.json --noEmit
  • ESLint on the changed common-package files
  • production dry run with fully reconciled counters
  • production unique index and zero-duplicate verification

🤖 Generated with Claude Code

https://claude.ai/code/session_01QNN5LA8L23gjK9vEsKHAVV

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 72c4518555

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/backend/src/scripts/backfill-user-majors.ts Outdated
@ritamzico
ritamzico added this pull request to stack #1205 September 9, 2026 21:28
Add an idempotent, dry-run-capable migration that copies canonical majors and minors from each user’s plan without overwriting existing profile values. Enforce the one-plan-per-user invariant with a unique userEmail index.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01QNN5LA8L23gjK9vEsKHAVV
@ritamzico
ritamzico force-pushed the ritam/backfill-user-major-from-gradtrak branch from 72c4518 to e576f65 Compare September 9, 2026 21:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant