Skip to content

Latest commit

 

History

History
75 lines (58 loc) · 17 KB

File metadata and controls

75 lines (58 loc) · 17 KB

РЕЕСТР КОМПРОМИССОВ (Tech Debt Register)

Каждый осознанный компромисс из дорожной карты «прототип → продукт» фиксируется здесь: что упростили → почему → влияние → как чинить после MVP. Перед любым расширением просматривайте этот файл: закрытые пункты помечайте ✅ и переносите в CHANGELOG.

Формат: ID | Блок | Компромисс | Почему | Влияние | План исправления


Блок A — надёжность

ID Компромисс Почему Влияние План после MVP
A-C1 Circuit breaker и token bucket живут в памяти процесса Нет Redis-зависимости на старте; один asyncio-loop Multi-worker деплой за балансировщиком видит разные стейты breaker'ов; квота делится неточно Перенести счётчики в Redis (точка замены — core.llm_gateway.get_breaker/get_bucket); либо sticky routing
A-C2 SQLite вместо Postgres Нулевая инфраструктура для MVP Ограничение по конкурентной записи и горизонтальному масштабу SQLAlchemy async + Alembic + Postgres (docker-compose уже имеет профиль postgres)
A-C3 /health?deep=true пингует провайдеров только вручную Дешёвые k8s-пробы не должны генерить трафик к внешним API Деградация LLM не видна в базовой пробе Синтетический мониторинг снаружи (self-check по расписанию) + алерт
A-C4 ✅ (2026-08-29) SLO-таблица в README без фактических цифр Нужен прогон locust на целевом железе Цели заявлены, но не подтверждены измерениями Закрыто с честной оговоркой: README ранее вообще не содержал числовых SLO (только упоминание каталога в дереве проекта) — добавлена реальная измеренная таблица p50/p95/p99 (docs/LOAD_TEST_RESULTS.md), явно помеченная «локальный dev-ноутбук, не прод-железо». Добавлен MockProvider (PROVIDER=mock) — бесплатный, без сети, для прогона locust без платных вызовов LLM. Второй сценарий подтверждает circuit breaker/retry-queue под гарантированно падающим провайдером. Nightly CI (nightly-loadtest.yml) прогоняет happy-path сценарий на каждый релизный тег. Прогон на реальном прод-железе остаётся будущим шагом
A-C5 Retry-воркер внутри процесса FastAPI Без Celery/Redis для MVP При остановке приложения очередь ждёт следующего старта; нет горизонтального масштабирования воркеров Вынести в Celery/RQ worker при росте объёмов

Блок B — форензика

ID Компромисс Почему Влияние План после MVP
B-C1 Поиск похожих pHash — линейный скан Python-кандидатов SQLite не умеет popcount; объёмы MVP малы O(N) на запрос; при сотнях тысяч хэшей замедлится pgvector/Faiss по эмбеддингам (CLIP) — заодно закроет reverse search по смыслу
B-C2 ELA-калибровка эвристическая (RMS×6, порог 25) Нет размеченной выборки реальных карточек Возможны ложные флаги на «шумных» фото Калибровка на golden dataset из реальных фото; ROC-подбор порога
B-C3 Консенсус — ровно второй провайдер (не ансамбль N) Стоимость/латентность Спорные случаи разрешаются двумя мнениями, а не тремя+ Расширить asyncio.gather до N моделей; агрегация большинством
B-C4 Форензика модулирует confidence максимум ±15 Предсказуемость и аудит важнее агрессивной коррекции Ярлык вердикта всегда за LLM После набора статистики — пересмотр весов формулы final_score

Блок C — discovery

ID Компромисс Почему Влияние План после MVP
C-C1 Один tick-job планировщика, расписание в БД Проще и персистентно без SQLAlchemy job-store Менее гибко, чем per-watch jobs; джиттер до минуты APScheduler job-store в БД при необходимости секундной точности
C-C2 Эталонные фото watch'ей — base64 в SQLite Нет файлового/S3-хранилища в MVP Раздувает БД; лимит размера картинки Файловое хранилище / S3-совместимое (MinIO)
C-C3 Ozon/Yandex discovery — HTML link-extraction (только ссылки) JS-rendering полный парсинг дорог; названия/цены добираются при фетче карточки Метаданные листинга беднее, чем у WB Полноценные парсеры выдачи через браузерную ферму / официальные API партнёров
C-C4 ✅ (2026-08-29) Email-дайджест — заготовка (лог) Не выбран SMTP-провайдер Уведомления только в Telegram Закрыто: app/email_alerts.py (SMTP-агностично, через SMTP_* env, без привязки к конкретному провайдеру), HTML-шаблон app/templates/emails/digest.html.j2, отправка встроена в существующий тик discovery-планировщика (maybe_send_digest). Новое поле digest_email у brand watch; создание watch с digest_email при не настроенном SMTP — явная ошибка 400 (не тихое молчание)
C-C5 Сканирование выдачи может триггерить антибот Используются публичные интерфейсы площадок Возможны пропуски/блокировки IP Прокси-пул + ротация UA; мониторинг успешности сканов как метрика

Блок E — дашборд и экспорт

ID Компромисс Почему Влияние План после MVP
E-C1 Time-to-detection считается только для Discovery-находок Дата публикации карточки конкурентом неизвестна для ручных проверок Метрика TTD отражает только автоматический мониторинг Парсинг даты публикации из карточки/первого появления в выдаче (историческая серия сканов)
E-C2 PPTX-экспорт — компактная текстовая дека (без графиков-картинок) Рендер Chart.js в изображения требует headless-браузера Презентация без визуальных графиков Снимок canvas через JS → загрузка картинки → вставка в слайд, либо серверный рендер
E-C3 ✅ частично (2026-08-29) Chart.js с CDN на фронтенде Не bundled-ассет в монолитном index.html Дашборд не работает офлайн / при блокировке CDN Проверено: frontend/ (React, Recharts) не имеет ни одной CDN-зависимости (JS, шрифты) — уже bundled, уже под строгим default-src 'self' CSP в frontend/nginx.conf. Осталось открытым только для legacy/index.html (Chart.js + Google Fonts с CDN, до сих пор отдаётся FastAPI на /) — решение сознательно отложено: legacy/ заморожен, полноценное закрытие требует отдельного продуктового решения о его судьбе (переезд на React-фронтенд vs локальный бандл в статическом файле без сборки)
E-C4 ✅ (2026-08-29, disclaimer) «Защищённая выручка» — грубая оценка (fakes × avg price) Нет данных о конверсии и каннибализации спроса Цифра для отчётности, не для финансового учёта Disclaimer-часть закрыта: React-дашборд показывает реальный disclaimer бэкенда рядом с цифрой (был баг несовпадения полей protected_revenue/methodology vs фактических protected_revenue_estimate/disclaimer — из-за него цифра/пояснение вообще не отображались верно, исправлено), заголовок переименован в «Оценка защищённой выручки». PPTX-экспорт теперь тоже несёт disclaimer рядом с цифрой (раньше — голая цифра в булите). Legacy-дашборд уже был корректен. Методологическая часть (коэффициент конверсии, каннибализация, калибровка по возвратам) остаётся будущим шагом

Блок F — мульти-тенантность и биллинг

ID Компромисс Почему Влияние План после MVP
F-C1 ✅ (2026-08-29) Изоляция тенантов — фильтрация tenant_id в SQL-запросах, без Row-Level Security SQLite не поддерживает RLS; фильтры централизованы в db-функциях Один пропущенный WHERE = утечка между тенантами Закрыто частично: добавлен app-level defense-in-depth tenancy.ensure_owned() (второй, независимый от SQL, гейт для всех get-by-id роутов) + систематический регресс-сьют tests/test_tenant_isolation.py + чеклист в docs/ARCHITECTURE.md. Postgres RLS остаётся будущим шагом после миграции (A-C2) — SQL-уровень по-прежнему не имеет защиты на уровне СУБД
F-C2 Биллинг — нормализованный контракт вебхуков, а не нативные события Stripe/Yookassa Разные форматы событий провайдеров; для MVP достаточно активации/отмены Реальные payload'ы требуют адаптеров на стороне интеграции Тонкие адаптеры под каждый провайдер + обработка invoice/payment_intent
F-C3 Роли хранятся на ключе, а не на пользователе (user == ключ) Нет полноценной системы пользователей/сессий в MVP «Users» в лимитах тарифа = число активных ключей Таблица users + сессии/JWT; ключи становятся сервисными токенами
F-C4 Партнёрский rate limit в памяти процесса Единый механизм с token bucket блока A На multi-worker деплое лимит умножается на число воркеров Общий bucket в Redis вместе с F-C1
F-C5 ✅ (2026-08-29) Open mode (без API_SECRET_KEY) остаётся доступным Обратная совместимость фронтенда и локальной разработки В продакшене забыть задать секрет = незакрытый API Закрыто: startup эмитит logger.warning(...) при open mode; STRICT_AUTH=1 (app/core/config.py) обрывает старт (RuntimeError) без API_SECRET_KEY; docker-compose.yml/docs/DEPLOY.md рекомендуют STRICT_AUTH=1 для клиентских деплоев (публичное демо намеренно остаётся open mode)
F-C6 pHash-кэш вердиктов глобальный, а не per-tenant Экономия LLM-вызовов: одно и то же фото контрафакта на разных карточках не анализируется дважды Tenant B может получить мгновенный вердикт, найденный для tenant A (только факт классификации фото, без пользовательских данных) Флаг PHASH_CACHE_SCOPE=global|tenant в конфиге; по умолчанию оставить global

Блок D — evidence & workflow

ID Компромисс Почему Влияние План после MVP
D-C1 ✅ (2026-08-29) Скриншот карточки — best-effort: при недоступном браузере делается при генерации PDF, а не строго «на момент проверки» Полноценный браузер на каждую проверку дорог; MVP-баланс Скриншот может отражать состояние страницы на момент генерации пакета — ослабляет chain of custody для этого файла Закрыто: screenshot_queue + screenshot_retry_worker.py — захват ставится в очередь немедленно при анализе (requested_at = момент анализа), с backoff-ретраями. PDF-эндпоинт больше не делает live-захват «на лету»; вместо этого evidence_store.get_screenshot_status() честно репортит captured/captured_late/pending/unavailable, PDF показывает «Дата анализа» и «Дата захвата скриншота» отдельными полями
D-C2 Артефакты доказательств на локальной ФС (EVIDENCE_DIR/) Нет S3 в MVP Не переживают пересоздание контейнера; нет репликации S3/MinIO + версионирование объектов
D-C3 PDF на reportlab с базовой вёрсткой Быстро, без headless-Chromium печати HTML Ограниченная типографика; нет подписи/печати WeasyPrint из HTML-шаблона + электронная подпись (КЭП) при юридических требованиях
D-C4 SLA-часы зашиты в константу DEFAULT_SLA_HOURS Конфигурируемость не была критична для первого workflow Смена SLA требует правки кода Перенести в таблицу/настройки tenant'а (естественно ляжет в Block F)
D-C5 Один кейс = одна проверка (нет объединения карточек одного продавца в групповое дело) Проще аудит и переходы Продавец с 20 карточками = 20 кейсов Группировка по seller_id + parent-case

Как работать с реестром

  1. Любое решение «сделаем проще сейчас» попадает сюда в момент принятия, а не потом.
  2. Перед началом нового блока — просмотреть открытые пункты затрагиваемых подсистем.
  3. После стабилизации MVP пункты закрываются по приоритету влияния; факт закрытия — в CHANGELOG.