# План работ RF4 Spotter Этот файл — рабочий источник правды по развитию проекта. После завершения задачи её чекбокс меняется с `[ ]` на `[x]`, рядом добавляется ссылка на коммит или короткое подтверждение проверки. Новые задачи добавляются в соответствующий этап, а не хранятся только в переписке. Последняя сверка плана со спецификацией, кодом и UI/UX-аудитом: 7 сентября 2026 года. Обозначения: - `[x]` — выполнено и проверено; - `[ ]` — ещё не выполнено; - пункты выполняются сверху вниз, если явно не зафиксирована другая зависимость. ## Текущее состояние - [x] Этап 0: исследован официальный источник, добавлены парсер, фикстуры и `docs/data-sources.md` (`a6f91a1`). - [x] Этап 1: создан Docker-каркас Astro + FastAPI + PostgreSQL, миграции, seed, публичный API и базовый E2E (`d3a4524`). - [x] Основная часть этапа 2: адаптер официальных рекордов, нормализация, дедупликация, журнал импорта и страница рекордов (`d3a4524`). - [x] Основная часть этапа 3: форма пользовательского улова, модерация, rate limit, MinIO и безопасная обработка скриншотов (`6d536d0`, `c524272`). - [x] Визуальный референс перенесён в Astro без Next.js, Vinext и React (`c524272`). ## Этап 2 — завершить официальный импорт - [x] Добавить административный endpoint ручного запуска импорта `POST /api/v1/admin/imports/official-records` (проверено API-тестом). - [x] Привести журнал импорта к административному контракту `GET /api/v1/admin/imports` с авторизацией, пагинацией и стабильной сортировкой (проверено API-тестом). - [x] Добавить HTTP-кэширование источника (`ETag`/`Last-Modified`, если источник их отдаёт) и сохранить диагностические метаданные ответа (миграция `0005`, тест условного запроса и `304`). - [x] Добавить планировщик импорта с безопасной частотой по умолчанию один раз в 60 минут; отдельный opt-in контейнер/процесс (профиль `scheduler`, обычным запуском не активируется). - [x] Защитить официальный импорт PostgreSQL session advisory lock: одинаковая source/region/category не запускается параллельно, admin получает `409`, scheduler безопасно пропускает цикл; проверено двумя независимыми PostgreSQL-соединениями. - [x] Проверить актуальные `robots.txt` и условия использования перед включением расписания; результат записать в `docs/data-sources.md` (`robots.txt` вернул `404`; автоматический профиль оставлен выключенным до явного разрешения). - [x] Добавить интеграционные тесты: повторный импорт не создаёт дубликаты, сбой источника не удаляет данные, изменение DOM завершается понятной ошибкой. Критерий готовности: официальный импорт запускается вручную и по расписанию, наблюдаем, идемпотентен и безопасно переживает недоступность источника. ## Этап 3 — завершить пользовательские уловы - [x] Добавить административный веб-интерфейс очереди модерации поверх существующего API (`/admin/moderation`, токен только в памяти страницы). - [x] Показать скриншот, данные улова и причину решения; реализовать действия «одобрить» и «отклонить» (проверено в браузере на desktop и 390 px). - [x] Добавить удаление пользовательского сообщения администратором с аудитом действия (обезличивание записи, удаление объекта MinIO, миграция `0006`). - [x] Заменить in-memory rate limit на общее хранилище, пригодное для нескольких API-процессов и перезапусков (PostgreSQL, HMAC-отпечаток без хранения исходного IP, миграция `0007`). - [x] Валидировать одновременно содержимое, MIME, расширение и лимит изображения; добавить тесты каждого отказа. - [x] Добавить сквозной тест: отправка → pending → модерация → появление одобренного улова в публичной статистике (Compose/Playwright: `2 passed`). - [x] Добавить понятные состояния успеха и ошибок загрузки в форму, включая отдельную ошибку скриншота без потери уже созданной заявки (повторная загрузка по ID сохранённой заявки). Критерий готовности: полный пользовательский сценарий проходит через браузер, а модератору не нужен ручной вызов API. ## Этап 4 — индекс клёва - [x] Сверить текущую формулу активности и уверенности с разделом 11 спецификации и зафиксировать формулу в `docs/activity-index.md`. - [x] Покрыть unit-тестами затухание по свежести, доверие к официальным и пользовательским источникам, повторные сообщения одного игрока и вклад разных игроков. - [x] Не учитывать pending/rejected/удалённые записи и доказать это тестами. - [x] Добавить детерминированные агрегаты для окон 6, 12, 24 и 72 часа. - [x] На карточке и странице точки показывать человекочитаемое объяснение оценки и объём данных, на котором она основана (unit-тест объяснения и Astro build). - [x] Реализовать состояния «данных мало», «данных нет», «источник недоступен» и ошибки валидации фильтров (Astro build; браузерная проверка войдёт в общий прогон фильтров). - [x] Проверить фильтры главной страницы сквозным тестом на desktop и mobile (Playwright, 1280 px и 390 px). Критерий готовности: оценка объяснима, воспроизводима тестами и никогда не маскирует недостаток или устаревание данных. ## Подготовка MVP к пилоту - [x] Добавить health/readiness-проверки PostgreSQL, MinIO, API и импорта; отразить их в Compose (`/health` без зависимостей, `/ready` с компонентами и режимом обязательного импорта). - [x] Добавить host-side production monitor контейнеров, `/ready`, диска, свежести/целостности backup и срока TLS; подготовлены systemd timer и runbook, реальный канал уведомлений подключается на сервере. - [x] Автоматизировать ежедневную цепочку backup → dry-run → retention с блокировкой параллельного запуска; ограничить Docker JSON-логи пятью файлами по 10 МБ на сервис. - [x] Добавить структурированные JSON-логи без пользовательских секретов и персональных технических данных (whitelist полей, redaction, request ID; Uvicorn access-log отключён). - [x] Добавить Gitea Actions CI: backend tests, Astro check/build, E2E и применение всех миграций на чистой PostgreSQL; сохранять логи Compose и Playwright-артефакты при падении (`.gitea/workflows/ci.yml`). - [x] Добавить отдельный тест полного bootstrap: пустые production volumes → миграции `0011` → seed без демо-уловов → readiness → браузерная отправка и проверка moderation API (`deploy/test-production-bootstrap.sh`, 7 сентября 2026). - [x] Сделать seed устойчивым к частично заполненной БД: справочники досеиваются независимо, демо-уловы идемпотентны и принудительно отключены в production; повторный/частичный запуск покрыт конфигурационными и интеграционными проверками. - [x] Проверить списочные API по требованию раздела 12: все выдачи имеют ограниченные `limit`/`offset`, детерминированный tie-breaker и типизированные фильтры; фильтр категории рекордов перенесён до пагинации. - [x] Добавить составные индексы PostgreSQL для activity, модерации, официальных рекордов, staging, импорта, аудита и очистки rate limit (миграция `0011`); бюджет и процедура проверки планов зафиксированы в `docs/query-performance.md`. - [x] Включить все пять разрешённых community-источников и автоматическую публикацию полных наблюдений по ранее подтверждённым алиасам без межисточникового склеивания (миграция `0012`, 7 сентября 2026). - [x] Добавить обязательную публичную атрибуцию записей и агрегатов, а также отдельную ленту неполных community-наблюдений с явным перечнем отсутствующих полей без влияния на индекс активности (7 сентября 2026). - [x] Провести security-проверку admin-аутентификации, CORS, headers, загрузок, контейнерных пользователей и секретов: двойная защита admin web/API, constant-time token, no-store, non-root API/web и отдельные MinIO root/app credentials; остаточные ограничения записаны в `docs/security-review.md`. - [x] Проверить авторизацию повторной загрузки скриншота: используется отдельный одноразовый случайный токен, в БД хранится только SHA-256, UUID заявки недостаточно. - [x] Определить сроки хранения ников, исходных payload, staging-наблюдений, moderation events и submission attempts; добавлены настраиваемая dry-run-first очистка, тест и `docs/data-retention.md`. - [x] Добавить резервное копирование и документированное восстановление PostgreSQL и MinIO: консистентные `pg_dump` и MinIO API mirror, контрольные суммы, runbook и успешный изолированный drill с намеренным удалением данных (6 сентября 2026). - [x] Проверить доступность публичного интерфейса: focus states, skip-link, контраст, подписи полей и axe на главной/рекордах/форме без critical/serious нарушений. - [x] Провести Lighthouse-проверку главной: accessibility 100, performance 75, FCP 1,2 с, TBT 0 мс; LCP 9,3 с оставлен наблюдаемым бюджетом оптимизации после размещения CDN. - [x] Провести UI/UX-аудит desktop/mobile и сформировать приоритетный план (`docs/UI_UX_AUDIT.md`). - [x] Исправить единую шкалу активности и русские числительные во всех публичных представлениях (пакет A UI/UX-аудита; unit-тест границ и E2E согласованности). - [x] Дополнить визуальный язык авторскими рыболовными SVG-иконками и шкалой активности в форме поплавка; анимации учитывают `prefers-reduced-motion`. - [x] Сократить мобильный путь до результатов и улучшить фильтры: компактный hero, основные фильтры перед глазами, раскрываемые период/сортировка, строка условий и сброс (пакет B UI/UX-аудита). - [x] Упростить форму улова, добавить помощь форматов, валидацию и сохранение значений при ошибке (пакет C UI/UX-аудита). - [x] Заменить slug-фильтры справочниками, добавить сброс и самодостаточные мобильные строки рекордов (пакет D UI/UX-аудита; detail уже адаптивен). - [x] Завершить accessibility/admin safety пакет с воспроизводимыми axe/Lighthouse-командами (пакет E). - [x] Добавить smoke-проверку административной очереди внешних источников на desktop/mobile: API-фикстура, неполная запись, disabled publish, ноль mutation-запросов и отсутствие overflow. - [x] Обновить README: актуальный статус, архитектура, группы переменных окружения, импорт, модерация, backup/restore, эксплуатация логов и известные ограничения. - [x] Выбрать лицензию кода `AGPL-3.0-only` и оформить отдельную политику использования, минимизации, хранения и удаления данных (`LICENSE`, `docs/data-policy.md`). - [ ] Завершить production-профиль для `rf4spotter.ru`: приложение и `deploy/preflight.sh` готовы; внешняя проверка 7 сентября не установила соединение с обоими доменами, требуются IP целевого сервера, DNS, запуск и канал уведомлений (`docs/deployment-status.md`). - [ ] Заменить демонстрационные секреты и определить целевое размещение перед внешней публикацией. ## Источники данных и согласование - [x] Провести аудит подключённых источников, потенциальных поставщиков и всех существующих парсеров; результат записан в `docs/data-source-audit.md`. - [x] Проверить оба официальных HTML-парсера на актуальной странице и добавить общий контрактный тест эквивалентности. - [x] Добавить `data_source` и алиасы рыб/водоёмов до подключения второго автоматического источника (миграции `0008`–`0009`; алиасы приманок уже нормализуются в `bait`). - [x] Вынести общий официальный DOM-парсер, устранив дублирование исследовательской и продуктивной реализации (`rf4_research/official_parser.py`). - [x] Добавить отдельную фикстуру и безопасный ручной импорт недельных официальных рекордов одной категории. - [x] Получено подтверждение владельца проекта о разрешениях RF4DB и RF4-STAT; добавлены пилотные HTML-парсеры и отчёт `docs/community-source-pilot.md`. - [x] Добавлены общий nullable-контракт, парсер detail-страницы RF4DB и ограниченный read-only CLI для RF4DB/RF4-STAT. - [x] Исследовать дополнительные публичные источники: добавлены read-only detail-парсеры RF4MAP и RF4 Posts, живые контрольные прогоны и тест разделения пространств ID; rf4pro/Farm.Trof отклонены для текущего пилота. - [x] Получить разрешение RF4MAP/RF4 Posts и зафиксировать интервал не менее 30 минут, хранение только URL изображений и семантику агрегированной точки; источники добавлены в выключенный staging, CLI блокирует ранний повтор. - [x] Зафиксировать проектное подтверждение разрешений, текущую атрибуцию и консервативные пилотные лимиты в `docs/data-permissions.md`. - [ ] Приложить или сослаться на первичный документ разрешения и зафиксировать точные продуктивные лимиты, срок хранения, удаление и обязательную атрибуцию до включения scheduler RF4DB/RF4-STAT. - [x] Добавить staging-модель внешних наблюдений и идемпотентный импорт RF4DB/RF4-STAT без автоматического влияния на индекс (миграция `0008`, сквозной контрактный тест). - [x] Добавить административную очередь сопоставления staging-записей с каноническими рыбами/водоёмами и явную публикацию в `catch_report` (`/admin/external-sources`, миграция `0009`; неполные записи публиковать запрещено). - [ ] Найти разрешённый способ получать полные наблюдения с рыбой, водоёмом, координатами и весом из одного источника либо через подтверждённый общий ID; текущие 139 записей неполны и не публикуются. - [x] Добавить защищённые подсказки алиасов по точным подтверждённым сопоставлениям без применения и запретить конфликтующую перезапись алиаса (7 сентября 2026). - [ ] Добавить управляемый повторный импорт staging с отчётом `created/updated/rejected`, лимитами запросов и наблюдаемым отказом при изменении DOM; регулярный запуск оставить выключенным до фиксации условий. - [ ] Определить процедуру повторной проверки опубликованного внешнего улова при изменении или удалении записи у источника. - [ ] Согласовать один добровольный канал сообщества и правила происхождения, модерации и удаления сообщений. ## Pre-deploy: SEO, визуальный язык и техническая полировка ### SEO и структура публичного сайта - [x] Добавить единый SEO-контракт страниц: уникальные `title`/`description`, canonical URL, Open Graph и Twitter Card (7 сентября 2026). - [x] Подготовить и подключить фирменное OG-изображение 1200×630; локально проверены размер и метатеги, внешняя проверка превью выполняется после DNS/TLS (7 сентября 2026). - [x] Добавить production-aware `robots.txt`, исключить `/admin`, `/api`, `/health` и `/ready` из индексации (7 сентября 2026). - [x] Добавить динамический `sitemap.xml` для существующих статических страниц и публичных точек; страницы рыб и водоёмов включить после реализации их маршрутов (7 сентября 2026). - [x] Добавить JSON-LD: `WebSite`, `Dataset` и `BreadcrumbList` на соответствующих страницах (7 сентября 2026). - [x] Сделать собственную полезную страницу 404 с возвратом к свежим точкам (7 сентября 2026). - [x] Явно отдавать `noindex` на административных страницах и состояниях ошибок (7 сентября 2026). - [x] Добавить постоянные URL точек `/spots/{waterbody}-{x}x{y}`, перевести карточки и sitemap, а старые UUID-ссылки сохранить через 301 redirect (7 сентября 2026). - [x] Создать индексируемые каталоги и страницы `/fish/{slug}`, `/waterbodies/{slug}` и сочетания водоём + рыба с уникальными метаданными и включением в sitemap (7 сентября 2026). ### Графические элементы и объяснение данных - [x] Не показывать дублирующий sidebar лидера при единственной найденной точке (7 сентября 2026). - [x] Показывать у каждого улова на detail-странице источник, относительную свежесть и точную дату в подсказке (7 сентября 2026). - [x] Подготовить фирменный знак «крючок + поплавок + координатная сетка» и согласованный набор favicon/app icons (7 сентября 2026). - [x] Добавить честный координатный радар без имитации отсутствующей карты на страницу точки (7 сентября 2026). - [x] Добавить временную шкалу активности 72 часа с 12-часовым шагом в виде лески с поплавками (7 сентября 2026). - [x] Добавить лёгкие детерминированные SVG-силуэты видов рыб в карточки результатов и каталог (7 сентября 2026). - [x] Собрать единый визуальный «паспорт данных»: источник, свежесть, полнота и доверие; применить к карточкам активности (7 сентября 2026). - [x] Добавить публичную легенду цветов источников и статусов качества на странице `/status` (7 сентября 2026). - [ ] Добавить skeleton-состояния для динамических публичных блоков. - [x] Добавить ненавязчивый глобальный баннер открытой альфы со ссылками на статус, правила и отправку улова (7 сентября 2026). - [ ] Подключить к баннеру публичный канал обратной связи после получения его адреса; не раскрывать частный Gitea. - [x] Дополнить пустые состояния собственной CSS-иллюстрацией поплавка и ряби с поддержкой `prefers-reduced-motion` (7 сентября 2026). ### Производительность, данные и эксплуатация - [x] Добавить scheduler всех пяти разрешённых community-парсеров с устойчивым cooldown ≥30 минут, PostgreSQL lock, журналом запусков и opt-in локальным профилем; backoff после повторных ошибок остаётся отдельным улучшением (7 сентября 2026). - [x] Добавить экспоненциальный backoff community scheduler после повторных ошибок от 30 минут до 24 часов со сбросом после успеха (7 сентября 2026). - [x] Показывать на `/status` безопасное состояние источников: актуален, устарел, временно ограничен, изменился DOM, ожидает запуска или выключен (7 сентября 2026). - [x] Добавить 20-секундный серверный кэш агрегата активности с явной инвалидацией после публикации, модерации и удаления (7 сентября 2026). - [x] Настроить долгий immutable cache для хешированных assets и разумный cache для изображений/favicon (7 сентября 2026). - [x] Добавить серверное «Показать ещё» для публичной ленты полевых сигналов с сохранением фильтров и пределом 48 записей (7 сентября 2026). - [x] Объединять одинаковые полевые сигналы в сюжеты без потери уникальных ссылок provenance и показывать число совпадений (7 сентября 2026). - [x] Добавить защищённый JSON-экспорт диагностики со сборкой и агрегированными счётчиками без персональных данных, URL и ошибок источников (7 сентября 2026). - [x] Контролировать рост PostgreSQL и MinIO host-monitor'ом с настраиваемыми порогами и runbook реакции (7 сентября 2026). - [ ] Добавить фоновую проверку битых исходных ссылок с соблюдением лимитов источников. - [x] Показывать версию и commit SHA в readiness и защищённой административной диагностике (7 сентября 2026). - [x] Добавить публичную `/status` без внутренних адресов, секретов и текстов ошибок (7 сентября 2026). - [ ] После появления сервера подключить privacy-friendly аналитику без cookies либо собственные агрегированные счётчики. - [ ] Зафиксировать нагрузочный бюджет и проверить p95 публичных API на целевом сервере. ### Обязательные внешние условия открытия альфы - [ ] Заменить все демонстрационные production-секреты. - [ ] Настроить DNS `rf4spotter.ru` и `files.rf4spotter.ru`, корректный TLS и закрыть внутренние порты. - [ ] Создать контакты `privacy`/`abuse` и определить срок реакции. - [ ] Приложить первичные документы разрешений и точные продуктивные лимиты источников. - [ ] Настроить внешнее хранение резервных копий и проверить восстановление с сервера. - [ ] Подключить реальный канал уведомлений мониторинга. - [ ] Наполнить альфу разрешённым набором реальных данных и выполнить финальный `preflight`. ## Этап 5 — пилот - [x] Опубликовать страницы `/rules` и `/privacy`, добавить ссылки в footer и обязательное несохраняемое согласие перед отправкой улова. - [ ] Создать и проверить ящики `privacy@rf4spotter.ru` и `abuse@rf4spotter.ru`, определить срок реакции на обращения. - [x] Проверить локально публичные abuse-сценарии: шестая заявка блокируется, файл > 8 МБ отклоняется до декодирования, повтор screenshot запрещён, admin queue ограничена 100 строками; нагрузочный прогон остаётся серверным шагом. - [x] Зафиксировать сценарий приёмки открытой альфы, измеримые пороги и стоп-критерии (`docs/open-alpha-acceptance.md`). - [x] Зафиксировать охват открытой альфы: без продуктового allowlist, доступны все корректно загруженные категории, водоёмы и виды рыб из разрешённых источников; ограничения качества и модерации сохраняются. - [ ] Наполнить базу небольшим разрешённым набором реальных данных. - [ ] Провести тестирование с несколькими игроками по подготовленному сценарию. - [ ] Собрать обратную связь по полезности точек, понятности уверенности, форме улова и мобильному интерфейсу. - [ ] Исправить блокирующие проблемы пилота и повторить проверку критериев MVP из раздела 16 спецификации. - [ ] Только после пилота принять решение по OCR, Telegram, профилям, уведомлениям и импорту сообществ. ## Ближайший рабочий пакет Технический production-контур, health/readiness, backup/restore и безопасные логи готовы. Ближайшие пункты выполняются небольшими проверяемыми пакетами: 1. базовый SEO-контракт, `robots.txt`, sitemap, structured data и 404; 2. индексируемые страницы рыб и водоёмов; 3. community scheduler и наблюдаемость источников; 4. графический паспорт данных, OG-изображение и легенда; 5. внешние контакты, мониторинг, нагрузка, DNS/TLS и production-профиль на целевом сервере. После каждого пункта необходимо: 1. запустить затронутые unit/integration-тесты; 2. выполнить `docker compose up --build -d` и проверить health; 3. для UI-изменений проверить desktop и ширину 390 px; 4. обновить чекбокс в этом файле; 5. зафиксировать результат отдельным небольшим коммитом.