From 8b5a4758e5fdc90956ef58b8c0104ed01b5973c1 Mon Sep 17 00:00:00 2001 From: Nguyen Ngo Date: Tue, 7 Jul 2026 20:31:32 -0400 Subject: [PATCH] Jul 7 - Fix alembic migration script --- CLAUDE.md | 8 ++++---- ...ation_recipients.py => phase37_contract_recipients.py} | 6 +++++- 2 files changed, 9 insertions(+), 5 deletions(-) rename migrations/versions/{phase37_project_notification_recipients.py => phase37_contract_recipients.py} (89%) diff --git a/CLAUDE.md b/CLAUDE.md index 703a8f0..8efb2fc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -781,7 +781,7 @@ limiter = Limiter( ## 17. Alembic Migration Chain -**Current HEAD:** `phase37_project_notification_recipients` (35 migrations total). +**Current HEAD:** `phase37_contract_recipients` (35 migrations total). **Chain root:** `0003_add_user_active` — a guarded squashed baseline (MT-2) that recreates the full 25-table schema with INFORMATION_SCHEMA guards. The original baseline migrations (0001/0002/0003) were lost; this file restores the chain root so Alembic can build the revision map. `down_revision = None`. @@ -813,10 +813,10 @@ limiter = Limiter( → phase34_inspection_schedules → phase35_user_mfa → phase36_issue_work_orders - → phase37_project_notification_recipients ← HEAD + → phase37_contract_recipients ← HEAD ``` -### phase37_project_notification_recipients +### phase37_contract_recipients Creates the `project_notification_recipients` table backing per-contract additional notification recipients (see §5 model + the `/projects//recipients` routes). Each row subscribes one recipient — a staff User (in-app + email) or an external email address (email only) — to a chosen set of notification-matrix event types, scoped to events occurring in that contract's facilities. Dispatched by `notify_by_matrix()` → `_notify_project_recipients()`. Guarded by an `INFORMATION_SCHEMA` table-existence check — safe to re-run. @@ -1691,7 +1691,7 @@ Ask: Does this change break any other code path that uses the modified function, **Rule 13 — List every file changed** with the exact location of each change (function name and what was modified). **Rule 14 — Migrations are required for any schema change.** -Follow the `phase{N}_description.py` naming convention. The new migration's `down_revision` must point to the current HEAD (`phase37_project_notification_recipients`). Use `INFORMATION_SCHEMA` existence checks so migrations are safe to re-run. Never use `batch_alter_table` for MySQL. +Follow the `phase{N}_description.py` naming convention. The new migration's `down_revision` must point to the current HEAD (`phase37_contract_recipients`). **Revision ids must be ≤ 32 characters** — `alembic_version.version_num` is `VARCHAR(32)`; a longer id passes every migration step and then fails the final version-pointer UPDATE with MySQL error 1406 (`Data too long for column 'version_num'`), leaving the DDL applied (auto-committed) but the version stamp still on the previous revision. Use `INFORMATION_SCHEMA` existence checks so migrations are safe to re-run. Never use `batch_alter_table` for MySQL. Self-contained package, own `ControlBase` + engine/session, own Alembic chain. No imports from `app/`. diff --git a/migrations/versions/phase37_project_notification_recipients.py b/migrations/versions/phase37_contract_recipients.py similarity index 89% rename from migrations/versions/phase37_project_notification_recipients.py rename to migrations/versions/phase37_contract_recipients.py index b34ec7a..653dc40 100644 --- a/migrations/versions/phase37_project_notification_recipients.py +++ b/migrations/versions/phase37_contract_recipients.py @@ -10,12 +10,16 @@ app/utils/notifications.py, AFTER the global matrix roles and custom emails. Idempotent: guarded by an INFORMATION_SCHEMA table-existence check so it is safe to re-run across every tenant DB (CLAUDE.md rule 14). + +NOTE: the revision id is deliberately short — alembic_version.version_num is +VARCHAR(32); ids longer than 32 chars fail the version-pointer UPDATE with +MySQL error 1406 ("Data too long for column 'version_num'"). """ import sqlalchemy as sa from alembic import op -revision = 'phase37_project_notification_recipients' +revision = 'phase37_contract_recipients' down_revision = 'phase36_issue_work_orders' branch_labels = None depends_on = None