Context
`schema_meta.schema_version` was added but there is no migration runner. Future schema changes (e.g. `effective_date` on adjustments, per-user schemas, etc.) currently require manual SQL on the production DB.
Proposal
A lightweight, stdlib-only migration runner:
- `app/migrations/0001_initial.sql`, `0002_xyz.sql`, …
- `init_db()` reads current `schema_version`, applies pending migrations in order, bumps the version.
- Each migration wrapped in a transaction.
- Dry-run CLI: `uv run python -m app.migrate --check`.
Alternative: adopt `yoyo-migrations` (adds a dep) or `alembic` (heavier, needs SQLAlchemy).
Acceptance
- Adding a new SQL file under `app/migrations/` and bumping the version is the only change needed for schema evolution.
- CI runs migrations against an empty DB and an existing v1 DB.
Context
`schema_meta.schema_version` was added but there is no migration runner. Future schema changes (e.g. `effective_date` on adjustments, per-user schemas, etc.) currently require manual SQL on the production DB.
Proposal
A lightweight, stdlib-only migration runner:
Alternative: adopt `yoyo-migrations` (adds a dep) or `alembic` (heavier, needs SQLAlchemy).
Acceptance