Migration clean-room integrity gate (reset_target)
Summary
An opt-in clean-room integrity hard-gate for re-rehearsing the ETL against the
shared staging DB: reset the target to a clean (schema + seeds only) state, then run
rangeday db migrate-legacy --entity all --fail-on-integrity, so any integrity
violation is a real ETL bug — not a pre-existing staging app row orphaning against the
legacy source-maps. Surfaces as a reset_target input on
staging-migration-deploy’s run-migration-etl dispatch.
Deferred from the 2026-06-23 integrity-surfacing change (the --fail-on-integrity flag +
INTEGRITY_VIOLATIONS=N line + the workflow’s always-warn step already shipped — those
kill green-washing). This item is the clean-room hard-gate half, parked behind a real
blocker (below). It is wanted for staging validation around Phase 4 — not now.
Requirements
- In-cluster re-migrate path (the blocker). The migration image (
Dockerfile.migration) copies onlycli/apps/sync/libs/*/protoand installs no sqlx-cli; the 46 migrations live inapps/server/migrations/and are embedded only in the server viasqlx::migrate!("./migrations")(apps/server/src/main.rs).rangeday db resetshells to thesqlxbinary (cli/src/main.rs), so the image cannot re-migrate at all. Fix: embedapps/server/migrationsinto therangedaybinary viasqlx::migrate!(a built-in migrator path that does not shell to sqlx-cli) and COPY that dir intoDockerfile.migration— making the image self-sufficient for schema + seed re-application. - DROP SCHEMA, not DROP DATABASE. Managed Postgres blocks dropping the connected DB
(and the role may lack the privilege), so
sqlx database reset(DROP DATABASE) is a no-op trap. Reset viaDROP SCHEMA public CASCADE; CREATE SCHEMA public;+ re-run migrations (re-applies schema + seeds), or — if the role does not ownpublic— a targeted TRUNCATE of ETL-written tables that preserves seeds and clearslegacy_id_map. - Fail-loud. The reset step must verify the target is actually clean (assert row
counts / seeds-only) and fail the dispatch otherwise —
reset_targetmust never silently no-op (the trap that motivated this item). reset_targetdispatch input (defaultfalse), labeled unmistakably destructive (it wipes the shared staging app DB) in the input description + a job-summary warning; whentrue→ reset → run with--fail-on-integrity.
Context
Not on the cutover path: at cutover the production target is clean by construction (fresh
DB), so --fail-on-integrity is already a meaningful hard gate there with no reset.
reset_target exists solely to get a clean room in the shared staging DB for repeat
rehearsals. The 2 integrity orphans seen in the 2026-06-23 rehearsal can be confirmed
benign by querying those rows directly (no reset needed), and the always-warn keeps the
count visible on every run in the meantime.
Details
Dispatch flow when reset_target: true (on the staging run-migration-etl):
- Reset the target in-cluster (a step / init using the self-sufficient image):
DROP SCHEMA public CASCADE; CREATE SCHEMA public;, then the embedded migrator re-applies schema + seeds. Connect as the target role; neverDROP DATABASE. - Verify clean — assert the target holds only seed rows (e.g. 0
scores, 0hit_factor_entries, emptylegacy_id_map) and fail the dispatch otherwise, so a restricted-privilege no-op can never masquerade as a clean room. - Run
migrate-legacy --entity all --fail-on-integrity→ the binary exits non-zero on any violation → the Job’sfailedcondition trips the result-gate → the dispatch fails loudly, with the count already surfaced by the always-warn step.
Default (reset_target: false) is unchanged: no reset, no --fail-on-integrity; the
always-warn step still reports the count.
Open Questions
- Does the staging target’s migration role own the
publicschema (can itDROP SCHEMA public CASCADE)? If not, the reset falls back to the targeted-TRUNCATE variant — confirm with James when this is picked up. - Should the embedded-migrator change also replace the sqlx-cli shell-out in
rangeday db migrate/rollback(consistency), or only add a self-sufficient reset path?
Cross-ref: e2e squad fixture re-assertion
Migration 0024_e2e_squad_fixture is self-gated on the e2e user existing and runs
ONCE per database — any reset flow built here must re-assert that fixture after
re-registering the e2e user (re-execute the 0024 body — it is idempotent — or move
to boot-time env-gated seeding if resets become routine).