feat(migrations): Unify schema and data journals#896
Conversation
Add a publishable migration package that executes SQL and TypeScript entries from one Drizzle-compatible journal. Route core and plugin upgrades through the shared runner so schema changes and resumable data migrations use one ordered ledger. Keep historical scripts isolated behind versioned capabilities, and wire the package into release, CI, Craft, documentation, and package validation. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Remove the migration task registry and keep historical data migration code in each journal entry. This prevents future runtime refactors or deletions from breaking pending upgrades. Expose versioned migration capabilities, move scheduler and core backfills into their migration files, and update migration tests and documentation. Co-Authored-By: GPT-5.6 Codex <noreply@openai.com>
Expose the exact host database adapter through MigrationContextV1 instead of reconstructing a partial SQL capability. Rebuild the SQL-heavy frozen migrations without connection setup and driver implementations so migrations retain transformation logic without duplicating database infrastructure. Co-Authored-By: GPT-5.6 Codex <noreply@openai.com>
| return { | ||
| bot: botConfig, | ||
| state: { keyPrefix: process.env.JUNIOR_STATE_KEY_PREFIX?.trim() || void 0 }, | ||
| }; |
There was a problem hiding this comment.
Advisor import builds broken adapter
High Severity
When a conversation has advisor/subagent history and no advisorSessionStore is injected, importConversationFromLegacy falls back to createLegacyAdvisorSessionReader, which calls capsule getStateAdapter(). The stub getChatConfig only exposes keyPrefix and omits adapter/redisUrl, so createStateAdapter throws REDIS_URL is required even when REDIS_URL is set in the environment.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit b26c218. Configure here.
Allow migrations to import append-only infrastructure helpers from @sentry/junior/migration-helpers/v1 while continuing to reject unversioned Junior runtime imports. Keep one-off Redis transformations and backfill decisions in their journal entries, replace generated capsules for migrations 0004 through 0006 with readable source, and publish explicit helper types without private module references. Co-Authored-By: GPT-5.6 Codex <noreply@openai.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
There are 3 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit fd37ff2. Configure here.
| SET status = 'completed', result = $1, completed_at = NOW() | ||
| WHERE created_at = $2`, | ||
| [result ?? null, args.migration.when], | ||
| ); |
There was a problem hiding this comment.
JSONB params break on strings
Medium Severity
progress.save and the migration result write bind MigrationJsonValue directly into JSONB columns. For string primitives, node-pg sends the raw text rather than a JSON-encoded string, so values like a string cursor fail with invalid JSON syntax. Objects work because they are stringified; the public value type still allows strings.
Reviewed by Cursor Bugbot for commit fd37ff2. Configure here.
| ...current | ||
| } = run; | ||
| return current; | ||
| } |
There was a problem hiding this comment.
Scheduler migration under-validates records
Medium Severity
The embedded scheduler data migration accepts tasks and runs after checking only a few fields, then inserts them into SQL. The removed migrateSchedulerStateToSql path used strict schema parsing before write, and the SQL readers still use those strict schemas, so partial or legacy-shaped Redis records can be stored as rows the runtime later silently drops.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit fd37ff2. Configure here.


Summary
@sentry/junior-migrationspackage for mixed SQL and TypeScript Drizzle journals0001_name.tsdata migrations through the same Drizzle Kit workflow used for schema migrationscontext.databaseinstead of rebuilding partial SQL capabilities@sentry/junior/migration-helpers/v1export for reusable parsers, stores, adapters, and key resolutioncontext.tasks, pluginmigrationTasks, and the duplicate hardcoded migration implementations from Junior runtime codeMigration contract
Each journal entry resolves to exactly one immutable
<tag>.sqlor<tag>.tsfile. TypeScript migrations target the versionedMigrationV1capability API and contain their permanent parsers, transforms, and migration-specific helpers. They cannot import relative application modules or unversioned@sentry/juniormodules. They may import the append-only@sentry/junior/migration-helpers/v1surface for infrastructure helpers, while one-off record transformations remain owned by the migration file.The runner tracks each entry by its journal timestamp and source hash, persists resumable TypeScript progress, serializes execution with the owning SQL lock, and supports SQL-only setup where tests or bootstrap paths do not have data-migration capabilities. TypeScript entries receive the same database adapter used by the journal runner, so connection setup, drivers, transactions, and lock implementations are not duplicated into frozen migrations.
Drizzle Kit remains the authoring and snapshot system. Its stock
migrate()executor is intentionally unsupported for mixed journals; Junior and plugins execute them throughrunMigrationJournal.Test Plan
pnpm --filter @sentry/junior-migrations test@sentry/junior-migrations,@sentry/junior-plugin-api,@sentry/junior-scheduler, and@sentry/juniorpnpm lintpnpm release:checkpnpm docs:checkgit diff --check