9.5 KiB
Sonnet Review: план миграции ВМ → Flask — анализ и ответ
Дата: 2026-07-14
Источник: Sonnet (Claude) — ревью History/migration-vm-to-flask-plan-2026-07-14.md
Резюме Соннета
План одобрен архитектурно. Критичных блокеров — 2, важных дополнений — 3, улучшений — 5.
Критичные проблемы (Соннет)
1. site/ — конфликт с Python stdlib ⛔
from site.db import ...—siteэто встроенный модуль Python. При запуске не из корня Python найдёт встроенныйsite, а не локальный.
Моё мнение: ✅ Согласен полностью. Это реальная проблема. site — built-in модуль Python, импортируется при старте интерпретатора. Если PYTHONPATH или sys.path поставит корень раньше чем site-packages — получим наш пакет. Если наоборот — stdlib. Нестабильно.
Решение: переименовать site/ → app/ или contracts_app/. Я за app/ — короче, семантически понятно (Flask application factory), не конфликтует ни с чем.
2. /api/cleanup — нулевая аутентификация ⛔
Любой может POST /api/cleanup → потеря всех данных.
Моё мнение: ✅ Согласен. Но с нюансом. Текущий convert_server.py тоже имеет этот эндпоинт без auth — и он на внешнем домене contracts.kube5s.ru. Так что это не регресс, а существующая дыра.
Решение:
- Минимум:
X-Api-Keyв заголовке, сверка сos.environ["API_KEY"] - Средний: nginx
allow 127.0.0.1; deny all;на этот location (если cleanup вызывается только с самого Flask-контейнера) - Правильный: убрать cleanup вообще, заменить на автоочистку по TTL (cron/APScheduler)
Но это отдельная задача, не часть миграции. В рамках миграции — просто не ухудшить ситуацию.
Важные дополнения (Соннет)
3. Файловый lock для classify — хрупкий
/tmp/classify_{batch_id}.lock— если Flask упадёт, lock остаётся навсегда.
Моё мнение: ✅ Согласен. Lock-файл — антипаттерн в stateless-приложении. Сейчас в convert_server.py та же схема (subprocess + lock-файл), и она работает только потому что ВМ не рестартит.
Решение: in-memory dict[batch_id] → thread + проверка thread.is_alive(). При старте Flask никаких lock-файлов нет. Лучше: store classify_status='processing' в БД и сбрасывать processing → pending при старте приложения.
4. SSE — нет heartbeat
При медленном LLM nginx и браузеры режут соединение.
Моё мнение: ✅ Согласен. Добавить yield ": heartbeat\n\n" раз в 15 секунд. SSE-комментарий (строка начинается с :) игнорируется EventSource, но сбрасывает таймауты nginx/браузера.
5. Старые proxy-роуты — явно не сказано удалить
В плане написано «создать новый app.py», но не сказано явно «удалить старые /api/* proxy-роуты».
Моё мнение: ✅ Согласен, но это очевидно. Новый app.py полностью заменяет старый — старых роутов /chat, /api/prompts (proxy на ВМ) не будет, потому что логика теперь локальная. Но в плане стоит написать явно: «удалить ВСЕ proxy-роуты, они больше не нужны».
Улучшения (Соннет)
6. prompts_bp.py и pages_bp.py — не раскрыты
Моё мнение: ✅ Справедливо. Но они тривиальны: prompts_bp.py = CRUD по таблице prompts (4 ручки), pages_bp.py = render_template("index.html") + render_template("architect.html"). Не расписывал детально потому что там нечего расписывать. Можно добавить один абзац для полноты.
7. drhider/ — не упоминается
Моё мнение: ⚠️ Справедливо, но осознанно. drhider/ — это отдельный сервис (обфускация персональных данных), не часть пайплайна сверки договоров. Он уже работает на Flask в site/services/drhider.py и site/routes/drhider_bp.py. В план миграции сверки он не входит — это другой сервис на том же хосте. Но стоит упомянуть это явно: «drhider не трогаем, он уже на Flask».
8. repository.py — не объяснено как расширить
Моё мнение: ⚠️ Справедливо, но слишком рано. Protocol-паттерн в repository.py уже есть, PgRepository частично реализован. Полное внедрение DI через репозиторий во все services — это Фаза 2, после того как базовая миграция заработает. Сейчас services используют from site.db import documents напрямую, и это ОК для первого шага. Переписывать всё на DI сразу = риск сломать работающий код.
План: после миграции — отдельная задача «внедрить Repository DI во все services».
9. Graceful shutdown — in-flight classify потоки
Моё мнение: ✅ Справедливо. При docker stop daemon-потоки убиваются, документы застревают в classify_status='processing'.
Решение (уже в коде ВМ!): db/documents.reset_classify_status(batch_id) сбрасывает processing → pending. Добавить вызов в app.py при старте:
# При старте: сбросить все застрявшие processing → pending
from site.db.connection import execute
execute("UPDATE documents SET classify_status='pending', error_message=NULL WHERE classify_status='processing'")
10. Тесты — только curl, нет pytest
Моё мнение: ✅ Справедливо. В репо уже есть tests/ с conftest.py. Нужен шаг «адаптировать тесты под app.test_client()». Но это не блокер для миграции — тесты можно дописать после.
Моя итоговая оценка
| Категория | Пунктов | Критичность |
|---|---|---|
| Критичные (надо исправить ДО миграции) | 2 | site/ → app/, auth на cleanup |
| Важные (надо добавить в план) | 3 | lock→in-memory, heartbeat, явное удаление proxy |
| Улучшения (можно после миграции) | 5 | prompts/pages детали, drhider, repository DI, graceful, тесты |
Что делаем ДО начала миграции:
- Переименовать
site/→app/— везде в плане, во всех импортах - Добавить auth на
/api/cleanup—X-Api-Keyилиrequire_local
Что добавляем в план:
- in-memory lock для classify вместо файлового
- SSE heartbeat раз в 15 сек
- Упомянуть явно: старые proxy-роуты удалить, drhider не трогать
- Graceful shutdown: сброс
processing → pendingпри старте - prompts_bp / pages_bp — один абзац что там
- repository DI — отложить на пост-миграцию
Что НЕ блокер:
drhider— уже на Flask, не часть миграции- Repository DI — Фаза 2
- pytest — после миграции
- prompts/pages — тривиальны
Вердикт
План рабочий. Соннет подтвердил архитектурные решения (blueprints, разделение db/services/routes, генератор для SSE, DI). Критичные замечания — реальные и требуют правки плана. Остальное — улучшения надёжности, которые можно добавить в план сейчас или отложить.
Готовность к миграции: 90%. После правки 2 критичных пунктов → можно начинать.