Files
runners-calendar/docs/product-backlog.md
2026-07-13 20:47:55 +03:00

25 KiB
Raw Permalink Blame History

Backlog Calendar Run

Срез репозитория: 12 июля 2026. Это единый продуктовый и технический backlog, составленный по текущему коду, а не список абстрактных идей.

Текущая база

Контур Версия Уже работает
Backend 1.6.0 Express + PostgreSQL, миграции и seed, строгий CRUD личных стартов, фильтры истории, cookie-сессии, CSRF, подтверждение email, Turnstile, SMTP, смена пароля и управление сессиями, SSRF-защита и восстановление обложек, /health, /ready, /meta, CI с PostgreSQL.
Frontend 0.9.0 Защищённые маршруты и auth-экраны, экран аккаунта, поиск/фильтры/экспорт истории, нативные поля даты/старта, удобный ввод результатов, дашборд с последними стартами и графиком AG Charts, версия FE/BE в футере.

Правила версионирования и выпуска

  1. BE и FE выпускаются независимо: задачам без серверного контракта не нужен искусственный bump BE.
  2. Патч (x.y.Z) — исправление без изменения публичного контракта; минор (x.Y.0) — новая обратносуместимая возможность; мажор — только при несовместимом API или модели доступа.
  3. Любой релиз с изменением API включает: обновлённый docs/backend-api-for-frontend.md, миграцию (если нужна), тесты контракта и проверку футера с фактическими версиями.
  4. До миграций в production выполнить чек-лист из auth-rollout.md: backup, число стартов до/после миграции и проверка владельца legacy-данных.

План релизов

Очередь BE FE Цель Условие выпуска
P0 1.4.2 0.7.1 Исправить риски текущего релиза и привести контракт в соответствие коду. Нет невалидных записей, SSRF-защита, критические сценарии проверяются на PostgreSQL.
P1 1.6.0 0.9.0 Дать пользователю контроль над аккаунтом и данными, улучшить работу с историей стартов. Управление сессиями и экспорт доступны, фильтры не ломают существующий список.
P2 1.7.0 1.0.0 Подготовка к старту: чек-листы, напоминания и календарная интеграция. Напоминание отправляется не более одного раза, подписка/токен отзываемы.
P3 1.8.0 1.1.0 Сделать персональный календарь эксплуатационно готовым. Восстановление из backup проверено, есть E2E критического пути и аудит доступности.

2.0.0 не планируется заранее: его поводом может быть только подтверждённая потребность в совместном доступе, командах или публичных стартах — это изменит модель владельцев и доступа.

P0 — BE 1.4.2 / FE 0.7.1

Backend 1.4.2

ID Приоритет Задача Результат / критерий приёмки
BE-142-04 P0 Выполнить backend-часть Ponytail-аудита Pipeline поднимает чистый PostgreSQL, применяет все миграции и выполняет минимум CRUD + auth, включая legacy-сценарий миграции 004. Самодельный SQL-эмулятор CALENDAR_RUN_MOCK_DB удалён; тесты работают с этой БД. cross-env удалён, если Windows не входит в список поддерживаемых сред разработки.
BE-142-01 P0 Актуализировать контракт API Документированы id (UUID сервера) и slug (ключ пользователя), обязательная авторизация для /races, CSRF для мутаций, реальный ответ /health, все коды ошибок и формат Race. Старое описание не обещает несуществующий неавторизованный CRUD.
BE-142-02 P0 Ввести строгую валидацию создания и изменения старта Одна схема на POST/PATCH: корректные дата и дистанция, enum статуса, допустимые форматы startTime и finishTime, URL, пределы длины текстов; неизвестные поля не игнорируются. Ошибка возвращает 400 validation_error с полем/причиной.
BE-142-03 P0 Защитить получение обложки от SSRF Автозагрузка принимает только безопасные HTTP(S)-адреса, запрещает loopback/private/link-local адреса и опасные редиректы, ограничивает число редиректов, размер и время ответа. При невозможности получить обложку старт всё равно создаётся без неё. Есть тесты на блокировку.
BE-142-05 P1 Подтвердить достаточность индексов по фактическим запросам Для списка владельца с фильтром года/месяца снять EXPLAIN ANALYZE; индекс (owner_user_id, race_date) добавлять только если он нужен по плану запроса или размеру данных.

Frontend 0.7.1

ID Приоритет Задача Результат / критерий приёмки
FE-071-00 P0 Выполнить frontend-часть Ponytail-аудита Удалены артефакты TypeScript/Vite из Git и добавлены в .gitignore, неиспользуемый getHealth и тип HealthResponse. Кастомные поля даты и времени старта заменены нативными input[type=date] и input[type=time]; каталог названий стартов с внешними картинками удалён, остаются coverImageUrl и локальные fallback-изображения. Создание и обновление старта используют один payload полей.
FE-071-01 P0 Исправить семантику личного рекорда Карточка «Последний личный рекорд» показывается только для результата, который действительно улучшил прошлое лучшее время на сопоставимой дистанции; иначе карточка называется «Последний результат» либо скрывается.
FE-071-02 P0 Валидировать время до отправки Форма не принимает 1:99, отрицательные и произвольные значения; пользователю показана ошибка у поля. Правило идентично серверному, но сервер остаётся источником истины.
FE-071-03 P0 Довести ошибки auth-форм до состояния UI На регистрации, повторной отправке письма, запросе и сбросе пароля есть loading-состояние, обработка ошибки и защита от повторной отправки. Никаких необработанных Promise в браузере.
FE-071-04 P1 Корректно обработать истёкшую сессию и CSRF После 401 пользователь возвращается на вход с сохранением целевого маршрута; после csrf_error интерфейс предлагает однократно обновить сессию и повторить действие, не теряя заполненную форму.
FE-071-05 P0 Завершать подтверждение email понятным экраном успеха После успешной ссылки /verify-email?token=… заголовок явно сообщает, что email подтверждён; ввод email, инструкция и кнопка повторной отправки не показываются. Форма повторной отправки остаётся только для недействительной/отсутствующей ссылки.
FE-071-06 P0 Упростить ввод результата забега Время вводится отдельными цифровыми полями «часы», «минуты», «секунды» без ручного ввода :. Часы — целое число ≥ 0, минуты и секунды — от 0 до 59; пустые минуты/секунды автоматически нормализуются в 0. Итоговое время не показывает незаполненные старшие части: без часов — MM:SS, без часов и минут — SS. Место вводится числами «место» и «всего участников» (второе необязательно), проверяется, что место ≥ 1 и не больше общего числа; в текущий finishPlace без изменения API сохраняется место/всего либо только место. Старые значения finishPlace не теряются до редактирования результата.

P1 — BE 1.6.0 / FE 0.9.0

Backend 1.6.0

ID Приоритет Задача Результат / критерий приёмки
BE-150-01 Готово Управление паролем и сессиями Выполнено в 1.5.0: авторизованный пользователь меняет пароль после проверки текущего; все другие сессии отзываются. Есть список активных сессий с отзывом выбранной и «выйти везде». Токены и пароли в ответах/логах не появляются.
BE-150-02 P1 Удаление аккаунта и данных Явно подтверждённый запрос удаляет аккаунт, его сессии и личные старты транзакционно; повторный запрос идемпотентен. Перед реализацией согласовать необходимость периода восстановления.
BE-150-03 Готово Обратносуместимая фильтрация стартов Добавлены документированные фильтры status, диапазон дат и диапазон дистанции без изменения ответа-массива GET /races, с валидацией параметров.
BE-150-04 Готово Восстановить обложки существующих стартов Добавлен идемпотентный backfill и обновление обложки при изменении официальной страницы. SSRF-защищённая логика переиспользуется; ручные URL не перезаписываются, результат содержит агрегированную статистику.

Frontend 0.9.0

ID Приоритет Задача Результат / критерий приёмки
FE-080-01 Готово Экран «Аккаунт» Выполнено в 0.8.0: пользователь видит подтверждённый email, меняет пароль и видит/отзывает другие сессии. Удаление аккаунта остаётся отдельной задачей BE-150-02.
FE-080-02 Готово Поиск, фильтры и сортировка истории Фильтры по статусу, периоду и дистанции, поиск по названию и сортировка синхронизированы с URL. Годы в селекторе строятся из данных.
FE-080-03 Готово Выгрузка личных данных Из текущей отфильтрованной выборки пользователь скачивает CSV и одноразовый .ics в браузере; сервер не нужен.
FE-080-04 Готово Сделать на дашборде список последних стартов Выполнено в 0.8.0: «Последние старты» показывают до 5 завершённых стартов с клиентской пагинацией, фильтрами по дистанции и году и доступным переходом в старт.
FE-080-05 Готово Связать рекорды по дистанциям со стартами Выполнено в 0.8.0: карточка рекорда с найденным стартом ведёт на /races/:id и использует существующую анимацию связанных карточек.
FE-080-06 Готово Сделать график прогресса информативным и интерактивным Выполнено в 0.8.0: график имеет подписанные оси, компактные подписи, tooltip по наведению/фокусу и доступный переход в старт.
FE-080-07 Готово Вернуть управление официальной страницей старта Поле «Официальная страница старта» доступно для любого старта, включая прошедший; для прошедших скрываются только неактуальные поля расписания.
FE-080-08 Готово Перевести график прогресса на AG Charts Самописный SVG заменён на AG Charts; сохранены данные, оси, tooltip, клавиатурная доступность, переход по точке, адаптивность и пустое состояние.

P2 — BE 1.7.0 / FE 1.0.0

Backend 1.7.0

ID Приоритет Задача Результат / критерий приёмки
BE-160-01 P1 Чек-листы подготовки к старту Новая сущность задач старта: текст, состояние, срок и порядок. Задачи принадлежат тому же пользователю, что и старт; каскадно удаляются вместе со стартом.
BE-160-02 P1 Надёжные email-напоминания Настройки пользователя и очередь напоминаний хранятся в БД; отправка идемпотентна, ошибки ретраятся ограниченно и логируются без PII. Расписание запуска — внешний cron/worker, а не setInterval в каждом web-инстансе.
BE-160-03 P2 Подписка на персональный iCalendar Выдать отдельный отзывной секретный URL только для read-only календаря пользователя. Нужны лимитирование, отсутствие email/заметок в ICS и отзыв/ротация токена.

Frontend 1.0.0

ID Приоритет Задача Результат / критерий приёмки
FE-090-01 P1 Чек-лист в карточке старта Пользователь добавляет, завершает и переставляет задачи подготовки; на дашборде видит ближайшие незавершённые пункты. Пустое состояние объясняет, что делать.
FE-090-02 P1 Настройки напоминаний Опт-ин по email и выбор сроков до старта; понятно отображаются часовой пояс и факт отключения рассылки.
FE-090-03 P2 Управление iCalendar-подпиской Экран показывает URL только по явному действию, даёт скопировать, отозвать и выпустить новый. Одноразовый экспорт из 0.8.0 остаётся доступным без подписки.
FE-090-04 P2 Исправить полную TypeScript-проверку frontend tsc -b проходит без ошибки Cannot find name 'Worker' в типах Vite; конфигурации browser/node остаются корректно разделены, проверка исходников не ослабляется.

P3 — BE 1.8.0 / FE 1.1.0

Backend 1.8.0

ID Приоритет Задача Результат / критерий приёмки
BE-170-01 P0 Эксплуатационный runbook Описаны deploy, миграция, rollback, backup, restore, ротация секретов, SMTP/Turnstile и реакция на недоступную БД. Restore протестирован на копии production-данных.
BE-170-02 P0 Наблюдаемость без утечки данных Структурированные логи с request ID, готовность БД, счётчики ошибок и alert на недоступность. Email, пароли, токены, cookie и текст заметок не логируются.
BE-170-03 P1 Машиночитаемый API-контракт OpenAPI-спецификация синхронизирована с маршрутам и используется в CI для проверки ключевых ответов. Это предотвращает повторное расхождение документации и реализации.

Frontend 1.1.0

ID Приоритет Задача Результат / критерий приёмки
FE-100-01 P0 Критический E2E-путь Автоматически проверяются: регистрация/подтверждение (с тестовым адаптером), вход, создание старта, внесение результата, экспорт и выход. Тест запускается в CI против реального API и PostgreSQL.
FE-100-02 P0 Accessibility и мобильный аудит Полный путь проходится клавиатурой; формы сообщают ошибки screen reader'у, focus не теряется после действий, контраст и touch-targets соответствуют WCAG 2.2 AA. Проверены малый экран и медленное соединение.
FE-100-03 P1 Полировка надёжности UI Единые skeleton/empty/error состояния, retry для безопасных загрузок, защита от двойной отправки мутаций, проверка загрузки внешних обложек и сохранение версии FE/BE в футере.

Зависимости и решения до начала P2

Решение Нужен ответ Почему блокирует
Канал напоминаний Только email или ещё push/мессенджер? Определяет модель согласия, worker и стоимость эксплуатации.
Планировщик Какой внешний cron/worker доступен в production? Нельзя надёжно рассылать напоминания таймером в web-процессе.
Удаление аккаунта Немедленное удаление или 30-дневное восстановление? Меняет схему данных и обязанность хранить архив.
Политика данных Для каких стран и пользователей запускается сервис? До публичного запуска нужны политика конфиденциальности, срок хранения и процесс обращения пользователя.

Явно не берём до подтверждения спроса

  • общий доступ к календарям, клубы, тренеры и публичные профили;
  • поиск и парсинг стартов из сайтов организаторов, кроме уже существующей необязательной обложки;
  • нативные iOS/Android-приложения, платежи и социальная лента;
  • серверную пагинацию: личный календарь пока мал, а изменение формы ответа GET /races без измеренной необходимости только усложнит FE и API.

Рекомендуемый первый спринт

  1. BE-142-04 и FE-071-00: выполнить выводы Ponytail-аудита — удалить самодельную инфраструктуру и использовать возможности платформы.
  2. BE-142-01 и BE-142-02: зафиксировать настоящий контракт и закрыть trust boundary.
  3. BE-142-03: убрать SSRF-риск до любого расширения автозагрузки обложек.
  4. FE-071-01FE-071-06: устранить неверную аналитику и ошибки критических форм, включая понятное завершение подтверждения email и ввод результата.

Выполненный спринт

Цель: пользователь быстро находит старты, восстанавливает данные истории, выгружает результат и видит прогресс в поддерживаемой chart-визуализации.

  1. BE-150-04 + FE-080-07: восстановление обложек существующих стартов и управление официальной страницей, включая прошедшие старты. Ручные обложки не перезаписываются; используется существующая SSRF-защищённая логика.
  2. BE-150-03 + FE-080-02: фильтры по статусу, периоду и дистанции, поиск по названию, сортировка и синхронизация с URL. Обновить API-документацию и контрактные тесты.
  3. FE-080-03: экспорт текущей отфильтрованной выборки в CSV и одноразовый .ics без нового backend и зависимостей.
  4. FE-080-08: заменить самописный SVG-график прогресса на AG Charts. Сохранить текущие данные и метрики, подписи осей, tooltip, клавиатурную доступность, переход по точке в старт, адаптивность и пустое состояние. Не менять API и не добавлять новую обёртку поверх PaceTrendChart.

BE-150-02 в спринт не брать до решения: удаление аккаунта сразу или с 30-дневным восстановлением.