Files
contracts/History/sonnet-review-migration-plan-2026-07-14.md
T

9.5 KiB
Raw Blame History

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, тесты

Что делаем ДО начала миграции:

  1. Переименовать site/app/ — везде в плане, во всех импортах
  2. Добавить auth на /api/cleanupX-Api-Key или require_local

Что добавляем в план:

  1. in-memory lock для classify вместо файлового
  2. SSE heartbeat раз в 15 сек
  3. Упомянуть явно: старые proxy-роуты удалить, drhider не трогать
  4. Graceful shutdown: сброс processing → pending при старте
  5. prompts_bp / pages_bp — один абзац что там
  6. repository DI — отложить на пост-миграцию

Что НЕ блокер:

  • drhider — уже на Flask, не часть миграции
  • Repository DI — Фаза 2
  • pytest — после миграции
  • prompts/pages — тривиальны

Вердикт

План рабочий. Соннет подтвердил архитектурные решения (blueprints, разделение db/services/routes, генератор для SSE, DI). Критичные замечания — реальные и требуют правки плана. Остальное — улучшения надёжности, которые можно добавить в план сейчас или отложить.

Готовность к миграции: 90%. После правки 2 критичных пунктов → можно начинать.