Editing a scenario and saving (with or without changes) returned 500: function pg_advisory_xact_lock(smallint, bigint) does not exist Postgres only ships (int4, int4) and (bigint) variants. The two-arg call passed `m = hash(uuid) & 0xFFFFFFFF` which can reach 2^32-1, so psycopg promoted it to bigint and no overload matched. Switched to the single-arg bigint form. While there, replaced Python's built-in hash() with hashlib.blake2b(...) — the built-in is randomised per process via PYTHONHASHSEED, so gunicorn workers were computing different lock keys for the same scenario and the lock wasn't actually serialising across workers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Metamorph backend
Flask 3 API. See repo root README.md for the big picture.
Layout
app/
├── api/ # HTTP layer (blueprints), versioned under /api/v1
├── core/ # config (env-driven), structured logging
├── db/ # SQLAlchemy session + Alembic (M1+)
├── models/ # ORM models (M1+)
├── services/ # domain logic (M2+)
└── i18n/ # message catalogs (M13)
tests/ # pytest
Local dev
Requires uv and a reachable Postgres (M1+; not needed yet for /health).
uv sync # install deps from pyproject.toml
uv run flask --app app.main run --debug --port 8000
curl http://localhost:8000/api/v1/health
Tests
uv run pytest
Lint
uv run ruff check .
uv run ruff format .