Migration Run Procedure

Authoritative step-by-step for applying a migration to scidex_v2.

Source: docs/operations/migration-run-procedure.md

v1 → v2 Migration Run Procedure

Script: scripts/migrate-from-v1.py Spec: SPEC-006 §3 (Phase C Cutover) Last updated: 2026-05-16


Prerequisites

Requirement Check
v1 Postgres reachable psql $SCIDEX_V1_DSN -c '\l'
v2 Postgres reachable psql $SCIDEX_DSN -c '\l'
v2 schema applied psql $SCIDEX_DSN -c 'SELECT COUNT(*) FROM schema_registry'
Python env active python -c 'import psycopg; import scidex_substrate'

Set environment variables before running:

export SCIDEX_V1_DSN="postgresql://scidex_v1_ro@127.0.0.1:5432/scidex"  # v1 source (read-only)
export SCIDEX_DSN="postgresql://scidex_v2_app@127.0.0.1:5432/scidex_v2"   # v2 target

Or pass them directly via --v1-dsn / --v2-dsn flags.


Step 1 — Validate row counts (no writes)

python scripts/migrate-from-v1.py --check --json

This prints a delta table and emits a JSON summary:

{
  "actors": {"v1_count": 1200, "v2_count": 1198, "match_pct": 99.83},
  "papers": {"v1_count": 45000, "v2_count": 44200, "match_pct": 98.22},
  ...
}

Gate: match_pct ≥ 80 for hypotheses, papers, wiki_pages, actors.


Step 2 — Dry run (no writes committed)

python scripts/migrate-from-v1.py --dry-run

Runs all migration steps but rolls back all v2 writes before exit. Use this to confirm the script runs end-to-end without fatal errors. Exit code 0 = safe to proceed.


Step 3 — Full migration

python scripts/migrate-from-v1.py

Migration order (dependency-safe):

  1. schema_registry — seed artifact type schemas

  2. actors — user accounts + identities

  3. artifacts — papers, hypotheses, wiki pages, knowledge edges, gaps

  4. signals — proposal votes, senate votes

  5. events — audit trail

Each step is idempotent: rerunning inserts missing rows and skips existing ones via ON CONFLICT DO NOTHING / upsert logic.


Step 4 — Verify after migration

python scripts/migrate-from-v1.py --check --json

Confirm match_pct ≥ 80 across all key tables. If any table is below threshold, see Troubleshooting below.


Incremental / partial re-runs

# Only migrate rows created after a date (incremental catch-up):
python scripts/migrate-from-v1.py --since 2026-05-01

# Only migrate specific types:
python scripts/migrate-from-v1.py --types actors,artifacts

# Combine:
python scripts/migrate-from-v1.py --types artifacts --since 2026-01-01

Exit codes

Code Meaning
0 All steps completed; sanity checks passed
1 Sanity check failed — v1 should stay up; do not flip traffic
2 Fatal error (connection failure, SQL error); check logs

Rollback

The script never deletes v1 data. If migration produces an unacceptable v2 state:

  1. Keep v1 up (it was never written to).

  2. Truncate affected v2 tables: TRUNCATE actors, artifacts, … RESTART IDENTITY CASCADE;

  3. Re-run from Step 1.

For a selective rollback of one entity type:

python scripts/migrate-from-v1.py --types actors --since 2026-05-15

Troubleshooting

Symptom Likely cause Fix
match_pct < 80 for papers Some v1 papers have null primary keys Check scripts/backfill/cleanup_null_id_wiki_pages.py
Exit code 2, FATAL: connection refused Wrong DSN Verify SCIDEX_V1_DSN / SCIDEX_DSN
SanityCheckError v2 is missing referential integrity Run --skip-checks to inspect counts, fix root cause, re-run
Slow run (> 30 min) Large row counts without batch tuning Add --types to migrate one step at a time

Related

  • SPEC-006 §3 — cutover runbook spec

  • docs/operations/v2-rc-checklist.md §7 — data migration gate

  • src/scidex_substrate/migrations/from_v1/ — individual migration modules

  • tests/integration/test_migrate_v1_v2.py — full integration test suite