diff --git a/README.md b/README.md index 203e7f2..494670c 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,9 @@ Web Docker-образ устанавливает зависимости чере Идёт исправление аудита: актуальные изменения и ограничения перечислены в [AUDIT_FIXES.md](docs/AUDIT_FIXES.md). Production Compose включает community scheduler; страницы rules/privacy реализованы. Для запуска остаются сервер, DNS/TLS, секреты, внешний backup и контакты. Шкала 72 часов использует полную выборку по времени поступления; одинаковые поля разных источников больше не считаются доказательством одного события. Фоновая публикация обновляет кэш API в пределах TTL, не мгновенно. -Основные публичные страницы и состояния проверены на ширинах 320, 390, 768 и 1280 px. Горизонтального переполнения нет; узкие навигационные ленты прокручиваются без системной полосы, а длинный заголовок рекордов адаптирован для 320 px. +Полный аудит 8 сентября: [отчёт](docs/PROJECT_AUDIT_2026-09-08.md), [приоритетный план](docs/ROADMAP.md#ближайший-рабочий-пакет). Перед production нужно исправить Caddy-маршруты формы, конфликт Basic/Bearer admin-аутентификации и исходящую сеть community scheduler. Затем — клиентский rate limit, надёжность парсеров, согласованность фильтров/оценок и HTTP/SEO-контракт ошибок. + +На ширинах 320, 390, 768 и 1280 px ранее проверено отсутствие горизонтального переполнения основных страниц. Это не полная визуальная приёмка: аудит обнаружил неверную desktop-компоновку фильтров; наполненные карточки, длинные названия, клавиатура и zoom остаются отдельной задачей. Функциональный MVP и локальный production-контур готовятся к открытой альфе: официальный импорт, пользовательские заявки, модерация, объяснимый индекс, staging внешних источников, адаптивный Astro UI, миграции, резервное копирование, retention, мониторинг и security/accessibility-проверки реализованы. На всех страницах подключён компактный баннер открытой альфы со ссылками на статус, правила и отправку улова. В production Compose включён community scheduler; локально он запускается отдельным профилем. Публичный запуск блокируют покупка и настройка сервера, DNS/TLS, реальные секреты, внешний backup, канал уведомлений; публичный адрес обратной связи ещё не задан. @@ -239,7 +241,7 @@ Production-логи структурированы в JSON и не содерж ## Известные ограничения альфы - нет пользовательских аккаунтов, OCR, Telegram-бота и уведомлений о клёве; -- community-источники автоматически публикуют только полные наблюдения с подтверждёнными алиасами; новые соответствия требуют ручной проверки, неполные наблюдения не публикуются; +- community-источники автоматически включают в активность только полные наблюдения с подтверждёнными external-ID алиасами; fallback alias по имени ещё не используется автопубликацией. Неполные наблюдения видны в «Полевых сигналах», но не влияют на индекс; - offset pagination рассчитана на пилотные объёмы, не на бесконечную ленту; - прежний локальный Lighthouse показывал LCP 9,3 с; после него hero уменьшен с 1,6 МБ до 71 КБ и получил высокий приоритет загрузки, повторный production-замер выполняется после размещения; - один сервер остаётся точкой отказа, поэтому обязательны внешний backup и мониторинг; diff --git a/docs/AUDIT_FIXES.md b/docs/AUDIT_FIXES.md index e7141e1..14eed88 100644 --- a/docs/AUDIT_FIXES.md +++ b/docs/AUDIT_FIXES.md @@ -1,5 +1,7 @@ # Исправления аудита 7 сентября 2026 +Дополнено [полным аудитом 8 сентября](PROJECT_AUDIT_2026-09-08.md): выявлены production-блокеры T01–T03. Исторические отметки ниже не означают закрытие новых пограничных случаев; актуальная очередь находится в ROADMAP. + ## Исправлено в рабочей версии - [x] Очередь внешних наблюдений фильтруется в SQL до limit/offset; UI показывает по 50 записей с навигацией, опубликованные записи не вытесняют ожидающие проверки. @@ -19,7 +21,7 @@ ## Остаётся -- [x] Production CLI и scheduler используют общий PostgreSQL-журнал и блокировку по домену; попытка резервируется до HTTP, включая ошибки. Endpoint одного сайта ротируются по давности. Автономный research CLI также ограничивает домен и ошибки, но явно запрещён параллельно с production scheduler (8 сентября 2026). +- [ ] Довести общий cooldown до критериев D01–D03 нового аудита. В `486b4e9` добавлены доменная блокировка и резервирование до HTTP, но disabled-сосед блокирует ротацию, research CLI не атомарен и не разделяет PostgreSQL-state. - [ ] Межпроцессная инвалидация: сейчас фоновая публикация видна после TTL. - [x] Изменённая публикация снимается с активности и отправляется на ручное сопоставление. Подтверждение обновляет прежний CatchReport без дубликата; старый снимок сохраняется до подтверждения. Изменения видны в API после TTL кэша. - [ ] Обнаружение удалённых оригиналов и долговременная история всех редакций источника. @@ -28,7 +30,7 @@ - [x] Web Dockerfile использует npm ci; инструменты проверки/тестирования перенесены в devDependencies и удаляются перед копированием runtime. Обновлён lock-файл без сети. Сборка Docker-образа остаётся проверкой перед деплоем. - [x] Удалить pytest из Python production-зависимостей; локальная среда и CI используют отдельный requirements-dev.txt (8 сентября 2026). - [x] Hero переведён в WebP (1,6 МБ → 71 КБ), PNG-иконка переупакована (368 КБ → 69 КБ), изображения получили явные размеры и приоритет LCP. Pillow нужен в runtime: API декодирует и повторно кодирует пользовательские изображения для проверки формата, удаления EXIF и безопасного хранения. -- [x] Визуальная проверка 320/390/768/1280 px: главная, рекорды, форма, статус и 404 не имеют горизонтального переполнения. На 320 px исправлен длинный заголовок рекордов; мобильные навигация и фильтры получили чистую прокрутку со scroll-snap без системной полосы (8 сентября 2026). +- [ ] Завершить визуальную приёмку U01/U07 нового аудита. В `731eade` проверено отсутствие горизонтального переполнения и исправлен заголовок на 320 px, но desktop-фильтры и наполненные состояния требуют проверки/исправления. - [ ] Согласовать остальные формулировки README/roadmap с фактическим поведением. Существовавшие перед этим пакетом незакоммиченные изменения кэша и карточек сохранены. diff --git a/docs/PROJECT_AUDIT_2026-09-08.md b/docs/PROJECT_AUDIT_2026-09-08.md new file mode 100644 index 0000000..9ec4357 --- /dev/null +++ b/docs/PROJECT_AUDIT_2026-09-08.md @@ -0,0 +1,103 @@ +# Аудит RF4 Spotter — 8 сентября 2026 + +База проверки: commit `486b4e9`. Аудит охватывает production-конфигурацию, API, импорт и публикацию данных, Astro, пользовательские сценарии, визуальную идентичность и SEO. Это отчёт о текущем состоянии, а не подтверждение готовности к запуску. Исправления приложения в этот пакет не входят. + +## Вывод + +Стек соответствует продукту: Astro SSR + FastAPI + PostgreSQL + MinIO + Caddy. Перенос на Next.js/Vinext, добавление SPA-фреймворка, Redis или микросервисов сейчас не обоснованы. Есть полезная основа: миграции, модерация, provenance, проверка изображений, Docker, backup/restore, SEO-разметка и выразительный рыболовный визуальный язык. + +Открывать production пока рано. Подтверждены три блокирующих противоречия конфигурации: маршрутизация формы, несовместимые схемы admin-аутентификации и отсутствие внешней сети у community scheduler. Успешная локальная проверка напрямую через Astro/API эти дефекты не обнаруживает. Главный продуктовый риск — фактическое наполнение и достоверность времени/оценок, а не недостаток декоративных элементов. + +## Метод и границы + +- Прочитаны конфигурации Compose/Caddy/Docker/CI, ключевые обработчики API и Astro, код агрегации, scheduler, research CLI, staging/review/retention, seed, компоненты и стили, текущие планы и ограничения. +- Встроенный браузер: главная текущего локального стенда, desktop 1280×1000 и mobile 390×844; оценены композиция, меню и фильтры. Предыдущий прогон 320/390/768/1280 проверял прежде всего переполнение. Его нельзя считать полноценной визуальной приёмкой всех состояний. +- `astro check`: 40 файлов, 0 ошибок, 0 предупреждений. Целевые тесты CLI/scheduler/activity: 16 passed. Общий pytest был начат, но завершённый результат в этом прогоне не получен; прежние общие результаты не выдаются за новый полный прогон. +- Внешние парсеры не запускались, 30-минутный лимит не расходован. Актуальность HTML источников, CVE всех зависимостей, нагрузка, TLS/DNS, реальный backup на сервере и поисковая индексация повторно не проверялись. +- Проверены официальные документы Docker, Caddy и Google. Подтверждение по коду отделено ниже от дизайнерских предложений и будущих проверок. + +Приоритеты: **P0** — блокирует production; **P1** — нужно до открытой альфы; **P2** — улучшение качества/масштабирования; **P3** — после обратной связи пилота. + +## Техническая часть и эксплуатация + +| ID | Приоритет | Подтверждение и последствие | Критерий исправления | +|---|---|---|---| +| T01 | P0 | `deploy/Caddyfile`: `/api/*` уходит в FastAPI, но `/api/report` и `/api/report-screenshot` реализованы в Astro. Production-форма попадает в отсутствующий маршрут. | Через Caddy отправка и повтор загрузки достигают Astro и возвращают ожидаемый 303; `/api/v1/*` достигает FastAPI. | +| T02 | P0 | Caddy `basic_auth` на `/api/v1/admin/*` требует Basic в `Authorization`; admin JS посылает Bearer, `_admin` требует именно Bearer. Два слоя используют один несовместимый контракт. | Выбрать согласованную схему; доказать успешные чтение/модерацию через proxy и отказ неавторизованному клиенту. | +| T03 | P0 | `compose.production.yaml`: community-scheduler только в `backend`, сеть `internal: true`. Нет внешней сети для HTTPS источников. | Разрешён исходящий доступ scheduler; БД/MinIO остаются без внешних портов. Проверить локальным контролируемым HTTP-источником. | +| T04 | P1 | Astro POST не передаёт идентичность клиента, FastAPI rate limit использует `request.client.host`. Заявки через web объединяются под его адресом. Доверенные proxy явно не настроены. | Два клиента имеют независимый лимит; поддельный forwarded header его не обходит; проверка через всю цепочку Caddy → Astro → API. | +| T05 | P1 | В Astro `request.formData()` выполняется до try, POST fetch без timeout; Caddy не задаёт лимит тела. Лимит 8 МБ в Python применяется после приёма multipart. | Ограничить входной body до буферизации, обработать повреждённый multipart, задать время ожидания и понятные 413/429/ошибки формы. | +| T06 | P1 | Bootstrap запускает db/minio/api/web, без proxy и community-scheduler; CI проверяет локальный Compose. `monitor.sh` не включает community-scheduler, `/ready` знает лишь официальный импорт. | Один production-contract прогон через proxy, проверка scheduler heartbeat/последнего успеха и уведомление о зависании/остановке. | +| T07 | P1 | `GET /api/v1/imports` публично возвращает `ImportRunOut`, включая `error_summary` и `source_url`. | Публичный DTO содержит только необходимые безопасные статусы; детали ошибок доступны администратору. | +| T08 | P2 | Python фиксирует прямые зависимости, но не транзитивные; Docker base tags изменяемы. В CI нет web unit job (`test:unit`) и отдельного dependency audit. | Воспроизводимый Python lock/constraints, поддерживаемые runtime-версии и регулярная проверка зависимостей; подключить существующие unit-тесты. CVE здесь не утверждаются. | +| T09 | P2 | `activity_rows` загружает все уловы окна и агрегирует Python-списками до пагинации; на карточке точки также читается история. Public cache локален процессу. | Измерить память/p95 на представительном объёме; оптимизировать запросы по результату. Сохранить документированный TTL 20 с до необходимости общей инвалидации. | +| T10 | P2 | API/web стартуют с миграциями/seed; MinIO app policy `readwrite` шире одного bucket, общие admin credentials. | Оформить отдельный release/migration шаг, bucket policy и план персонального доступа при росте команды; не добавлять identity provider до потребности. | + +Семантика изоляции подтверждена [Docker networks](https://docs.docker.com/reference/compose-file/networks/#internal). Конфликт заголовков T02 следует из кода и [контракта Caddy Basic Auth](https://caddyserver.com/docs/caddyfile/directives/basic_auth). + +## Парсеры и достоверность данных + +| ID | Приоритет | Подтверждение и последствие | Критерий исправления | +|---|---|---|---| +| D01 | P1 | `oldest_site_source` включает выключенные адаптеры. Если самым старым RF4-STAT endpoint окажется disabled, включённый сосед будет постоянно пропускаться. | Ротация только enabled-кандидатов; тест выключенного соседа, включения обратно, ошибки и двух конкурирующих запусков. | +| D02 | P1 | Research CLI делает check/write раздельно без межпроцессного lock, повреждённый state трактует как пустой; старые source-key записи после перехода к доменам не учитываются. Автономный state не связан с PostgreSQL. | Атомарное резервирование с fail-closed, миграция состояния; production сбор через единый механизм, offline parsing отделён. Нельзя заявлять общую гарантию только на основании предупреждения README. | +| D03 | P1 | `fetch_html` допускает произвольный override URL, автоматические redirects, неограниченный `read()`. `fetch_site_key` различает `download.rf4db.com` и `rf4db.com`. | Единый реестр разрешённых площадок/хостов, проверка URL и redirects до HTTP, лимит ответа, корректные 429/Retry-After в рамках ≥30 минут. | +| D04 | P1 | RF4-STAT fishing выдаёт `waterbody_external_id=None`. Mapping сохраняет fallback alias по имени, но `_auto_publish` требует оба external ID. Ранее подтверждённое имя не включает автопубликацию следующей полной записи. | Использовать единый безопасный контракт алиасов для suggestion/manual/auto; не разрешать неподтверждённое fuzzy-сопоставление. | +| D05 | P1 | Scheduler RF4MAP/RF4 Posts опрашивает по одному фиксированному detail URL. Seed даёт только 2 рыбы/2 водоёма; на локальной главной также только они и 0 активных точек. «Все парсеры включены» не означает полный охват. | Реестр задач/очередь разрешённых URL и пополнение канонического каталога; видимые счётчики fetched/staged/mapped/published/age; приёмочный набор из каждого адаптера. | +| D06 | P1 | Score зависит от количества сообщений одного ника; текст главной «Один игрок не может искусственно поднять уверенность» неверен. Имена без аккаунтов не доказывают независимость. | Исправить обещание, определить ограничения вклада одного игрока и неизвестных авторов; воспроизводимый пример спама не изображает независимые подтверждения. | +| D07 | P1 | В activity окно/затухание по `reported_at`, подпись свежести по `caught_at or reported_at`; community publisher записывает `published_at` также в `caught_at`. В feed «Получено» — `last_seen_at`, который обновляется каждым опросом. | Разделить время улова, публикации, первого получения и последнего обнаружения; старый повторно найденный материал не выглядит новым уловом. | +| D08 | P1 | На `/spots/[id]` score берётся из первых 100 глобальных activity-строк и только по spot_id, хотя индекс рассчитан на spot+fish. Возможны ложное «данных нет» и оценка одной рыбы под видом всей точки. | Прямой запрос оценки точки с явным контекстом рыбы; тест >100 комбинаций и нескольких рыб на точке. | +| D09 | P2 | Staging и autopublish коммитятся отдельными операциями; ошибка поздней записи может оставить часть публикаций при failed journal. Изменение сравнивает весь payload; полная история редакций отсутствует. | Явная семантика partial success, идемпотентный retry, сравнение значимых полей и история редакций/удалений; отсутствие записи в очередном списке само по себе не означает удаление. | +| D10 | P2 | `source_status` анализирует последние 20 запусков, running может считаться healthy; backoff смешан для домена, успешный сосед может сбросить ошибки endpoint. | Раздельные domain cooldown и endpoint backoff, stalled/running статус, последний успех вне ограничения 20 строк. | + +Обнаруженная особенность sitemap **не является ошибкой пагинации**: API `public-spot-pages` выдаёт два пути на строку БД, поэтому `limit=500` и остановка при `<1000` путях согласованы. Нужен контрактный тест и явная документация, а не механическая замена на `<500`. + +## UI/UX + +| ID | Приоритет | Наблюдение | Критерий/решение | +|---|---|---|---| +| U01 | P1 | На screenshot 1280×1000 период и сортировка стоят вертикально; фильтры занимают около 196 px. Используется `details`/`display:contents`, результат не соответствует пятиколоночной задумке. | Устойчивая desktop-сетка и мобильное раскрытие, проверка реального layout браузеров; отсутствие overflow недостаточно. | +| U02 | P1 | Главная передаёт waterbody/fish/hours только в activity; feed получает лишь limit. После выбора водоёма снизу остаются чужие сигналы. | Фильтровать оба блока согласованно либо явно подписать независимый общий feed. URL и сброс сохраняют понятную область действия. | +| U03 | P1 | Главная показывает первые 20 activity, records — 50, каталоги — 100 activity; навигации по остальным нет. Счётчик `items.length` выглядит общим числом. | «Показано N из M», пагинация/следующая страница с сохранением фильтров; отдельно считать уникальные точки и комбинации точка+рыба. | +| U04 | P1 | Ошибки 422/429/5xx формы сводятся к «Проверьте поля»; нет ожидания/защиты от повторной отправки. Сохранение текста через sessionStorage не гарантировано при его недоступности; файл не восстанавливается. | Разные полезные сообщения, Retry-After для 429, блокировка повторного submit, честная подсказка о файле, server fallback. | +| U05 | P2 | В 390×844 результаты начинаются около y=700, mobile меню скрывает последний пункт; при 0 точек зелёный «пульс» всё ещё изображает живую активность. | Компактный первый экран, заметный доступ к скрытой навигации, отдельные empty/stale/error состояния, прямые действия «72 часа», «Сбросить», «Добавить улов». | +| U06 | P2 | Карточки многократно повторяют качество/доверие; «Не рассчитана», «сырые данные», «сюжеты» описывают реализацию. Текстовые badges часто 9–11 px. | Иерархия рыба → точка → приманка → возраст → источник; понятные статусы без выдуманных процентов, крупнее значимые подписи. | +| U07 | P2 | Предыдущая визуальная приёмка недостаточна: главная была пустой. | Матрица empty/1/many/long names/error; desktop/mobile, клавиатура, 200% zoom, раскрытые фильтры и форма; проверить также каталоги, detail и admin. Отдельно повторить axe, не приравнивая его к UX-аудиту. | + +## Визуальная идентичность + +Сильная основа: тёмный хвойный фон, лаймовый акцент, спокойная бумажная подложка, контрастная антиква, озеро, крючок, поплавок, рябь и координатный радар. Это стоит сохранить. Следующие пункты — дизайнерские предложения, не программные дефекты. + +| ID | Приоритет | Предложение | Проверяемый результат | +|---|---|---|---| +| V01 | P2 | Утвердить RF4 Spotter как основное имя, «Ни хвоста, ни чешуи» как слоган либо явно выбрать обратную иерархию. | Одинаковое узнаваемое имя в шапке, favicon/manifest, title, OG и footer. | +| V02 | P2 | Свести размеры текста, отступы, радиусы, тени, focus и семантические цвета в небольшой набор CSS-токенов. | Компонентные состояния document/demo; источник и качество различаются текстом/формой, не только цветом. Разделить монолитный минифицированный global.css по компонентам. | +| V03 | P2 | Сделать паспорт точки компактным «полевым журналом»: координатная метка с копированием, линия времени, SVG рыбы, легенда шкалы. | Один основной визуальный акцент на карточку; индекс не выглядит вероятностью поимки, радар не выдаётся за настоящую карту. | +| V04 | P2 | Проверить маленькие знаки 16/32 px, sharpness app icons и maskable safe area; точность OG alt. | Чёткие отдельные SVG/PNG варианты; OG обещает реальный продукт и имеет осмысленную подпись изображения. | +| V05 | P3 | Отдельные обложки водоёмов и тематические OG для рыбы/точки. | Только после согласования прав и измерения веса; не препятствуют чтению карточек и загрузке. | + +## SEO и производительность + +SSR, русский `lang`, canonical, OG/Twitter, JSON-LD, sitemap, slug URL и 301 со старых UUID уже есть. Недостающие метатеги не являются главным препятствием: сейчас важнее корректные ответы, полнота каталога и полезный постоянный контент. + +| ID | Приоритет | Подтверждение/риск | Критерий исправления | +|---|---|---|---| +| S01 | P1 | index/records/report/status при сбое API отображают ошибку с HTTP 200; в detail возможен частично заполненный spot и `noindex={!spot}`. | Единая таблица 200/404/422/503, no-store/Retry-After и политика индексации ошибок. Не выдавать пустую аварийную страницу за нормальный результат. | +| S02 | P1 | Sitemap собирает полный справочник, но detail ищет slug только в первых 500 элементах; формы загружают первые 200. При росте появятся sitemap URL с ложным 404. | Прямые slug endpoints и пагинация каталогов; все sitemap URL разрешаются независимо от позиции в справочнике. | +| S03 | P2 | canonical убирает все query; structured data содержит жёсткий домен, `PUBLIC_SITE_URL` не передаётся в Docker build. Отдельная политика trailing slash/пагинации не оформлена. | Единый origin, политика вариантов URL и страниц пагинации; внутренние ссылки используют конечные canonical URL (в sidebar ещё UUID redirect). | +| S04 | P2 | Постоянные страницы рыба+водоём живут в sitemap, но контент ограничен 72 часами и быстро превращается в шаблон «данных нет». | Полезный подтверждённый справочный контент, дата обновления/источники, архив последних известных наблюдений с явной давностью, видимые breadcrumbs. Решение об индексации пустых комбинаций. | +| S05 | P2 | Sitemap lastGood хранится в памяти без максимального срока fallback; >49000 путей вызывает ошибку вместо index. | Документированный срок fallback, sitemap index при необходимости, корректный lastmod только по реальным изменениям. | +| S06 | P2 | Hero уже WebP ~71 КБ, но не включён в Caddy matcher brandAssets; старый LCP 9,3 с не является текущим измерением. | Проверить фактический Cache-Control hero; свежий mobile/desktop performance baseline после P0/U01, затем production Core Web Vitals. Не обещать ускорение в секундах без измерения. | +| S07 | P2 | robots содержит обычные правила и не закрывает весь staging; поисковые сервисы/превью ещё не проверены на рабочем домене. | Закрытие тестового окружения, проверка Google Search Console и Яндекс Вебмастера после DNS/TLS, sitemap/JSON-LD/OG validation и наблюдение за индексацией. | + +Политика HTTP основана на [Google: HTTP status codes](https://developers.google.com/crawling/docs/troubleshooting/http-status-codes), а согласование URL — на [Google: canonical URLs](https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls). Наличие schema/сайткарты само по себе не обещает индексацию или позиции. + +## Порядок реализации + +1. T01–T06: production маршруты, auth, сеть, клиентский лимит и реальная приёмка через Caddy. +2. D01–D08, T07: лимиты/публикация, наполнение, честное время и оценка конкретной точки. +3. U01–U04, S01–S02: основные сценарии, пагинация, ошибки и доступность индексируемых страниц. +4. V01–V04, U05–U07, S03–S06: визуальная система, понятность и поисковое качество. +5. T08–T10, D09–D10 и внешний launch checklist; S07 на рабочем домене. V05 после пилота. + +Тестировать по риску: адресные unit/contract проверки, один общий proxy/Compose-прогон для инфраструктурного пакета, браузер для визуального пакета. Документация и небольшие стилевые правки не требуют пересборки всех контейнеров. Статус выполнения ведётся в ROADMAP по ID этого отчёта. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 5fb4c2e..b48cdca 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -1,10 +1,10 @@ # План работ RF4 Spotter -Приоритет после аудита: [пакет исправлений и оставшиеся задачи](AUDIT_FIXES.md). Ранее закрытые пункты не означают, что замечания аудита устранены. +Приоритет после полного аудита: [отчёт 8 сентября 2026](PROJECT_AUDIT_2026-09-08.md). Новый пакет ниже имеет приоритет над историческими чекбоксами; [AUDIT_FIXES.md](AUDIT_FIXES.md) сохраняет историю исправлений. Этот файл — рабочий источник правды по развитию проекта. После завершения задачи её чекбокс меняется с `[ ]` на `[x]`, рядом добавляется ссылка на коммит или короткое подтверждение проверки. Новые задачи добавляются в соответствующий этап, а не хранятся только в переписке. -Последняя сверка плана со спецификацией, кодом и UI/UX-аудитом: 7 сентября 2026 года. +Последняя сверка кода, production-конфигурации, UI/UX, визуальной идентичности и SEO: 8 сентября 2026 года (база `486b4e9`). Обозначения: @@ -180,18 +180,58 @@ ## Ближайший рабочий пакет -Технический production-контур, health/readiness, backup/restore и безопасные логи готовы. Ближайшие пункты выполняются небольшими проверяемыми пакетами: +Production-контур требует исправлений до запуска. Доказательства и критерии приёмки каждого ID находятся в [полном аудите](PROJECT_AUDIT_2026-09-08.md). Следующая задача — **T01**, затем T02 и T03. Инфраструктурные изменения можно объединить в один проверочный Compose-прогон. -1. базовый SEO-контракт, `robots.txt`, sitemap, structured data и 404; -2. индексируемые страницы рыб и водоёмов; -3. community scheduler и наблюдаемость источников; -4. графический паспорт данных, OG-изображение и легенда; -5. внешние контакты, мониторинг, нагрузка, DNS/TLS и production-профиль на целевом сервере. +### P0 — блокеры production -После каждого пункта необходимо: +- [ ] T01: исправить Caddy-маршруты формы и повторной загрузки скриншота. +- [ ] T02: согласовать Basic/Bearer admin-аутентификацию через production proxy. +- [ ] T03: обеспечить исходящую сеть community scheduler при изоляции БД/MinIO. -1. запустить затронутые unit/integration-тесты; -2. выполнить `docker compose up --build -d` и проверить health; -3. для UI-изменений проверить desktop и ширину 390 px; -4. обновить чекбокс в этом файле; -5. зафиксировать результат отдельным небольшим коммитом. +### P1 — до открытой альфы + +- [ ] T04: сохранить реальную идентичность клиента для rate limit с проверенной цепочкой доверия proxy. +- [ ] T05: ограничить request body и время POST-запросов, обработать multipart/413/429. +- [ ] T06: расширить production acceptance на Caddy и scheduler; включить scheduler в мониторинг. +- [ ] T07: убрать диагностические подробности из публичного журнала импортов. +- [ ] D01: исключить disabled endpoint из ротации сайтов, проверить конкурирующие запуски. +- [ ] D02: атомарный cooldown research CLI, миграция старого state, единый production-путь запросов. +- [ ] D03: allowlist URL/redirects, общий ключ площадки и ограниченное чтение HTTP-ответов. +- [ ] D04: согласовать fallback alias по имени с автоматической публикацией. +- [ ] D05: расширить справочники и охват detail-источников через управляемую очередь с лимитом сайта. +- [ ] D06: устранить неверное обещание защиты от одного игрока и определить ограничения его вклада. +- [ ] D07: разделить время улова/публикации/получения/повторного обнаружения. +- [ ] D08: запрашивать оценку конкретной точки и рыбы без поиска в глобальном top-100. +- [ ] U01: исправить desktop-сетку фильтров, проверить раскрытие и мобильный layout. +- [ ] U02: согласовать область действия фильтров активности и полевых сигналов. +- [ ] U03: добавить публичную пагинацию и честные общие счётчики. +- [ ] U04: полезные ошибки формы, ожидание отправки и защита от двойного submit. +- [ ] S01: единый HTTP/SEO-контракт пустых, ошибочных и недоступных страниц. +- [ ] S02: получать сущности по slug, исключить ложные 404 после первых 500 записей каталога. + +### P2 — качество интерфейса, SEO и сопровождения + +- [ ] V01: согласовать основное имя RF4 Spotter и роль слогана «Ни хвоста, ни чешуи». +- [ ] V02: оформить CSS-токены и компонентные состояния, разделить глобальные стили. +- [ ] V03: компактный паспорт точки, копирование координат, ясная легенда шкалы. +- [ ] V04: проверить малые и maskable-иконки, качество OG и точность alt. +- [ ] U05: сократить первый экран, улучшить мобильную навигацию и empty/stale/error действия. +- [ ] U06: упростить карточки, подписи качества и терминологию. +- [ ] U07: визуальная матрица empty/1/many/long/error, клавиатура, zoom и повторный axe. +- [ ] S03: единый site origin, canonical/query/trailing-slash политика и конечные внутренние URL. +- [ ] S04: полезный постоянный контент рыб/водоёмов и архив с честной датой/атрибуцией. +- [ ] S05: ограничить срок sitemap fallback, документировать двойной URL-контракт и подготовить sitemap index. +- [ ] S06: проверить кэш hero и снять новый performance baseline после исправлений. +- [ ] T08: Python lock/constraints, CI web unit tests и регулярный аудит зависимостей. +- [ ] T09: нагрузочный бюджет агрегации/истории и решение об общей инвалидации по замерам. +- [ ] T10: release/migration шаг, bucket-scoped MinIO policy, план персонального admin-доступа. +- [ ] D09: частичные результаты импорта, история значимых редакций и процедура удаления оригиналов. +- [ ] D10: endpoint backoff, running/stalled состояния и достоверный последний успех. + +### После появления сервера / после пилота + +- [ ] S07 (P2): закрыть staging от индексации, подключить Search Console/Яндекс Вебмастер и проверить рабочие canonical/sitemap/OG/JSON-LD. +- [ ] V05 (P3): тематические обложки и OG рыб/водоёмов после обратной связи. +- [ ] Выполнить существующий внешний launch checklist: секреты, DNS/TLS, backup, мониторинг, контакты и реальные данные. + +После завершения задачи: адресная проверка по риску, отметка `[x]` со ссылкой на коммит/результат, обновление README при изменении поведения и отдельный коммит. Docker запускается для инфраструктурных или общих интеграционных проверок, а не после каждой правки документации. UI проверяется на desktop/mobile с наполненными и пустыми состояниями.