Files
Deal/.superpowers/sdd/channel-discovery/final-review-report.md
T
Rustam Khalimov 9e07568ddd Инициализировать репозиторий «Дейл»
Первый коммит: модульный монолит ядра (.NET 10) и gRPC-сервисы
ai/ml/telegram, фронтенд Vue 3/Vite/Tailwind, документация (ТЗ,
инструкция пользователя, техдокументация, код-стайл), бэклог,
скрипты развёртывания и архив прототипа LeadRadar.
2026-09-11 02:50:17 +03:00

42 lines
5.6 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.
# Discovery — scoped-ревью фикс-волны: вердикт и доработки
**Вердикт ревьюера: Ready — With fixes** (все 8 заявленных фиксов I1–I4/M1–M5/m1–m6 подтверждены код-ревью; 2 Important + 4 Minor).
Смоук-прогон новых веток после правок — зелёный (A–E), образ пересобран, контейнер поднят, API 200.
## Findings ревьюера и что сделано
### Important
1. **Search-flood retry storm** (`discovery_worker.py`) — при `FloodWaitError` ключ не двигался, а тик каждые 5 с снова дёргал `discovery_search` весь день (флуд > 60 с Telethon не гасит сам), спамя лог flood и блокируя eval/join всех задач.
**Исправлено**: `tick()` теперь при `ban_guard.flood_today()` возвращает none (полный стоп discovery-сетевых действий до конца суток — согласуется с «стоп до конца суток» из note_flood); generic-ошибка ключа — после 3 попыток подряд ключ пропускается (`advance_search` + лог «ключ пропущен»), счётчик ошибок сбрасывается при успешном поиске.
2. **Re-check после wait_join_delay без BanGuard** (`_join_step`) — за паузу 50–70 с пользователь мог нажать стоп-кран / случиться флуд, а pending-join всё равно выполнялся.
**Исправлено**: в условие после паузы добавлено `not ban_guard.can_auto_join()` (покрывает стоп-кран, flood дня и суточный лимит).
### Minor
3. **Single-key PATCH ломает инвариант min ≤ max пауз**`PATCH {discJoinDelayMax: 5}` при сохранённом min=600 давал инверсию → `random.uniform` падал на каждом join-тике.
**Исправлено** в двух местах: `settings_routes.patch_settings` клампит одиночный конец интервала относительно сохранённого другого; `ban_guard.wait_join_delay` защитно меняет концы местами (и выходит без паузы, если обе настройки 0).
4. **UPDATE join_failures / delete на 3-й неудаче без ре-валидации status='review'** — узкая гонка: человека отклонил кандидата между re-read и падением join.
**Исправлено**: перед инкрементом счётчика статус перечитывается (`row_now`); UPDATE идёт с `AND status='review'`; при выходе из review воркер выходит молча, не трогая запись.
5. **Ручной join в API ждал inline-backfill ~1530 с** (`backfill_dialog` спит 1.5–3 с/сообщение) — кнопка «Вступить» висела, прокси с коротким таймаутом показал бы ложную ошибку.
**Исправлено**: `POST /candidates/{id}/join` запускает backfill в фоне через `_spawn(_backfill_quiet(...))` (паттерн set_monitor_all), ответ API быстрый, ошибки backfill не роняют запрос.
6. **Skip-лог на каждого человека в поиске** — известный minor из отчёта фиксера (concern #2). Оставлен как есть: по одному логу на источник информативно для истории задачи; при желании можно агрегировать («пропущено личных чатов: N») — не критично.
## Smoke новых веток (временный скрипт в data/, удалён после прогона)
```
A_FLOOD_GATE_OK # flood_today -> tick none, search не вызван
B_PAUSE_DURING_DELAY_OK # стоп-кран во сне -> join не выполнен, кандидат review
C_JOIN_FAILURES_REVIEW_GONE_OK # кандидат rejected во время падения join: счётчик 0, запись жива
D_INVERTED_DELAYS_OK # min>max: wait_join_delay не падает (swap)
E_SEARCH_KEY_SKIP_OK # 3 ошибки ключа -> ключ пропущен, searchDone, лог «ключ пропущен»
LR_REVIEW_FIX_SMOKE_OK
```
## Проверки
1. `python -m py_compile` изменённых файлов (discovery_worker.py, ban_guard.py, discovery_routes.py, settings_routes.py) — OK.
2. `docker compose build app` — Built (внутри образа повторный `vite build` — ok).
3. `docker compose up -d app` — контейнер Recreated/Started.
4. `GET /api/health` — 200 `{"ok":true,...}`; login admin — 200; `/api/settings` отдаёт disc-ключи и `discPaused: false`; `/api/discovery/tasks` и `/blacklist` — 200 `{"items":[]}`.
## Осталось
- Живой E2E с Telegram-аккаунтом (шаги в task-10-report.md §«Осталось для ручной проверки») — вместе с пользователем: поиск → кандидаты → вступить/отклонить → авто-вступление с паузами и расходом лимита. Реальные flood/форумы офлайн не воспроизводятся.