Перейти к основному содержимому

ADR 0093 — Роль dbre становится двумя скриптами: линтер миграций и отчёт о резервном копировании

  • Статус: Принят
  • Дата: 2026-08-17
  • Контекст: решение владельца продукта предвосхитить часть роли dbre, объявленной в docs/fluxo.yml (ADR 0085), не дожидаясь срабатывания триггера ("реальный объём данных")
  • Пересматривает: docs/fluxo.yml, блок id: dbre

Контекст​

docs/fluxo.yml (ADR 0085) уже объявлял dbre как proposto, поглощённую dev-lead (ревью миграций) и platform (tuning, когда активируется), с критерием разделения: "volume real de dados no projeto gerenciado (hoje o risco é de SCHEMA, não de carga)".

Эта фраза уже содержала ответ, который оставалось исполнить. Из четырёх целевых результатов роли — parecer-de-migracao, plano-de-capacidade, backup-restore-testado, tuning — лишь два действительно зависят от реального объёма данных:

  • plano-de-capacidade и tuning требуют реальной нагрузки, чтобы что-то значить. Имитировать их без неё значило бы выдумать число — тот же класс ошибки, который ADR 0042 уже отвергает для оценки модели, а ADR 0077 отвергает для рейтинга возможностей.
  • parecer-de-migracao — это СТАТИЧЕСКИЙ анализ текста SQL: шаблон риска (DROP COLUMN, ALTER COLUMN ... TYPE, ADD COLUMN ... NOT NULL без DEFAULT) рискован независимо от того, десять или десять миллионов строк в управляемой базе.
  • backup-restore-testado уже реален и протестирован СЕГОДНЯ — CronJob резервного копирования работает с Фазы 5, пишет в backup_runs, а процедура восстановления была действительно ВЫПОЛНЕНА (docs/runbook.md#restore, реальный RTO ~40 с для базы ~108 KB). Не хватало лишь способа ПРОЧИТАТЬ это состояние по требованию, оформленного как отчёт.

Ни один из двух не требует LLM-агента (нет суждения на естественном языке — это распознавание шаблонов в тексте SQL и чтение таблицы) и GenServer engine (нет долгоживущего состояния и цикла — каждое выполнение — точечное чтение, запускаемое человеком или CI). Решением было трактовать их как то, чем они являются: два механических скрипта того же рода, что scripts/ci/pr-police.ts и apps/api/scripts/medir-execucao.ts.

Решение​

  1. apps/api/scripts/lint-migracao.ts просматривает ВСЕ apps/api/src/db/migrations/*.sql (без --projeto — это анализ репозитория, а не выполнения) и отмечает пять шаблонов риска построчно: DROP TABLE, TRUNCATE, DROP COLUMN, ALTER COLUMN ... TYPE/SET DATA TYPE и ADD COLUMN ... NOT NULL без DEFAULT. Логика ЧИСТАЯ (lintarConteudo, получает имя + текст SQL, возвращает находки), отделённая от адаптера ввода-вывода (lintarDiretorio/principal), та же конструкция, что у pr-police.ts (avaliarPr). Строка-комментарий игнорируется, потому что комментарии этого репозитория цитируют сами шаблоны в прозе, объясняя, почему их ИЗБЕГАЛИ (реальный случай: 0042_tough_captain_midlands.sql, строка 3). Завершается с != 0, если находит хотя бы одно вхождение.
  2. apps/api/scripts/relatorio-backup.ts читает backup_runs с ТОЙ ЖЕ логикой, что DomainGaugesCollector.collectBackup() (последний УСПЕХ — возраст, размер — и как завершилось ПОСЛЕДНЕЕ выполнение), оформленной как отчёт по требованию — не gauge Prometheus, а точечное чтение для того, кто хочет ответ сейчас. Ссылается на уже протестированную процедуру восстановления вместо её повторного выполнения. Логика классификации (avaliarBackup) чистая, тестируемая с подменённым backup_runs.
  3. Ни один из двух пока не входит в CI — см. Последствия.
  4. docs/fluxo.yml: dbre переходит из status: proposto в status: active. entregaveis_alvo (плоский список) становится entregaveis (список объектов со status): parecer-de-migracao и backup-restore-testado отмечены real, с доказывающим их механизмом; plano-de-capacidade и tuning сохранены как lacuna, с явной причиной. hoje_absorvido_por/criterio_de_separacao остаются в блоке, теперь описывая лишь то, что осталось, — правило ОДНОЙ миграции на волну (meta/_journal.json) больше НЕ называется "механизированной версией роли": оно предотвращает КОНФЛИКТ snapshot между параллельными агентами, забота, ортогональная "в этом SQL есть рискованный шаблон", у которой теперь есть собственный механизм.

Последствия​

  • Линтер просматривает ВЕСЬ репозиторий, а не diff PR. Его запуск против сегодняшних реальных миграций НАХОДИТ три вхождения в уже смёрженных и принятых миграциях — 0006_whole_princess_powerful.sql:22 и 0034_quick_saracen.sql:33 (DROP COLUMN), 0007_groovy_bullseye.sql:2 (ALTER COLUMN ... SET DATA TYPE). Это не дефект для исправления: это уже принятые решения, и их исправление мимоходом стёрло бы свидетельство того, почему они существовали (то же правило CLAUDE.md для находок Z/AD/AE). Именно поэтому скрипт не вошёл как шаг CI: gate, просматривающий весь репозиторий, отклонял бы каждый PR навсегда из-за находок, которые ему не принадлежат. Сделать его настоящим gate требует той же техники, что у pr-police.ts, — ограничить diff относительно базы PR, — оставленной на тот случай, когда dbre действительно понадобится БЛОКИРОВАТЬ merge; сегодня это заключение, а не вердикт, и он запускается вручную (pnpm --filter api lint:migracao).
  • Отчёт о резервном копировании не перезапускает восстановление. Он переиспользует исключительно чтение, которое уже делает collectBackup(); сама процедура восстановления уже протестирована и задокументирована (docs/runbook.md#restore). Смешение двух раздуло бы область скрипта до переписывания того, что уже существует и уже работает.
  • Порог "резервная копия опаздывает" (26 ч) дублирован, а не импортирован, из оповещения brabo-backup-atrasado (deploy/k8s/observability/alerts/brabo-alerts.yaml) — YAML Grafana не читается процессом Node скрипта. Два числа могут разойтись, если кто-то изменит одну сторону и забудет другую; это принято сознательно, с той же ценой, которую имел бы любой дублированный порог в коде.
  • plano-de-capacidade и tuning остаются ПРОБЕЛОМ, объявленным в docs/fluxo.yml, без срока. Триггером для их выделения остаётся реальный объём данных в УПРАВЛЯЕМОМ проекте — не объём token_usage/session_events самого Brabo, который уже велик, а объём продукта, который строят агенты.