TimestampMixin was setting default=datetime.now(UTC) — calling the
function once at module import time. Every INSERT (and every UPDATE
via onupdate) since the app last restarted got stamped with the same
process-start timestamp, silently breaking created_at / updated_at on
every table that uses the mixin (customers, stores, cards, users,
orders, etc.).
Pass the _utc_now callable instead so SQLAlchemy invokes it per row.
Forward fix only — historical rows on prod since the most recent app
restart all read as the start-time microsecond and will need a
separate decision on whether to backfill.
Found while debugging why a customer + their loyalty card had
microsecond-identical created_at timestamps despite being created
through separate service calls.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Replace black, isort, and flake8 with Ruff (all-in-one linter and formatter)
- Add comprehensive pyproject.toml configuration
- Simplify Makefile code quality targets
- Configure exclusions for venv/.venv in pyproject.toml
- Auto-fix 1,359 linting issues across codebase
Benefits:
- Much faster builds (Ruff is written in Rust)
- Single tool replaces multiple tools
- More comprehensive rule set (UP, B, C4, SIM, PIE, RET, Q)
- All configuration centralized in pyproject.toml
- Better import sorting and formatting consistency
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Standardize quote style (single to double quotes)
- Reorder and group imports alphabetically
- Fix line breaks and indentation for consistency
- Apply PEP 8 formatting standards
Also updated Makefile to exclude both venv and .venv from code quality checks.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>