Fix the onboarding data migration on Postgres #136

Merged
robbertbos merged 1 commit from fix/onboarding-migration-postgres into main 2026-07-19 22:06:26 +00:00
Owner

The onboarding data migration (c1f0a7d92b41, merged with the coach-marks PR) compares and assigns the json column hints_seen with raw SQL strings. SQLite treats json as text and accepts it; Postgres has no = operator for the json type and does not implicitly cast a varchar bind parameter on assignment, so alembic upgrade head fails while planning the statement.

Because sync_runner._ensure_schema() runs upgrade head in the FastAPI lifespan, a deploy from current main would fail to boot the pod on Postgres (ZAD).

Found by the new migration-test harness on the fase-1-fundament-v2 branch (PR #123): its backend-test-postgres job went red on exactly this. That is the class of bug the harness was built to catch - the revision passes on SQLite and fails on Postgres.

Fix

Rewritten with typed SQLAlchemy constructs: a sa.table with a sa.JSON column (so the bind parameter is serialized and typed correctly on both dialects) and an explicit CAST(hints_seen AS TEXT) for the comparisons. Semantics unchanged: only rows with an empty/NULL hints_seen belonging to users with a connected account are marked, and re-running stays a no-op.

Verified

  • upgrade head -> downgrade base -> upgrade head against Postgres 17: clean.
  • The 6 migration tests from the fase-1-fundament-v2 branch: green on SQLite and Postgres with this fix (they fail on Postgres without it).
  • Full backend suite: 1337 passed.

After merge, PR #123 needs one more rebase to pick this up; its own migration re-parents onto this chain.

The onboarding data migration (`c1f0a7d92b41`, merged with the coach-marks PR) compares and assigns the `json` column `hints_seen` with raw SQL strings. SQLite treats json as text and accepts it; Postgres has no `=` operator for the `json` type and does not implicitly cast a `varchar` bind parameter on assignment, so `alembic upgrade head` fails while planning the statement. Because `sync_runner._ensure_schema()` runs `upgrade head` in the FastAPI lifespan, a deploy from current main would fail to boot the pod on Postgres (ZAD). Found by the new migration-test harness on the `fase-1-fundament-v2` branch (PR #123): its `backend-test-postgres` job went red on exactly this. That is the class of bug the harness was built to catch - the revision passes on SQLite and fails on Postgres. ## Fix Rewritten with typed SQLAlchemy constructs: a `sa.table` with a `sa.JSON` column (so the bind parameter is serialized and typed correctly on both dialects) and an explicit `CAST(hints_seen AS TEXT)` for the comparisons. Semantics unchanged: only rows with an empty/NULL `hints_seen` belonging to users with a connected account are marked, and re-running stays a no-op. ## Verified - `upgrade head` -> `downgrade base` -> `upgrade head` against Postgres 17: clean. - The 6 migration tests from the fase-1-fundament-v2 branch: green on SQLite and Postgres with this fix (they fail on Postgres without it). - Full backend suite: 1337 passed. After merge, PR #123 needs one more rebase to pick this up; its own migration re-parents onto this chain.
Fix the onboarding data migration on Postgres
All checks were successful
CI / pre-commit (pull_request) Successful in 55s
CI / frontend-test (pull_request) Successful in 5m3s
CI / release-scripts (pull_request) Successful in 10s
security-scan / Python SCA (pip-audit) (pull_request) Successful in 57s
security-scan / Python SAST (bandit) (pull_request) Successful in 38s
security-scan / JS SCA (npm audit) (pull_request) Successful in 45s
CI / backend-test (pull_request) Successful in 8m26s
security-scan / Filesystem scan (trivy fs) (pull_request) Successful in 23s
security-scan / SBOM (trivy) (pull_request) Successful in 15s
CI / backend-test-postgres (pull_request) Successful in 9m4s
test-build / build (backend) (pull_request) Successful in 1m54s
CI / e2e (pull_request) Successful in 9m42s
test-build / build (frontend) (pull_request) Successful in 2m13s
test-build / build (pull_request) Successful in 0s
CI / pre-commit (push) Successful in 50s
CI / frontend-test (push) Successful in 5m23s
CI / release-scripts (push) Successful in 10s
CI / backend-test (push) Successful in 9m1s
publish-main / build (backend) (push) Successful in 2m8s
CI / backend-test-postgres (push) Successful in 9m31s
security-scan / Python SCA (pip-audit) (push) Successful in 58s
security-scan / Python SAST (bandit) (push) Successful in 34s
security-scan / Filesystem scan (trivy fs) (push) Successful in 23s
security-scan / JS SCA (npm audit) (push) Successful in 39s
publish-main / build (frontend) (push) Successful in 2m12s
publish-main / build (push) Successful in 0s
security-scan / SBOM (trivy) (push) Successful in 16s
CI / e2e (push) Successful in 9m51s
9cdf70822b
The raw-SQL text comparison on hints_seen has no = operator for the json
type on Postgres, and the varchar bind parameter is not implicitly cast on
assignment either. The migration runs at application startup, so a deploy
from current main would fail to boot the pod.

Rewritten with typed SQLAlchemy constructs (JSON column, explicit CAST to
TEXT for the comparisons), which render correctly on both SQLite and
Postgres. Verified with upgrade/downgrade/upgrade against Postgres 17 and
the migration test suite from the fase-1-fundament-v2 branch.
robbertbos deleted branch fix/onboarding-migration-postgres 2026-07-19 22:06:26 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
robbertbos/waggle!136
No description provided.