Problem
The migrations check (added to prevent the claimed_at-style drift) only verifies that every *.sql file is listed in applied.txt (grep -qxF). It never connects to the database, so it can’t tell whether a migration was actually run on prod — a developer can add the filename to make CI green without running the SQL.
Evidence — backend/migrations/applied.txt, .github/workflows/migrations.yml:39-41.
Impact — It catches “you forgot to record it,” not “you forgot to apply it” — the exact failure it’s named for could still recur if someone lists the file but skips the SQL.
Suggested direction — Have the release/CI step actually verify prod/staging: compare applied.txt against a schema_migrations table the apply step writes to, or run a post-deploy smoke SELECT for the columns/tables each migration adds (psql "$DIRECT_URL"). That turns the honor system into verification. (Adopting Alembic would also kill the ledger and the hand-maintained models.py.)
Problem
The migrations check (added to prevent the
claimed_at-style drift) only verifies that every*.sqlfile is listed inapplied.txt(grep -qxF). It never connects to the database, so it can’t tell whether a migration was actually run on prod — a developer can add the filename to make CI green without running the SQL.Evidence —
backend/migrations/applied.txt,.github/workflows/migrations.yml:39-41.Impact — It catches “you forgot to record it,” not “you forgot to apply it” — the exact failure it’s named for could still recur if someone lists the file but skips the SQL.
Suggested direction — Have the release/CI step actually verify prod/staging: compare
applied.txtagainst aschema_migrationstable the apply step writes to, or run a post-deploy smokeSELECTfor the columns/tables each migration adds (psql "$DIRECT_URL"). That turns the honor system into verification. (Adopting Alembic would also kill the ledger and the hand-maintainedmodels.py.)