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

133 lines
9.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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` при старте:
```python
# При старте: сбросить все застрявшие 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/cleanup`**`X-Api-Key` или `require_local`
### Что добавляем в план:
3. **in-memory lock** для classify вместо файлового
4. **SSE heartbeat** раз в 15 сек
5. **Упомянуть явно:** старые proxy-роуты удалить, drhider не трогать
6. **Graceful shutdown:** сброс `processing → pending` при старте
7. **prompts_bp / pages_bp** — один абзац что там
8. **repository DI** — отложить на пост-миграцию
### Что НЕ блокер:
- `drhider` — уже на Flask, не часть миграции
- Repository DI — Фаза 2
- pytest — после миграции
- prompts/pages — тривиальны
---
## Вердикт
План **рабочий**. Соннет подтвердил архитектурные решения (blueprints, разделение db/services/routes, генератор для SSE, DI). Критичные замечания — реальные и требуют правки плана. Остальное — улучшения надёжности, которые можно добавить в план сейчас или отложить.
**Готовность к миграции:** 90%. После правки 2 критичных пунктов → можно начинать.