PostgreSQL Compatibility
This repo now has a dedicated PostgreSQL compatibility loop for the wrapped legacy launch.
Goals
- catch PostgreSQL blockers in
seedtrust_flask/andseedtrustapi/before release - preserve legacy behavior with public-behavior regression tests
- stop new MySQL-only code from landing while the legacy stack remains live
Commands
Run the static audit only:
uv run python scripts/postgres_compat_audit.pyRun the audit in fail-on-new-blockers mode:
uv run python scripts/postgres_compat_audit.py --checkRun the combined local workflow:
uv run python scripts/run_postgres_compat_checks.pyRun only the Postgres regression tests:
uv run python scripts/run_postgres_compat_checks.py --tests-onlyLegacy Migration Workflow
For the shared legacy schema, treat the working Postgres database as the baseline.
- Keep Alembic as the migration owner for legacy tables used by
seedtrust_flaskandseedtrustapi. - Keep Goose in
new-stlimited to experimental/shell concepts only;new-stis not the strategic product destination. - Do not replay the full MySQL-era Alembic history to create a fresh Postgres database.
uv run flask db check is now configured to ignore known pgloader representation drift so that the remaining diff output is focused on semantic mismatches:
TIMESTAMP WITH TIME ZONEvs legacyDateTime()model declarations- native Postgres enums vs equivalent portable SQLAlchemy enums
- numbered
pgloaderindex names vs model-owned indexes/unique constraints
Fresh Legacy Postgres Bootstrap
These steps change the target database and must be run manually.
- Restore a schema-only snapshot from a known-good Postgres legacy database.
- Stamp the restored database to the current Alembic head.
- Run
uv run flask db checkand inspect any remaining diffs before creating new migrations.
Example manual commands:
pg_dump --schema-only --no-owner --no-privileges "$SOURCE_DATABASE_URL" > legacy_schema.sqlpsql "$TARGET_DATABASE_URL" -f legacy_schema.sqlcd seedtrust_flask && uv run flask db stamp headcd seedtrust_flask && uv run flask db checkLegacy Schema Change Loop
- Update the shared schema/model metadata.
- Run
cd seedtrust_flask && uv run flask db check. - Audit the remaining diff for semantic changes such as real enum enforcement, uniqueness, or missing columns.
- Create the Alembic migration only after the diff is intentional.
- Run
uv run python scripts/run_postgres_compat_checks.py.
What The Audit Checks
Strict blockers fail the --check command unless they are already tracked in scripts/postgres_compat_allowlist.json.
- MySQL upserts in runtime Flask code
- MySQL timestamp math in runtime Flask code
- MySQL date arithmetic in runtime Flask code
- FastAPI trigger helpers that still reject PostgreSQL
- FastAPI trigger SQL that still uses MySQL-only syntax
Advisory categories are reported but do not fail the audit yet.
- raw SQL execution
- request id coercion risks
- string-based boolean handling
- naive datetime handling
Regression Test Convention
Every PostgreSQL fix should add or update a public-behavior test marked with postgres_compat.
Current marker commands:
cd seedtrust_flask && uv run pytest -m postgres_compatcd seedtrustapi && uv run pytest -m postgres_compatUse the marker for tests that protect:
- route/form coercion
- boolean behavior
- naive/aware datetime behavior
- config URL generation
- auth/session interoperability
- PostgreSQL-specific regressions discovered during staging
Iterative Remediation Loop
- Run
uv run python scripts/postgres_compat_audit.py - Pick one P0/P1 hotspot
- Write one failing public-behavior test with
@pytest.mark.postgres_compat - Make the smallest fix that restores behavior on PostgreSQL
- Run
uv run python scripts/run_postgres_compat_checks.py - If the fix removes a strict blocker entirely, delete its entry from
scripts/postgres_compat_allowlist.json - If a new strict blocker is discovered, add it to the allowlist with a note and follow-up test target
Current Strict Hotspots
The strict allowlist is currently empty. New strict hits should be treated as regressions.
The current high-risk PostgreSQL areas are still visible in the advisory audit output and should be burned down iteratively.
Staging Lane
Before launch, run the compatibility suite against the real clone database.
uv run st db config set testing-pg --no-interactiveuv run python scripts/run_postgres_compat_checks.pyThen do manual wrapped-shell smoke checks on PostgreSQL:
- FastAPI login + 2FA
- Flask admin login + dashboard
/api/agency/get- unread message count
- one disbursement/ledger flow
- one banking/reporting flow
Release Gate
Do not ship the wrapped legacy launch until all are true.
scripts/run_postgres_compat_checks.pypasses- no unowned P0/P1 PostgreSQL blockers remain
- launch-critical wrapped flows have a
postgres_compatregression test - staging smoke checks pass against the PostgreSQL clone
- any deferred issues are documented and outside launch-critical paths