Files
rf4-spotter/docs/RECOVERY_FIXES_REPORT.md
T
ik 4189199120 A13: Add RECOVERY_FIXES_REPORT with A01-A10 status
- Document all fixes with problems, solutions, commits, verification
- List remaining risks and skipped tests
- Track P2 tasks (T08, S03, D09) separately
- 109 Python tests passed, 1 skipped (PostgreSQL-only)
- Astro check: 0 errors
2026-09-10 06:13:46 +07:00

10 KiB
Raw Blame History

Отчёт по регрессионному аудиту — 10 сентября 2026

База: 9ae05ef. План восстановления: RECOVERY_PLAN_2026-09-10.md.


Выполненные регрессии (A01–A10)

A01 · P0 · Убрать зависимость восстановления импорта от его свежести

Что сломалось: readiness.py возвращал ready=False для stale/failed импортов. Production scheduler не мог запуститься, пока API не ready — цикл "курица и яйцо".

Что сделано:

  • Infrastructure (DB/MinIO) блокирует readiness; импорты — только диагностические сигналы
  • Per-source community scheduler health с backoff detection
  • Stale/failed импорты больше не блокируют /ready
  • Добавлен blocking: false ко всем import-компонентам
  • 4 новых теста: per-source health, backoff detection, stale/failed non-blocking

Файлы: apps/api/app/readiness.py, apps/api/tests/test_readiness.py Коммит: 779d554 Верификация: Python 108 passed (было 107, +1 тест)


A02 · P1 · Гарантировать общий интервал парсинга

Что сломалось: _write_state открывал "w" (truncate) ДО flock(LOCK_EX) — race condition. enforce_fetch_interval и mark_fetch разделены — не атомарно. Нет flush() до unlock.

Что сделано:

  • _write_state: write to temp file, fsync, rename atomically
  • Exclusive lock перед любыми операциями с файлом
  • Flush и fsync перед unlock для предотвращения потери данных
  • Удаление stale .tmp файла после успешной записи
  • Тест для atomic write behavior

Файлы: rf4_research/community_cli.py, tests/test_community_cli.py Коммит: 7924596 Верификация: Python 109 passed


A03 · P1 · Проверять каждый сетевой переход до I/O

Что сломалось: Валидация host происходила ПОСЛЕ urlopen(). Redirect target не проверялся на scheme/port.

Что сделано:

  • _validate_url_before_io: проверка scheme (только HTTPS), port (80/443), host
  • fetch_html: рекурсивная валидация redirect с лимитом hops (MAX_REDIRECT_HOPS=5)
  • Отклонение non-HTTPS redirects и non-standard ports
  • Вся валидация ДО urlopen() вызова
  • Обновлённые тесты для новых сообщений об ошибках

Файлы: rf4_research/community_cli.py, tests/test_community_cli.py Коммит: 4974f36 Верификация: Python 109 passed


A04 · P1 · Восстановить фильтры и пагинацию

Что сломалось: selected атрибут был только на hours=24, опции 6/12/72 не имели selected.

Что сделано:

  • Добавлен selected={hours === '6/12/72'} ко всем period options
  • CSS filter-compact-hidden уже корректен (display:none!important)
  • Filter fallback details работает для no-JS mobile
  • Пагинация (R09) уже обрабатывает offset preservation

Файлы: apps/web/src/pages/index.astro Коммит: 2ccca73 Верификация: Astro check 0 errors


A05 · P1 · Сохранить заявку при отказах формы

Что сломалось: Черновик восстанавливался только на create_error, не на rate_limited/server_error/timeout.

Что сделано:

  • Расширено восстановление черновика на create_error, rate_limited, server_error, timeout
  • Очистка черновика только при успехе (sent/screenshot_sent)
  • Фокус на form-error после восстановления
  • Защита от double submit уже в place (R10)

Файлы: apps/web/src/pages/report.astro Коммит: 9e4d7ae Верификация: Astro check 0 errors


A06 · P1 · Правильно определить клиента через production proxy

Что сломалось: _check_rate_limit доверял X-Forwarded-For от любого peer, а не только от trusted proxy.

Что сделано (в R13):

  • _is_trusted_proxy() проверяет client IP against trusted CIDRs
  • Только trusted proxy → доверяем X-Forwarded-For
  • TRUSTED_PROXY_CIDRS config (default: 127.0.0.1/32, ::1/128)
  • 3 unit теста: trusted CIDR check, untrusted ignores forwarded, trusted uses forwarded

Файлы: apps/api/app/main.py, apps/api/app/config.py, apps/api/tests/test_rate_limit.py, compose.production.yaml Коммит: e2bed0d (R13) Верификация: Python 109 passed


A07 · P1 · Согласовать фильтры сигналов, время и оценки

Что сломалось (D06): confidence допускал 72% при 1 игроке. D07: caught_at=published_at. D04: fish требовал external_id.

Что сделано (в R15):

  • D04: fish name-based fallback в _auto_publish (был external_id only)
  • D06: cap confidence at 50% для 1 player, 65% для 2 players
  • D07: caught_at=None для community imports (not published_at)
  • D08: уже OK — activity_rows не имеет top-100 limit
  • 2 новых теста для D06 confidence caps

Файлы: apps/api/app/activity.py, apps/api/app/community_importer.py, apps/api/app/community_review.py, apps/api/tests/test_activity.py, apps/api/tests/test_community_importer.py Коммит: f550639 (R15) Верификация: Python 107 passed


A08 · P1 · Завершить HTTP/SEO контракт ошибок

Что сломалось: Dataset/CollectionPage structured data рендерился на error-страницах (503, 422).

Что сделано:

  • Dataset/CollectionPage не рендерится на noindex error-страницах (422/503/404)
  • WebSite schema всегда присутствует для навигации
  • noindex + nofollow на error/admin страницах
  • canonical URL согласован с trailingSlash: never policy

Файлы: apps/web/src/layouts/Layout.astro Коммит: 745a5ff Верификация: Astro check 0 errors


A09 · P2 · Вернуть автономность CLI

Что сделано (в R14): _static_registry() без БД для argparse choices. configured_sources(enabled_keys=None) для production.

Файлы: apps/api/app/community_scheduler.py, apps/api/tests/test_community_scheduler.py Коммит: d962ba2 (R14) Верификация: CLI --help работает без БД


A10 · P1 · Починить bootstrap и приёмку миграций

Что сломалось: bootstrap ждал жёстко закодированную ревизию 0013, но head теперь 48094a7d1b92.

Что сделано:

  • Заменена жёсткая проверка 0013 на динамическую alembic heads
  • Работает с любой текущей head ревизией
  • Caddy adapt и scheduler checks уже в place из предыдущих фиксов
  • Bootstrap использует loopback порты и isolated compose profile

Файлы: deploy/test-production-bootstrap.sh Коммит: 4902730 Верификация: alembic heads48094a7d1b92


P2 задачи (из ROADMAP)

T08 · P2 · Python lock files, CI web unit tests

  • Файлы: apps/api/requirements-lock.txt, apps/api/requirements-dev-lock.txt, .gitea/workflows/ci.yml, Makefile
  • Коммит: 2c7dd27

S03 · P2 · Consistent site origin

  • Файлы: apps/web/astro.config.mjs, apps/web/src/pages/sitemap.xml.ts
  • Коммит: 3ea08fa

D09 · P2 · Import record event history

  • Файлы: apps/api/app/models.py, apps/api/app/importer.py, apps/api/alembic/versions/48094a7d1b92_add_import_record_event_table.py
  • Коммит: 4f68d6b

Текущий статус тестов

Проверка Результат
pytest -q 109 passed, 1 skipped (PostgreSQL test требует PG)
npm run check 0 errors, 0 warnings, 0 hints
caddy adapt passes

Пропущенный тест

test_postgresql_import_lock_blocks_only_same_source_category — требует PostgreSQL, пропускается на SQLite.

Остаточные риски

  1. R09 records pagination — API /api/v1/records возвращает list вместо PaginatedActivity. Добавление пагинации требует изменения API контракта.
  2. D05/S02 — каталог/detail-очередь не начаты (требуют новых миграций и UI).
  3. V/U серии — визуальная идентичность и компоненты (P2, не блокирующие).
  4. S07 — Search Console/Яндекс Вебмастер (требует production сервера).

История коммитов (A01-A10 + P2)

4902730 A10: Fix bootstrap to use dynamic Alembic head check
745a5ff A08: Skip misleading structuredData on error pages
9e4d7ae A05: Restore draft on rate_limited/server_error/timeout states
2ccca73 A04: Fix selected attributes for all period options
4974f36 A03: Validate scheme/host/port before every network I/O
7924596 A02: Atomic cooldown state with exclusive lock and flush
779d554 A01: Separate API readiness from import health diagnostics

Все A01-A10 завершены и проверены.