diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 77f2f109..8892e585 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -67,6 +67,11 @@ name. Spell out: `config context manager request response environment utilities` `migrations/versions/`; each plugin owns its own chain under `plugins//migrations/`. New schema = a new migration with an idempotent guard and a real downgrade. Migrations must run clean on strict MySQL 8. + Any model column/table change ships WITH its migration in the SAME change, and + bringing up a new image MUST run `flask db upgrade` then + `flask plugin upgrade-all` before serving traffic. A model that references a + column its migration has not applied yet 500s every query on that table + (`1054 Unknown column`), so code and schema must never be deployed out of step. - **Asset model is the platform contract.** Physical things are an `Asset` plus a plugin subtype row linked by `assetid` (FK, `ON DELETE CASCADE`). Consumables with quantities are standalone tables, not assets. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 848e3026..5fddca00 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,6 +24,24 @@ - **Index names**: `idx__`. Underscores allowed here only because indexes are infrastructure, not data. - Good: `idx_machines_locationid` +### Migrations and deploy ordering + +- **A model change ships WITH its migration in the same change.** Adding or + changing a column/table on a SQLAlchemy model without the matching Alembic + migration is a broken change, even if it passes tests against a freshly built + schema. +- **Code and schema must never deploy out of step.** A model that references a + column its migration has not applied yet makes MySQL raise + `1054 Unknown column`, which 500s every query on that table (this is not a + degraded read - the whole endpoint fails). +- **Bringing up a new image MUST run migrations before serving traffic**, in + order: `flask db upgrade` (core chain) then `flask plugin upgrade-all` + (per-plugin chains). A rebuilt image with new model code but an + un-migrated database will 500 until this runs. +- Core schema lives in `migrations/versions/`; each plugin owns its own chain + under `plugins//migrations/`. New schema = a new migration with an + idempotent guard and a real downgrade, clean on strict MySQL 8. + ### Allowed acronyms (closed list, expand by PR) - **Universal**: `id`, `url`, `api`, `http`, `https`, `json`, `jwt`, `sql`, `os`, `ip`, `dns`, `csv`, `pdf`, `cors`, `ttl`, `uuid`, `html`, `css`, `orm`