57 KiB
План работ RF4 Spotter
Этот файл — единственный актуальный список задач. Завершённые аудиты сохранены как история в PROJECT_AUDIT_2026-09-08.md, REGRESSION_AUDIT_2026-09-09.md и RECOVERY_PLAN_2026-09-10.md; их старые чекбоксы не являются текущей очередью.
Последняя сверка: 15 сентября 2026.
Подтверждено:
- пакет восстановления A01–A13 завершён; итог и доказательства собраны в RECOVERY_FIXES_REPORT.md;
- полный Python suite: 178 passed, 1 skipped; skip относится к интеграционной проверке PostgreSQL и покрывается Docker-приёмкой;
- Astro check: 40 файлов, 0 errors / 0 warnings / 0 hints; production build проходит;
- API после миграции healthy;
apps/api/tests/test_api.py: 20 passed; - граф Alembic линеен и имеет единственную голову
0018; CI применяет её на чистой PostgreSQL, полный production bootstrap запускается отдельным еженедельным drill; - изолированный production bootstrap проходит Caddy adapt, scheduler validation и Playwright-сценарий отправки/модерации без обращения к внешним источникам;
- Astro + FastAPI + PostgreSQL остаются целевым стеком; Next.js и Vinext не используются.
Ближайший пакет — без сервера
Пункты выполняются сверху вниз, небольшими связанными коммитами.
Брендинг и визуальная идентичность
- B01 · Иерархия имени. RF4 Spotter — единое имя продукта в UI, metadata, manifest и документации; «Ни хвоста, ни чешуи» — поддерживающий слоган. Обновлены wordmark в header/footer и подпись выпуска.
- B02 · Дизайн-токены. Добавлены семантические роли поверхностей, текста, границ, фокуса, success/warning/danger и всех источников. На токены переведены паспорта данных, status-карточки, skeleton и focus; контрастные пары зафиксированы в brand system.
- B03 · Графическая грамматика. Мотивы закреплены за функциями: крючок — бренд, поплавок — активность/ожидание, леска — время, радар — координаты, силуэт — сущность рыбы. Убраны ложная рябь неполных сигналов и дублирующие радар круги карточки лидера; неполнота теперь подчёркнута спокойной полевой меткой.
- B04 · Компонентная подпись.
SectionHeading,PageHeroиStatePanelунифицируют заголовки, hero и состояния; семантическая иерархияdata-actionсогласует CTA публичных и admin-страниц. - B05 · Motion-система. Смысловые микроанимации пульса, поплавка, загрузки и интерактивного отклика используют общие duration/easing-токены; декоративное движение empty-state удалено, reduced motion покрывает элементы и псевдоэлементы.
- B06 · Brand QA. Desktop/mobile-проверка усилила каталоги atlas-hero, счётчиками, береговыми контурами и выразительными карточками. Favicon/PWA PNG перегенерированы из SVG-мастера без размытия;
anyи полнофоновыеmaskable-иконки разделены, manifest и Caddy cache обновлены, OG подтверждён как 1200×630. - B07 · Аудит официальных изображений. Текущий records-parser получает названия рыб/водоёмов и
titleприманки, но не image URL; официальные gallery/userguide содержат тематические медиа без устойчивого полного соответствия каноническим сущностям. Изображения не хотлинкать и не считать разрешение на данные автоматическим разрешением на медиапубликацию. - B08 · Семейства силуэтов рыб. Три случайных hash-варианта заменены классификатором и отдельными формами
pike,salmonid,cyprinid,perch,catfish,eel,flatfish,marine, плюс честныйgeneric. Компонент допускает ручное переопределение family; название остаётся главным идентификатором. - B09 · Глифы снастей и приманок. Добавлены SVG-глифы
spinner,wobbler,soft,boilie,worm,rig,unknownи стабильная палитра по normalized name. Классификация срабатывает только по явным словам; глиф сопровождает текст в activity, лидере, уловах, рекордах и списке лучших приманок, не выдавая категорию за точную модель. - B10 · Визуальные отпечатки водоёмов. Для каждого slug воспроизводимо выбираются один из восьми береговых контуров, число волн, положение точки и двухсимвольный индекс. Знак используется в каталоге и detail-hero; это явно абстрактный отпечаток, а не карта или игровая география.
- B11 · Разрешённый media pipeline и публичная медиатека. Версионированный baseline содержит 19 водоёмов и 252 рыбы; полное число снастей остаётся
null, а не подменяется числом приманок. После явного разрешения владельца от 14.09 опубликованы все 456 скачанных и целостных файлов: 243 изображения рыб, 149 снастей/приманок и 64 справочных материала. Публичные/mediaи/api/v1/media/*отдают Git-копии по SHA-256 без hotlink; у каждой карточки есть плашка и прямая ссылка на источник. 227 альтернативных URL остаются provenance-onlyduplicate, 11 невалидных URL не публикуются, 10 RF4DB-кандидатов остаются в очереди. Audit не находит ошибок и бесхозных файлов. Batch-контур по-прежнему соблюдает единый 30-минутный cooldown домена и не одобряет будущие загрузки автоматически. - B12 · Эмблема сочетания. Страница «водоём + рыба» получила составной атласный seal: собственный отпечаток водоёма пересекается со смысловым силуэтом рыбы. Так визуальная идентичность сопровождает всю иерархию каталога и не требует внешних изображений.
- B13 · Навигационная леска атласа. Разрозненные ссылки назад на detail-страницах заменены доступной breadcrumb-цепочкой с мотивом лески и узлов. Страница точки связывает главную, водоём и координаты; сочетание — каталог, водоём и рыбу. Текущий узел всегда подписан текстом и отмечен
aria-current. - B14 · Атласные переходы сущностей. Боковые списки рыб и водоёмов на detail-страницах получили компактные силуэты и отпечатки рядом с полным текстовым названием. Знаки продолжают систему каталога в рабочей навигации, а стрелка явно показывает переход к странице сочетания.
- B15 · Целостность медиакаталога. Локальный audit сводит статусы очереди и проверяет наличие файлов, SHA-256, фактические dimensions/MIME, каноническое соответствие approved-записей и бесхозные файлы. Проверка не обращается в сеть и может использоваться как pre-publication gate.
- B16 · Самодостаточное хранение media dataset. Все управляемые оригиналы из
data/media/files/версионируются в Git вместе с manifest; локальный клон содержит полный исследовательский набор. Пользовательские скриншоты остаются в MinIO/S3, а наличие файла в Git не означает разрешение на публикацию без статусаapproved. - B17 · Полный каталог рыб RF4DB — финальная загрузка. Разрешённый RF4DB дал все 252 подписанных кандидата; 227 совпадений с RF4MAP сохранены как provenance-only
duplicate, 15 уникальных файлов скачаны, а 10 URL (9 уникальных названий) остаются в очереди после корректно остановленного TLS-сбоя. Все поддомены RF4DB объединены одним 30-минутным cooldown. Завершить следующим разрешённым batch-окном и отдельно утвердить новые файлы. - B18 · Каталог водоёмов RF4DB. После cooldown проиндексировать 19 русских карточек водоёмов, проверить полноразмерные карты и поставить только уникальные изображения в media queue.
- B19 · Каталог снастей RF4DB. Подготовительный media-контур; полный справочник, crosswalk, связи с уловами и публичные страницы выполняются по пакету G01–G09 ниже. Не считать каталог полным до получения проверяемого общего счётчика по категориям.
- B20 · Аудит качества рыбных изображений. Offline
media_cli --quality-reportпроверяет фактические dimensions опубликованных файлов, отдельно считает неизбежный upscale в карточках и известные альтернативы. На 14.09: 227 из 243 опубликованных рыб имеют только 48×48 PNG RF4MAP и растягиваются до 180 px; 16 имеют 1024×1024 WebP. Для всех 227 низких файлов уже известны альтернативные URL RF4DB. Совпадение сущности больше нельзя считать достаточным основанием пропустить потенциально более качественный файл. - B21 · Очередь quality-upgrade. Текущие approved-файлы не меняются до готовности замены. 15.09 отдельная очередь создана для 226 прямых RF4DB-альтернатив с сохранением
duplicate_of; первая партия из 40 файлов загружена без ошибок вupgrade_stored, ещё 186 замен и 10 кандидатов недостающих рыб ожидают следующих разрешённых 30-минутных окон. Один provenance-onlyduplicate, не связанный с низкоразрешённым published-файлом, намеренно не переведён. Ошибка останавливает домен по существующим правилам; публикация старой версии при этом не прерывается. - B22 · Сравнение вариантов разных источников. Offline-команда
--compare-quality-upgradesсопоставляет сохранённые кандидаты с опубликованными fallback поduplicate_ofи проверяет dimensions, размер файла, MIME, прозрачность и aspect ratio. Первая партия: 40/40 кандидатов — прозрачные WebP 1024×1024, все прошли порог 256 px и сохранили пропорции; технических ошибок нет. До переключения остаётся визуально подтвердить соответствие подписи. Маленький файл сохраняется как fallback; источник RF4DB/RF4MAP/официальный RF4 не получает автоматического приоритета. - B23 · Безопасное продвижение и provenance. Связи
supersedes/replaced_by, отдельное решение review, атомарное переключение публичногоentity_keyи явный CLI rollback сохраняют обе исходные ссылки и возможность отката. Публичная карточка показывает источник выбранного изображения, а manifest сохраняет проверенные варианты. Старый Git-файл не удаляется при включении нового. - B24 · Производные размеры. После выбора оригиналов генерируются детерминированные WebP/AVIF thumbnails для каталога и отдельный крупный вариант для detail, хэши производных фиксируются в manifest и отдаются через каталог. Маленькие карточки больше не обязаны загружать 1024×1024 оригинал.
- B25 · Визуальная приёмка media. Contact sheets для всех 226 замен собраны и просмотрены offline: явных ложных соответствий, обрезки и искажённых пропорций не обнаружено; одно отличие подписи (
Ерш-носарь/Ёрш-носарь) орфографическое. Осталась browser-проверка репрезентативных desktop/mobile страниц в light/dark; sandbox Chromium пока не позволяет её выполнить. Gate: нет битых файлов, искажённых пропорций, ложных соответствий, обрезанного объекта и изображений ниже 256 px без явной пометки «низкое разрешение».
Каталог водоёмов, карты и координаты
- W01 · Canonical-каталог RF4DB. 16.09 через браузерный контекст получен и проверен индекс
/ru/maps: 19/19 карточек, source slug/ID, русское название, уровень открытия, число видов рыб и URL изображения сохранены вdata/waterbodies/rf4db-catalog-2026-09-16.json. Добавлена командаpython -m app.cli import-waterbody-catalog --input ...для применения снимка в БД. Изображения остаются кандидатами без автоматической ролиmapилиcover; detail-данные выполняются отдельно по W02. - W02 · Карточки водоёмов RF4DB. Строгий fixture-based parser
rf4db-waterbodyрасширен под реальный DOM/fishes/и/positions/, regression-тесты, безопасныйupdate_waterbody_detailи атомарный batchupdate_waterbody_details/import-waterbody-detailsготовы; первая detail-карточка (level_000_home) сохранена браузером как snapshot. Осталось последовательно разобрать 18 detail-страниц с общим 30-минутным cooldown домена. - W03 · Модель и provenance. В модель
Waterbody, API-каталог и миграции0017/0019/0020добавлены nullable-поля provenance, счётчик видов и detail-факты; идемпотентные upsert-функции обновляют только подтверждённые source identity и не удаляют исчезнувшие строки. Осталось применить их к проверенному canonical-каталогу и отдельно разделить игровой и редакционный тексты при подключении detail-данных. - W04 · Классификация изображений. Добавлены допустимые роли
waterbody_cover,waterbody_map,waterbody_depth_map,waterbody_screenshotи проверка их назначения только через review для canonical waterbody. Кандидаты по-прежнему не получают роль автоматически. Осталось наполнить очередь detail-изображениями и провести contact-sheet review с проверкой dimensions, MIME, SHA-256, соответствия названию и источника. - W05 · Crosswalk источников. Добавлен offline-конструктор консервативных предложений: нормализуются только точные имена/алиасы, неоднозначные и unmatched строки не получают canonical key; отсутствие ID выдаётся лишь диагностикой и не считается удалением. Осталось подать реальные RF4DB/RF4MAP/RF4 Posts identities и вручную подтвердить результаты, включая три ранее отмеченных отсутствующих RF4MAP объекта.
- W06 · Координаты и точность. В
ExternalObservation, staging, provenance опубликованного улова и публичных activity/spot-ответах добавленыcoordinate_raw,coordinate_precision = exact | approximate | area | missingи список источников; RF4DB/RF4-STAT/RF4MAP/RF4 Posts parsers теперь протягивают исходную строку, включая строки без доступных числовых координат. Карточка точки показывает точность рядом с координатами. Осталось провести browser QA. - W07 · Публичный API и страницы. API и detail-страница теперь выводят подтверждённые detail-факты водоёма: описание, уровень, количество видов, алиасы, число ссылок на точки и отдельный счётчик изображений-кандидатов; источники и непроверенные media не смешиваются. Осталось подключить только проверенные waterbody media roles и завершить browser QA, включая различение карты, заставки и абстрактного отпечатка.
- W08 · Приёмка и эксплуатация. Добавить fixture-based parser tests, offline catalog/media audit, проверку 19 canonical entities, отсутствие битых файлов и browser QA desktop/mobile. Сетевые тесты не выполнять; регулярный импорт оставить opt-in и под общим cooldown/backoff.
Каталог снастей, наживок и прочей оснастки
Этот пакет повторяет жизненный цикл W01–W08, но не смешивает разные уровни описания. Наживка/приманка — предмет, который указан в улове; снасть — компонент комплекта (удилище, катушка, леска, крючок и т. п.); оснастка — собранная схема или монтаж (например, донная, поплавочная, method). Число найденных изображений приманок не считается размером полного каталога.
- G01 · Canonical-каталог RF4DB. Получить разрешённый индекс категорий gear и проверить полный набор доступных страниц по типам:
bait,lure,rod,reel,line,hook,rig,float,sinker,other. Сохранить исходный slug/ID, русское название, категорию, подкатегорию, бренд, семейство, игровые ограничения/уровень и source URL. Отсутствие общего счётчика или закрытая категория должны оставаться явноunknown, а не превращаться в оценку полноты. - G02 · Карточки предметов и оснасток. Подготовить строгие fixture-based parsers для списка и detail-страницы: характеристики, варианты, совместимость, изображения, связанные типы монтажа и исходные значения. Парсер должен различать отсутствующее поле, «не применимо» и фактическое нулевое значение; при неполном или изменившемся ответе сохранять предыдущие подтверждённые данные. Detail-запросы выполнять только для выбранных карточек и с общим 30-минутным cooldown домена.
- G03 · Модель и provenance. Расширить
baitдо каноническогоtackle_itemлибо выполнить безопасную миграцию с обратной совместимостью API:kind,category,subcategory,brand,family,source_system,source_external_id,source_url,source_checked_at,raw_payload. Отдельно моделироватьrig/монтаж и его компоненты; не хранить удилище, катушку и монтаж в одном свободномrig_type. Для каждой характеристики сохранить источник и статус проверки. - G04 · Crosswalk и нормализация. Построить offline-crosswalk между RF4DB, официальными рекордами, RF4MAP, RF4 Posts и локальным справочником. Нормализовать регистр, пробелы, дефисы, единицы и локализацию; предлагать совпадение только при точном имени/алиасе плюс совместимой категории. Неоднозначные, брендовые варианты и unmatched-строки отправлять на review без автоматического canonical key; исходное значение всегда сохранять.
- G05 · Связи с уловами и источниками. Протянуть канонические предметы и монтажи через community import, официальные записи и форму улова, сохранив
raw_payloadи список missing fields. Поддержать несколько предметов в одном комплекте, порядок/роль компонента и источник каждой связи; старыйbait_idи текстовые значения не терять при миграции. Публикация полного наблюдения по-прежнему требует подтверждённых соответствий, а не простого совпадения строки. - G06 · API и публичный каталог. Добавить пагинированные каталоги и detail endpoints с фильтрами по категории, бренду, семейству и уровню, а также безопасные ссылки из улова/точки на использованную приманку, снасть и монтаж. Показывать только подтверждённые характеристики, источник, свежесть и неполноту; не выдавать рейтинг эффективности, если его нельзя объяснить числом наблюдений, игроками, периодом и качеством источников.
- G07 · Аналитика сочетаний и рекомендации. После появления достаточных данных считать отдельно «водоём + рыба + предмет», «точка + рыба + предмет» и «способ ловли + монтаж». Зафиксировать минимальный объём выборки, защиту от одного игрока/дубликатов и decay по свежести; разделить факт использования, частоту и рекомендацию. Пустая или малая выборка должна показывать «данных мало», а не советовать конкретную снасть.
- G08 · Медиа и качество. Разнести media roles для
tackle_item,bait,rigи общего reference; связать варианты черезentity_key,duplicate_of,supersedes/replaced_by. Проверять dimensions, MIME, SHA-256, прозрачность, aspect ratio, подпись и категорию; не переключать approved-файл автоматически, не считать userguide-скриншот карточкой предмета и не публиковать media-кандидатов без review и разрешённого provenance. - G09 · Приёмка и эксплуатация. Добавить fixture/regression tests, offline catalog/crosswalk/media audits, проверку идемпотентности и сохранения старых данных при сбое, API/UI acceptance для пустых, неоднозначных и многокомпонентных комплектов, browser QA desktop/mobile и query-plan gate для фильтров/сочетаний. Сетевые тесты не выполнять; импорт оставить opt-in, последовательным и под общим cooldown/backoff. Закрывать пакет только после проверяемого счётчика по каждой категории либо явной фиксации
unknown.
Тёмная тема
Реализовывать последовательно: сначала семантическая палитра и системный режим, затем ручное управление и полировка компонентов. Тёмная тема должна сохранять полевую эстетику RF4 Spotter, а не быть механической инверсией светлой.
-
D01 · Семантическая палитра и color-scheme. Жёсткие цвета и конфликтующая роль исторического
--deepпроинвентаризированы; добавлены независимые canvas/surface/elevated/control, text, border, shadow и контрастные status-роли для light/dark. Базовые public/admin поверхности получили первый dark-layer, нативные controls используютcolor-scheme: light dark; стратегия миграции зафиксирована в dark-theme.md. -
D02 · Системный режим без вспышки. Первый визит следует
prefers-color-schemeполностью через CSS: серверный HTML сразу совместим с системной темой, состояние не хранится, inline bootstrap не добавлен и CSP не ослаблена. Явное переопределение появится только вместе с D03–D04. -
D03 · Переключатель темы. В header добавлен доступный трёхпозиционный выбор «Системная / Светлая / Тёмная»; на узких экранах он сохраняет три понятные иконки и доступные названия. Кнопки работают с клавиатуры, имеют видимый focus и синхронизируют
aria-pressed. -
D04 · Сохранение и SSR-согласование. Выбор хранится год в allowlist-cookie
rf4-themeсSameSite=LaxиSecureна HTTPS; Astro SSR выставляетdata-themeдо отрисовки, а системный режим оставляет выбор браузеру. Собранный Astro client script переключает тему без inline-кода, localStorage и ослабления CSP. -
D05 · Темизация компонентов и графики. На семантические light/dark-токены переведены базовые и admin-поверхности, header, каталог, breadcrumb-леска, ссылки сущностей, таблицы, формы, moderation-карточки, provenance/quality/status badges, паспорта данных, pagination, empty/loading-состояния, радар, timeline, силуэты и глифы; добавлен forced-colors layer. Инвентаризированы публичные растры и alpha-каналы PWA-иконок, hero получил dark/print treatment, screenshots — семантическую подложку. Осталась браузерная приёмка теней и градиентов в обеих темах. Источники и статусы сохраняют текстовые подписи и не различаются только цветом.
-
D06 · Метаданные браузера и CSP. Добавлены парные
theme-colorдля системной light/dark схемы; явный cookie-выбор согласуется с SSR и мгновенно переключает активный meta-тег. PWA manifest использует устойчивый тёмный brand chrome и splash background. Production build сохраняетinlinedScripts: []; новые inline script/style/attributes не появились, ослабление CSP не потребовалось. -
D07 · Визуальная и accessibility-приёмка. Проверить light/dark/system на 320/390/768/1280 px для главной, каталогов, detail, records, report, status и всех admin-экранов; покрыть normal/hover/focus/disabled/error/loading/empty и длинные данные. Для обеих тем обеспечить WCAG AA, отсутствие горизонтального scroll и CLS, корректную печать, reduced motion и переключение без потери введённых данных; сохранить эталонные screenshots и краткий отчёт.
-
Q01 · Документы источников — реестр готов, нужны первичные подтверждения. Создан единый production-gate с атрибуцией, общим лимитом 30 минут, хранением и процедурой отзыва для RF4DB, RF4-STAT, RF4MAP, RF4 Posts и официального RF4. До открытой публикации приложить устойчивые ссылки/копии первичных разрешений, контакты, даты и отдельно подтвердить право на изображения; пустое поле блокирует соответствующий источник.
-
Q02 · Управляемое удаление источника. Изменение ранее опубликованной записи возвращает её в staging, сбрасывает сопоставление и переводит связанный улов в pending с новой версией решения. Результат проверки хранится как
available,missing,temporary_errorилиblocked: только подтверждённыйmissingотзывает публикацию, временная ошибка и блокировка остаются диагностикой. Повторное появление требует ручного подтверждения. Withdrawn-записи исключены из публичной активности, а статус и время проверки доступны в admin provenance и журнале решений. Отдельного сетевого обхода нет: Q03 подключит эту реакцию к разрешённому плановому запросу. -
Q03 · Целостность ссылок. Результат уже разрешённого scheduler-запроса классифицируется без retry и дополнительного HTTP:
404/410—missing,401/403/429—blocked, остальные сетевые/парсерные сбои —temporary_error. Результат применяется только к наблюдениям с точным совпадением source system и запрошенного URL. Отсутствие записи в агрегатном списке намеренно не считается удалением; появившиеся в успешном ответе записи отмечаютсяavailableобычным staging-проходом. Все попытки по-прежнему резервируются до запроса и расходуют общий cooldown домена. -
Q04 · Состояния ожидания. Асинхронные admin-очереди получили каркасные карточки,
aria-busy, очистку при ошибке и поддержкуprefers-reduced-motion. Публичные страницы остаются SSR и не показывают искусственный skeleton; форма уже блокирует повторную отправку и сообщает «Отправка…». -
Q05 · Базовая визуальная матрица. Главная проверена в браузере на 320/390/768/1280 px, ключевые public-маршруты — на 320 px; удалён корневой
min-width, создававший горизонтальный scroll. Добавлен E2E-контракт для/, records, report, waterbodies, status и видимого skip-link. Ширина 320 px также покрывает reflow, эквивалентный 200% zoom для окна 640 px. Расширенная матрица наполненных/длинных/error-состояний остаётся постоянной частью приёмки UI, а не отдельным блокером. -
Q06 · Performance baseline. На локальной production-сборке после оптимизации hero: performance 100, LCP 1,66 с, FCP 1,15 с, CLS 0,023, TBT 9 мс. Устранены найденные Lighthouse проблемы контраста и accessible name; методика и бюджеты записаны в performance-baseline.md. Полевой INP измеряется только после запуска.
-
Q07 · Нагрузочная методика. Добавлен read-only runner для activity, records, staging и moderation с warm-up, p50/p95/max, распределением HTTP-кодов и ограниченной concurrency. Методика фиксирует контекст запуска, ступени нагрузки и бюджеты, но не объявляет результатов до трёх прогонов на целевом сервере.
-
Q08 · Политика MinIO. Production bootstrap создаёт bucket и отдельную policy только с bucket location/list и get/put/delete его объектов. App credentials проверяются через bucket stat и отрицательную проверку глобального list; API больше не требует
ListAllMyBucketsи не пытается создавать bucket. Root credentials остаются только у init-задачи. -
Q09 · Release-процедура. Миграции вынесены из API runtime в одноразовый
migrateservice; API запускается только после успешного Alembic upgrade. Документированы backup, rollout и два варианта отката. Изолированный drill поднимает предыдущую ревизию схемы, добавляет контрольные данные, обновляет до head и проверяет их сохранность и новые колонки. -
Q10 · Документальная ревизия. README и активный ROADMAP сверены 13.09.2026 с тестами, Alembic head, CSP и локальными
media_cli --audit/--coverage; устаревшие числа исправлены, исторические аудиты не возвращены в backlog. Дальнейшее обновление обоих файлов остаётся обязательным правилом каждого пакета.
Дополнения после ревизии PROJECT_AUDIT_2026-09-10
Аудит выполнен на старой базе 13e04e6; рекомендации ниже повторно проверены по текущей ветке. Уже реализованные или неприменимые предложения не возвращаются в backlog.
- Q11 · Декомпозиция API. Catalog, activity/spots, records/community/status/import-history, submissions и весь admin API вынесены в отдельные
APIRouter.main.pyоставляет composition root, middleware, health/readiness и временные совместимые экспорты rate-limit для тестового контракта; URL и OpenAPI сохранены. - Q12 · Query-plan gate. Воспроизводимый TEMP-only fixture создаёт 100 000 уловов, 5 000 точек, 252 рыбы и 19 водоёмов без изменения рабочей БД; gate снимает
EXPLAIN (ANALYZE, BUFFERS)для activity, records count/page, spot detail и public spot pages, требует index scan у селективных путей и бюджет 250 мс. Повторный прогон: 1,58 / 13,66 / 0,55 / 0,14 / 29,61 мс. Планы подтвердили существующие индексы; новыйfish_id-индекс, SQL-агрегация и materialized view не добавлялись без оснований. Production p95 остаётся задачей после сервера. - Q13 · Production bootstrap в CI. Отдельный workflow запускает
deploy/test-production-bootstrap.shвручную или раз в неделю, а не на каждом push. Вывод bootstrap всегда сохраняется 14 дней; при падении добавляются Compose status и Playwright diagnostics. - Q14 · Полная CSP. Каждый Astro SSR-ответ получает отдельный nonce для динамического JSON-LD и собственный строгий CSP;
FILES_DOMAINвалидируется как hostname. Production запрещает inline handlers,unsafe-inline, eval, wildcard и HTTP; page scripts/styles остаются same-origin_astro-ассетами, style/script attributes запрещены. Caddy сохраняет upstream policy и использует строгий fallback для API. Bootstrap проверяет совпадение nonce на главной, report и admin и доступность OG; реальный signed screenshot проверяется после наполнения production MinIO. - Q15 · Частичная деградация главной. SSR независимо получает activity, community signals и оба справочника через settled-результаты. Отказ секции показывает собственный
StatePanel, сохраняет остальные данные и HTTP 200 сX-RF4-Partial/Cache-Control: no-store; только отказ всех четырёх частей возвращает 503,Retry-Afterи noindex. Client-side loading и optimistic UI не добавлялись. - Q16 · Контракт OpenAPI.
apps/api/openapi.jsonдетерминированно генерируется из FastAPI; CI проверяет его актуальность после backend suite. Изменение artifact обязательно рассматривается вместе с реализацией, а ручное редактирование не используется. - Q17 · Эксплуатационные документы. Зафиксированы ADR по Astro/FastAPI/PostgreSQL, локальному cache, scheduler/cooldown и разделению PostgreSQL/MinIO. Incident runbook покрывает заполнение диска, отказ PostgreSQL/MinIO, зависшие импорты, ошибки миграций, компрометацию секретов и критерии закрытия без опасных reset/recreate операций.
- Q18 · Минимальная observability. Зафиксированы дешёвые SLI, стартовые пороги и источники для request count/latency/5xx, moderation/staging depth, возраста импортов и инфраструктуры. Начальный контур использует JSON-логи, readiness, diagnostics и host monitor; exporter выбирается после покупки сервера, tracing и Prometheus не вводятся без измеренной потребности. Перед альфой остаётся подключить реальный канал и проверить critical/recovery alert.
Административная панель
- M01 · Усиление административного входа. Сохранён двойной барьер Caddy Basic Auth + API Bearer; внешние ссылки ограничены
http/https, UI завершает сессию после 15 минут бездействия, предоставляет явный выход и возвращает вход после401. Неуспешная API-авторизация ограничена постоянным счётчиком по HMAC-идентификатору клиента с учётом доверенного proxy; успешный вход очищает ошибки клиента. Bearer-токен не сохраняется в URL, cookie или browser storage. - M02 · Единый dashboard.
/adminпоказывает счётчики pending-уловов и staging-наблюдений, число активных источников, их безопасные публичные статусы, последние импорты и быстрые переходы в очереди. Dashboard использует тот же memory-only токен и 15-минутную сессию, не выводит секреты, внутренние URL и полные тексты исключений. - M03 · Эффективность очередей. Очередь внешних наблюдений получила серверные фильтры по источнику и полноте, безопасный поиск по рыбе/водоёму и сортировку по свежести или риску; проблемный порядок поднимает неполные и несопоставленные записи, а параметры работают до пагинации. Обе очереди блокируют всю карточку на время решения, сохраняют введённую причину при ошибке, явно подтверждают успех и переводят фокус к следующей записи. Безопасные горячие клавиши работают только внутри карточки с фокусом и отключены в полях ввода; отклонение и удаление намеренно оставлены только на кнопках.
- M04 · Полный provenance и история решений. Admin API отдаёт время первого/последнего обнаружения и проверки, явный список missing fields и allowlist безопасных скалярных полей исходной записи; карточка показывает их перед публикацией. Единый read-only журнал объединяет решения по пользовательским и внешним записям без ников, URL и исходных payload и отображается на dashboard. Отдельный JSON-экспорт исключает также UUID сущностей, оператора и свободный текст причины; токен остаётся только в памяти вкладки.
- M05 · Защита от параллельных решений. Обе очереди отдают
moderation_version; mapping/publish/reject/approve/delete требуют увиденную версию и повторно сверяют её под row lock. Успешное решение атомарно увеличивает version, а устаревшая вкладка получает понятный409и автоматически перезагружает очередь. Схема обновляется линейной миграцией0015. - M06 · Персональные роли — после пилота. Если модераторов станет больше одного, заменить общий токен индивидуальными аккаунтами, короткими сессиями, отзывом доступа и ролями; писать идентификатор оператора в аудит. Для одного владельца альфы не добавлять отдельный auth-сервис заранее.
План доведения административной панели
Этот план фиксирует следующий рабочий контур поверх уже закрытых M01–M05. Текущий MVP функционален для одного владельца альфы, но ниже перечислены эксплуатационные пробелы, найденные аудитом, и критерии их закрытия.
- A01 · Защита маршрутов и границы сессии. Покрыть точный
/adminи/admin/*единым Caddy Basic Auth, выставлятьnoindexиno-storeдля всех административных ответов, проверить отсутствие обхода через API и корректные401/429. Критерий: автоматический proxy-smoke для/admin, страниц и/api/v1/admin/*с отсутствующим, неверным и валидным доступом. - A02 · Единая auth/error UX. Привести dashboard, moderation и external sources к одинаковому поведению при
401,409,429,5xx, loading/empty-состояниях: понятное сообщение, блокировка повторной отправки, возврат к входу только при истёкшей авторизации. Критерий: regression-тесты на каждый ответ и сохранение введённой причины. - A03 · Полный single-owner workflow. Добавить пагинацию очереди уловов, кнопку запуска официального импорта и отображение результата/истории, безопасные статусы источников с возрастом данных, cooldown/backoff и ручное обновление очередей. Критерий: владелец может пройти путь «импорт → проверка → решение → история» без API/CLI; старые данные сохраняются при сбое импорта.
- A04 · Контур медиа-проверки. Сделать admin-экран для просмотра approved/upgrade_queued медиа, исходника, размеров, производных и provenance; добавить approve/rollback только через существующие безопасные состояния. Критерий: ни одна публичная замена не происходит без явного решения и проверяемого manifest-а.
- A05 · Многопользовательский доступ. После пилота заменить общий Bearer-токен персональными аккаунтами и короткими серверными сессиями с отзывом, ролями read-only/moderator/importer/owner, operator ID в аудите и журналом входов. Критерий: минимальные права реально ограничивают действия, logout/revoke инвалидируют сессию на сервере.
- A06 · Browser/accessibility acceptance. Проверить
/admin, moderation и external sources в 320/390/768/1280 px: клавиатура, focus order, screen reader labels, reduced motion, forced colors, темы, ошибки/пустые очереди и конфликт409. Критерий: Playwright + axe без блокирующих дефектов и ручная визуальная проверка. - A07 · Production gate. Выполнить preflight с реальными секретами, проверить Caddy Basic + API Bearer, закрытые внутренние порты, backup/restore PostgreSQL и MinIO, readiness, no-store и внешний smoke после деплоя. Критерий: acceptance-runbook пройден, rollback и процедура отзыва доступа документированы.
Готовность открытой альфы — требуется сервер или внешний сервис
- Купить/подготовить Linux-сервер и подтвердить его публичный IPv4/IPv6.
- Настроить DNS
rf4spotter.ruиfiles.rf4spotter.ru, открыть только необходимые внешние порты и получить корректный TLS через Caddy. - Создать
.env.production, заменить все демонстрационные секреты и выполнитьdeploy/preflight.sh. - Создать публичные контакты privacy/abuse и подключить их к сайту и alpha-баннеру.
- Настроить внешний backup, выполнить восстановление с сервера и подключить реальный канал уведомлений.
- Наполнить альфу небольшим разрешённым набором данных и провести финальную приёмку по open-alpha-acceptance.md.
- После 24 часов стабильной работы пригласить первых игроков, собрать обратную связь и закрыть блокирующие проблемы пилота.
- Подключить Search Console/Яндекс Вебмастер и проверить реальные canonical, sitemap, robots, OG и JSON-LD.
- Снять серверные p95 и Lighthouse; скорректировать индексы, кэш и изображения только по измерениям.
После пилота
- Решить по обратной связи, нужны ли OCR, Telegram, профили и уведомления о клёве.
- Рассмотреть cursor pagination при росте объёмов; текущая offset pagination достаточна для альфы.
- Рассмотреть общую инвалидацию кэша при нескольких API-процессах; сейчас действует TTL и локальная инвалидация.
- Рассмотреть Redis только при переходе к нескольким API-процессам или после измеренного дефицита локального cache; не добавлять отдельный stateful-сервис заранее.
- Подготовить тематические обложки и индивидуальные OG для рыб/водоёмов, если страницы подтверждают поисковую ценность.
Правила выполнения
- Сетевой парсинг одной площадки — не чаще одного раза за 30 минут, включая ошибки и разные endpoint.
- Каждая публичная запись обязана показывать источник; неполные данные — отдельную пометку и список отсутствующих полей.
- Реальные источники не используются в тестах: только fixtures и изолированные Docker-сценарии.
- Docker запускается одним общим прогоном для инфраструктурного пакета, а не после каждой правки.
- Пункт закрывается только после адресной проверки; команда и результат фиксируются в коммите или отчёте.
- Не менять целевой стек Astro + FastAPI + PostgreSQL.