IQ·PLAY ТЗ на MVP
Галерея мокапов ↗

iqplay — Техническое задание на MVP

Версия 2.1 · 2026-08-14

1. Обзор и цели MVP

1.1. Что это

iqplay — веб-приложение для любителей тенниса. Игрок ведёт рейтинг по шкале NTRP, участвует в турнирах и фиксирует результаты матчей с обоюдным подтверждением. Турниры — платформенные и однодневные, их создаёт пользователь с ролью «организатор». Матчи — одиночные и парные. Рейтинг — динамический: система предсказывает исход матча по разнице рейтингов и корректирует оценку по фактическому счёту (полный механизм — §6.4).

1.2. Проблема и аудитория

1.3. Цели MVP

  1. Игрок получает осмысленный NTRP-рейтинг после калибровочных матчей и видит его динамику.
  2. Игрок находит соперников в общей ленте, вызывает на матч, фиксирует одиночные и парные результаты с фото.
  3. Организатор создаёт однодневный турнир (3 формата, одиночный/парный), собирает заявки, назначает пары и проводит его.
  4. Платформенный админ модерирует пользователей и турниры.

1.4. Не цели MVP (см. §11)

Сообщества и гостевой доступ, Double Elimination, отдельный парный рейтинг, push-уведомления, реальные выплаты призов, нативные приложения, многоязычность, формат Round Robin.

2. Роли и матрица прав

Возможность Игрок Организатор Глоб. админ
Регистрация с самооценкой NTRP, профиль, история рейтинга
Запись матча (одиночного/парного) с фото, вызов, подтверждение/оспаривание
Лента платформы (матчи + турниры), каталог турниров
Заявка на участие в турнире
Создание турниров, приём заявок, назначение пар, результаты, споры своих турниров
Выдача/снятие роли «организатор»
Глоб. модерация: люди, турниры, баны, audit-log

Примечания: - Роль «организатор» выдаёт глобальный админ (тумблер на admin/person). - Роли игрока накопительные: «организатор» — тоже игрок. - Глобальный админ работает в отдельном тёмном UI (/admin, см. §5.6), одна роль super-admin, иерархии нет.


3. Глоссарий и ключевые сущности

Термин Значение
NTRP National Tennis Rating Program — шкала 1.0–7.0, шаг 0.5 (отображаемый уровень)
r (internal rating) непрерывная внутренняя оценка силы на оси NTRP (2 знака)
RD (rating deviation) мера неопределённости рейтинга (Glicko-логика)
Калибровка / Подтверждён статусы зрелости рейтинга; стартуют с обязательной самооценки при регистрации (см. §6.4 «Жизненный цикл»)
Турнир однодневное соревнование одного из 3 форматов (playoff/groups/swiss), одиночное или парное
Товарищеский матч матч вне турнира, требует обоюдного подтверждения
Турнирный матч матч внутри турнира, результат проставляет организатор
Вызов (challenge) приглашение сыграть с предложенными датой/местом
Заявка (entry) запрос игрока на участие в турнире, требует одобрения организатора
Пара команда из двух игроков в парном матче/турнире; в турнире состав пары назначает организатор
Санкция (suspension) набор ограничений, наложенный глоб. админом

Полный перечень сущностей данных — в §9.


4. Сквозные сценарии

Сценарии end-to-end, связывающие модули. Каждый — последовательность экранов и системных действий.

4.1. Онбординг и первый рейтинг

Регистрация (auth) → объяснение шкалы и обязательная самооценка уровня (onboarding, сидирует r с макс. RD) → калибровочные матчи → после порога (≈5 матчей, RD ниже порога) статус становится «Подтверждён», показывается NTRP (profile-me).

4.2. Найти соперника и сыграть

profile-other / поиск → «Вызвать» → вызов с датой/местом → соперник принимает/переносит (match-challenge) → согласовано, событие в календаре (calendar-me) → после игры один из игроков фиксирует счёт (match-record) → второй подтверждает или оспаривает (match-confirm) → confirmed → пересчёт рейтинга обоих, запись в историю (rating-history).

4.3. Подать заявку и сыграть турнир

Игрок находит турнир в каталоге/ленте (home, tournament) → «Подать заявку» (tournament-apply) → организатор принимает (tournament-entries) → для парного турнира организатор назначает пары → жеребьёвка → игрок видит сетку и расписание.

4.4. Провести турнир

Организатор → мастер из 4 шагов: тип — одиночный/парный (tournament-create-type) → число участников (tournament-create-count) → формат из трёх — playoff/groups/swiss (tournament-create-format) → параметры: дата (один день), best-of-N, приз, приём заявок (tournament-create-params) → приём заявок (tournament-entries) → [парный: назначение пар] → жеребьёвка (tournament-draw) → сетка → организатор проставляет результаты → победитель.

4.5. Модерация спорного матча

Игрок оспаривает результат товарищеского матча (match-confirm → Disputed) → матч попадает в очередь платформенного админа (/admin, §5.6) → админ решает (засчитать как есть / исправить счёт / аннулировать) → состояние матча и рейтинг обновляются. Споры по турнирным результатам решает организатор турнира в пределах своего турнира.


5. Функциональные требования по модулям

Формат: каждый модуль — список требований FR-<модуль>-<n>. Экраны модуля перечислены; детальные спецификации экранов — в §6.

5.1. Аутентификация и аккаунт

Экраны: auth, onboarding.

5.2. Профиль и NTRP-рейтинг

Экраны: profile-me, profile-other, rating-history, rating-leaderboard.

5.3. Турниры

Экраны: tournament-create-type, tournament-create-count, tournament-create-format, tournament-create-params, tournament, tournament-apply, tournament-entries, tournament-doubles, tournament-draw, tournament-bracket-single, tournament-groups-playoff, tournament-swiss, tournament-calendar.

5.4. Матчи

Экраны: home, matches, match-record, match-confirm, match-invite.

5.5. События и календари

Экраны: calendar-me, tournament-calendar, match-challenge. Обновления: блок «Ближайшее событие» в profile-me, кнопки 📅 в tournament/match-confirm, ссылка в matches.

5.6. Админ-консоль (платформенная)

Экраны: admin/login, admin/dashboard, admin/people, admin/person, admin/communities, admin/community, admin/audit-log, admin/news, admin/suspend. Отдельный тёмный UI, desktop-first.

5.7. Уведомления

6. Карта экранов и спецификации

Шаблон спецификации экрана: файл мокапа · назначение · доступ · ключевые элементы · состояния · действия и переходы · данные (читает/пишет) · валидации. Дизайн каждого экрана уже зафиксирован в мокапах mockups/ — ТЗ описывает поведение и данные, не переопределяя визуал.

Полная карта — 35 экранов (26 мобильных + 9 админских):

# Файл Модуль Назначение Доступ
1 auth.html Auth Вход / Регистрация (табы) Все
2 onboarding.html Auth NTRP-шкала + обязательная самооценка Игрок (новый)
3 home.html Главная Лента платформы + CTA «Записать матч» Игрок
4 profile-me.html Профиль Свой профиль + «Мои матчи» Игрок
5 profile-other.html Профиль Чужой профиль, H2H, вызов Игрок
6 rating-history.html Профиль График NTRP + события Игрок
7 rating-leaderboard.html Профиль Общий рейтинг игроков (одиночный/парный, фильтры) Игрок
8 tournament-create-type.html Турниры Мастер шаг 1: тип (одиночный/парный) Организатор
9 tournament-create-count.html Турниры Мастер шаг 2: число участников Организатор
10 tournament-create-format.html Турниры Мастер шаг 3: формат (playoff/groups/swiss) Организатор
11 tournament-create-params.html Турниры Мастер шаг 4: дата, best-of-N, приз Организатор
12 tournament.html Турниры Обзор турнира + «Подать заявку» Игрок
13 tournament-apply.html Турниры Заявка игрока на турнир Игрок
14 tournament-entries.html Турниры Заявки: приём/отказ + назначение пар Организатор
15 tournament-doubles.html Турниры Парный турнир: пары + сетка пар Игрок
16 tournament-draw.html Турниры Жеребьёвка: посев, пережеребьёвка, фиксация Организатор
17 tournament-bracket-single.html Турниры Сетка: плей-офф + утешительный Игрок
18 tournament-groups-playoff.html Турниры Группы + платина/золото плей-офф Игрок
19 tournament-swiss.html Турниры Швейцарка: таблица очков + пары по турам Игрок
20 tournament-calendar.html События Расписание матчей турнира Игрок
21 matches.html Матчи Полный список матчей Игрок
22 match-record.html Матчи Запись матча: табло, парный, фото Игрок
23 match-confirm.html Матчи Подтверждение / оспаривание + фото Игрок
24 match-invite.html Матчи Создание вызова Игрок
25 match-challenge.html События Принять вызов (3 CTA + drawer) Игрок
26 calendar-me.html События Календарь игрока Игрок
27–35 admin/*.html (9 экранов, вкл. news.html — новости/блог) Админ Как в v1.0 + тумблер «организатор» + блог Глоб. админ

admin/communities.html / admin/community.html — задел под модерацию сообществ (см. роадмап, §11); в MVP не используются.

Ниже — спецификации ключевых экранов. Алгоритм, который экран использует, описан тут же, рядом с экраном — документ самодостаточен. Остальные экраны следуют тому же шаблону; визуал — в мокапах.

6.1. auth.html

6.2. onboarding.html

6.3. profile-me.html / profile-other.html

6.4. rating-history.html + алгоритм рейтинга NTRP

Экран: график r во времени + событийные точки матчей (↑/↓) из RatingEvent, фильтр периода (30 / 90 дней / всё). Тренд = Δr за период, цвет success/danger.

Рейтинг — динамический движок в духе UTR / ITF WTN / USTA dynamic NTRP: перед матчем предсказывает исход по разнице рейтингов, после матча сравнивает фактический счёт с предсказанием и сдвигает рейтинг на величину ошибки, с поправкой на уверенность, свежесть и вес матча. Константы — ориентир, калибруются на реальных данных.

Двухслойная модель. У игрока хранится не одно число, а три:

Величина Что это Диапазон
r — внутренний рейтинг непрерывная оценка силы на оси NTRP, до 2 знаков (напр. 3.62) 1.00 – 7.00
RD — отклонение (uncertainty) насколько система уверена в r (Glicko-2 rating deviation) ~1.0 (новичок) → ~0.12 (устоявшийся)
NTRP — отображаемый уровень r, округлённый до ближайшего 0.5 1.0 – 7.0 шаг 0.5

UI оперирует NTRP; предсказания и пересчёт работают на r; RD управляет скоростью изменений и переходом Калибровка → Подтверждён.

Шкала NTRP (полная). Отображаемый уровень — число из этой шкалы; в UI показывается только число и тренд, без «маркетинговых» названий уровней. Описания ниже — адаптация официальной таблицы USTA NTRP (General Characteristics of Various NTRP Playing Levels); используются в онбординге для самооценки и как справочник. Целевая аудитория iqplay — 2.0–4.5.

NTRP Характеристика игрока
1.0 Только начинает играть в теннис.
1.5 Небольшой опыт; работает в основном над тем, чтобы вводить мяч в игру.
2.0 Нужен игровой опыт; заметные пробелы в ударах, но знаком с базовыми позициями в одиночке и паре.
2.5 Учится оценивать, куда летит мяч (покрытие корта пока слабое); держит короткий медленный розыгрыш с равным соперником.
3.0 Достаточно стабилен на мячах средней силы, но не во всех ударах; не хватает контроля направления, глубины и мощи.
3.5 Улучшилась стабильность и контроль направления на умеренных мячах, но не хватает глубины и разнообразия; агрессивнее у сетки, лучше покрытие корта, развивается взаимодействие в паре.
4.0 Надёжные удары с контролем направления и глубины с обеих сторон; уверенно использует свечи, смэши, укороченные, удары с лёта; подачей иногда вынуждает ошибку; в паре видно взаимодействие.
4.5 Освоил мощь и вращение, справляется с темпом; уверенная работа ног, контроль глубины, меняет тактику под соперника; первая подача с силой и точностью, вторая — с расстановкой.
5.0 Хорошо читает игру, часто имеет коронный удар, вокруг которого строит игру; выигрывает очко или вынуждает ошибку с коротких мячей, уверенно завершает у сетки.
5.5 Мощь и/или стабильность — главное оружие; варьирует тактику и стиль, надёжен в стрессовых ситуациях.
6.0 Прошёл интенсивную подготовку для национальных турниров (юниорский/студенческий уровень); рейтинг NTRP уже не нужен — говорят результаты.
6.5 – 7.0 Игрок международного / профессионального уровня.

Жизненный цикл.

Регистрация ──▶ обязательная самооценка ──▶ Калибровка ──RD ниже порога──▶ Подтверждён
                  сидирует r с макс. RD        (provisional)                     │
                                               быстрый пересчёт            долгий простой
                                               (высокий K)             ─▶ RD растёт ─▶ обратно в provisional
Статус Условие Поведение
Калибровка < 5 матчей / 10 сетов, RD высок NTRP ориентировочный («≈3.5»), задан самооценкой; K большой — рейтинг движется быстро
Подтверждён RD ниже порога стабильный NTRP с трендом
Реактивация простой > ~6 мес. RD подрастает → первые матчи после паузы двигают сильнее

Предсказание (используется здесь, на profile-other, match-challenge, match-record). Из разницы рейтингов — ожидаемая доля геймов игрока A (margin-based):

E_A = 1 / (1 + 10^( −(r_A − r_B) / S ))          S ≈ 1.7 (пунктов NTRP)
Δ = r_A − r_B Ожидаемая доля геймов A ≈ Ожидаемый счёт сета
0.00 50 % 6:6 — равная игра
0.25 58 % ~6:4
0.50 66 % ~6:3
1.00 80 % ~6:2
1.50 88 % ~6:1
2.00 94 % ~6:0

Сравнение и пересчёт. Фактическая доля геймов — посетово (как в WTN, чтобы один «баранкой» взятый сет не перекосил картину):

A_A = среднее по сетам ( геймы_выиграны / геймы_в_сете )
Δr_A = K_eff · w · (A_A − E_A)
r_A ← r_A + Δr_A      r_B ← r_B − Δr_A      (приблизительно нулевая сумма)

Вес матча w ∈ (0,1] — произведение факторов: формат (best-of-3 > короткий сет); соревновательность (разрыв > ~2 ступеней NTRP малоинформативен → вес затухает); надёжность соперника (vs подтверждённого > vs provisional); свежесть (окно ~30 матчей / 12 мес., экспоненц. затухание, полураспад ~3 мес.); повторы соперника (анти-фарм, w_rep — формула в «Целостности» ниже).

Парные матчи. Рейтинг пары = среднее r партнёров; RD пары = среднее RD. Предсказание E считается по разнице рейтингов пар той же формулой. Сдвиг Δr = K_eff · (w × 0.5) · (A − E) делится поровну между партнёрами (и зеркально — у соперников). Пониженный вес ×0.5 отражает меньшую информативность парного счёта для индивидуальной силы. Отдельного парного рейтинга в MVP нет.

Что влияет: счёт по геймам/сетам и рейтинги соперников до матча. Не влияет напрямую: факт «победа/поражение» сам по себе (важна разница с ожиданием), возраст, место в таблице. Товарищеский учитывается только после обоюдного подтверждения (pending не считается, disputed заморожен); турнирный — сразу; незавершённые — только доигранные сеты; парные — с весом ×0.5 (см. «Парные матчи»).

Триггеры пересчёта. Переход матча в confirmed (товарищеский) или проставление турнирного результата организатором → вычислить E и A, обновить r/RD обоих игроков (для парного — обоих партнёров с обеих сторон, см. «Парные матчи»), записать RatingEvent (before/after, expected, actual, weight) для истории. Базово — инкрементально в момент подтверждения; полный пересчёт (после исправления/аннулирования организатором) — фоновая задача, перепроигрывающая RatingEvent хронологически. Рост RD при простое — ночной джоб.

Отображение и тренд. NTRP = ближайшие 0.5 от r; у самой границы — подсказка «почти 4.0». Тренд — Δr за период, цвет success/danger.

Целостность (anti-abuse). Результаты против только что зарегистрированного соперника (RD на максимуме, ещё не откалиброван) весят ≈0; большой разрыв между отображаемым и динамическим рейтингом — флаг на ревью админа (сэндбэггинг/буст). Два предохранителя заданы формулами — они особенно важны в калибровке, где K_eff велик и рейтинг двигается быстро:

Константы (ориентир, калибруется на реальных данных): S ≈ 1.7; K_base ≈ 0.25 → 0.06 по RD; RD₀ ≈ 1.0 / RD_floor ≈ 0.12; порог подтверждения ≈ 5 матчей / 10 сетов и RD < порога; окно свежести ~30 матчей / 12 мес., полураспад ~3 мес.; анти-фарм w_rep = max(0.5^(n−1), 0.1), окно повторов 30 дней; дисконт разгромов T ≈ 0.25, β ≈ 0.5; кап сдвига |Δr| ≤ 0.15 (калибровка) / 0.05 (подтверждён).

Методология-ориентир: UTR · ITF WTN · USTA dynamic NTRP · Elo.

6.5. match-record.html

6.6. match-confirm.html + жизненный цикл матча

Жизненный цикл матча:

Товарищеский:
  created ──отправка──▶ pending ──подтверждение B──▶ confirmed ──▶ [пересчёт §6.4]
                          │
                          └──оспаривание B──▶ disputed ──решение платформ. админа──▶ confirmed | corrected | void

Турнирный:
  created ──результат от организатора──▶ confirmed ──▶ [пересчёт §6.4, прогресс сетки §6.7]

6.7. Турниры: создание, сетки, посев и прогресс

Экраны: tournament-create-type (шаг 1: одиночный/парный); tournament-create-count (шаг 2: число участников); tournament-create-format (шаг 3: формат из трёх — playoff/groups/swiss — с инлайн-настройкой); tournament-create-params (шаг 4: дата (один день), best-of-N, призовой фонд, приём заявок; валидации: число участников ≥ минимума формата, дата в будущем, призовой фонд необязателен); tournament-apply, tournament-entries, tournament-doubles, tournament-draw, tournament-bracket-single, tournament-groups-playoff, tournament-swiss. Создание → Tournament(status=draft/registration).

Однодневный турнир: одна дата, расписание внутри дня.

Посев для всех форматов — по внутреннему рейтингу r на момент жеребьёвки (выше r → выше сид; для парного турнира — рейтинг пары, см. §6.4 «Парные матчи»).

Жеребьёвка. Организатор запускает после приёма заявок. Посев детерминирован рейтингом r (для парного — рейтинг пары). «Пережеребьёвка» переставляет только игроков с равным r (случайный тай-брейк). «Зафиксировать» лочит жеребьёвку (drawLocked=true) и генерирует структуру формата; турнир → active.

Плей-офф + утешительный. Дополнить до ближайшей степени двойки byes (топ-сидам); расстановка 1–N, 2–(N−1), …. Проигравшие 1-го круга формируют утешительную single-elim; победитель утешительной берёт (N/2+1)-е место.

Группы → платина/золото. Сиды по группам «змейкой» (размер groupSize ∈ {2,3,4}), круговой внутри группы. По итогам: топ-2 каждой группы → платиновый плей-офф (за чемпионство), остальные → золотой плей-офф. В обеих ветках — single-elim с перекрёстным посевом (1-е место группы против 2-го другой).

Швейцарская система. Туров R = ⌈log₂N⌉. Тур 1 — посев рейтингом, верхняя половина против нижней (сид i против сида i+N/2). Туры 2+: игроки в очковых группах (победа 1, поражение 0, бай 1), внутри группы верх×низ по рейтингу; запрет повторных встреч (при коллизии — обмен со следующим игроком); нижний из нечётной группы «проваливается» (float) в следующую. Нечётное N → нижний без предыдущего бая получает бай (1 очко). Правку пар тура организатор может внести вручную до старта тура (FR-TOUR-12).

Автоперевод (прогресс). confirmed турнирный матч → победитель продвигается в TournamentMatch.nextSlot основной/платиновой ветки; проигравший 1-го круга playoff → утешительная; проигравшие групп по правилу распределения → золотой плей-офф. Для swiss — по завершении тура пересчитываются очки и генерируется следующий тур.

6.8. tournament-groups-playoff.html / tournament-swiss.html + standings и тай-брейки

6.9. admin/suspend.html (drawer)


7. Доступ: видимость и права

Сквозное правило для всех модулей. Функции canSee(user, resource) и canDo(user, action, resource): - Гостевого доступа нет — все действия требуют аккаунта. - Турнир: платформенный, каталог виден всем зарегистрированным. - Действия организатора — только в пределах своих турниров (создание, приём заявок, назначение пар, результаты, разбор споров). - Санкции (§5.6) накладывают запреты поверх ролевых прав (например, cannot_record_matches блокирует FR-MATCH-2, cannot_create_tournaments — создание турниров).

Алгоритмы (рейтинг NTRP, генерация сеток и посев, standings и тай-брейки, жизненный цикл матча) описаны в §6 — рядом с экранами, которые их используют: рейтинг — §6.4, жизненный цикл матча — §6.6, сетки/посев/прогресс — §6.7, standings — §6.8.


8. Архитектура (high-level)

[ PWA-клиент: Next.js (App Router), React ]
        │  HTTPS / cookie-сессия
        ▼
[ API: Next.js Route Handlers / server actions (монолит) ]
        │
        ├─▶ [ PostgreSQL ] ── Prisma ORM
        ├─▶ [ Очередь задач: BullMQ + Redis ] ── пересчёт рейтинга, прогресс турнира, рассылка, .ics
        ├─▶ [ Объектное хранилище S3-совместимое ] ── аватары, фото матчей
        ├─▶ [ Telegram OAuth ] ── вход
        └─▶ [ Транзакционная почта (SMTP/Resend) ] ── регистрация, сброс пароля

9. Модель данных

Сущности и ключевые поля (без полного DDL; финальные схемы — на этапе реализации через Prisma).

Сущность Ключевые поля Связи
User id, email, passwordHash, telegramId?, name, avatarUrl, isOrganizer(bool), ntrpStatus(calibrating/rated), selfAssessedNtrp, r, RD, createdAt, status(active/suspended/deleted) 1–N RatingEvent
RatingEvent id, userId, matchId, rBefore, rAfter, rdBefore, rdAfter, expected, actual, weight, createdAt N–1 User, N–1 Match
Match id, type(friendly/tournament), tournamentId?, tournamentMatchId?, isDoubles(bool), playerAId, playerA2Id?, playerBId, playerB2Id?, winnerId?, status(pending/confirmed/disputed/void), format(bestOfN), venue?, playedAt, createdBy, confirmedAt?, photos[] (до 3, URL) 1–N MatchSet, 1–N RatingEvent
MatchSet id, matchId, setIndex, gamesA, gamesB, tiebreakA?, tiebreakB? N–1 Match
Tournament id, name, format(playoff/groups/swiss), isDoubles(bool), params{slots, matchFormat, prize, date}, groupSize?, swissRounds?, drawLocked(bool), status(draft/registration/active/completed), createdBy 1–N TournamentEntry, 1–N TournamentMatch, 1–N Group, 1–N Pair
TournamentEntry id, tournamentId, userId, seed?, groupId?, pairId?, status(applied/accepted/declined) N–1 Tournament, N–1 User
Pair id, tournamentId, player1Id, player2Id, seed? N–1 Tournament
TournamentMatch id, tournamentId, bracket(main/consolation/group/platinum/gold/swiss), round, slot, entryAId?, entryBId?, matchId?, nextSlot?, nextSlotLoser? N–1 Tournament, 0–1 Match
Group id, tournamentId, name 1–N TournamentEntry
MatchInvite id, fromUserId, toUserId, proposedAt, venue?, type, note?, status(pending/accepted/declined/countered), counterProposedAt? N–1 User×2
Notification id, userId, type, payload(json), createdAt, readAt? N–1 User
Suspension id, targetType(user), targetId, restrictions[] (включая cannot_create_tournaments), duration(until/indefinite), endsAt?, category, comment, internalNote?, createdBy, createdAt, liftedAt? N–1 User
AuditLogEntry id, actorId, action, targetType, targetId, meta(json), createdAt N–1 admin User
NewsPost id, title, body, imageUrl?, kind(news/ad), externalUrl? (required при kind=ad), status(draft/published/scheduled), pinned(bool), publishAt?, createdBy, createdAt N–1 admin User

Производные представления: CalendarEvent (агрегат из MatchInvite, Match, TournamentMatch, турнирных вех — вычисляется, не отдельная таблица).


10. Нефункциональные требования

Категория Требование
Производительность P95 загрузки ключевых экранов < 2.5 c на 4G; критичные данные — server-render; lazy-load тяжёлых списков (лента, таблицы)
Безопасность пароли — argon2/bcrypt; CSRF-защита; rate-limit на auth и запись матчей; авторизация (canDo) проверяется на сервере на каждом мутирующем запросе; валидация всего ввода
Приватность персональные данные минимизированы; согласие при регистрации; hidden_from_search уважается
Локализация UI — русский; даты/время — MSK; код готов к i18n (строки вынесены), но многоязычность вне MVP
Адаптив mobile-first (база 420px), корректный таблет/десктоп; админка — desktop-first (1280+)
Доступность (a11y) контраст по WCAG AA, видимый фокус, клавиатурная навигация, semantic HTML, tap-target ≥ 44px
PWA устанавливаемость, offline-оболочка, иконки/манифест
Аналитика ключевые события: регистрация, первый матч, подтверждение, создание турнира, активация; дашборд продуктовых метрик
Наблюдаемость логирование ошибок сервера, трассировка фоновых задач рейтинга

11. Вне скоупа MVP / роадмап


12. Открытые вопросы

  1. Калибровочные константы NTRP (S, K_base, пороги RD, окно свежести) — заданы как ориентир в §6.4 («Константы»); требуют подбора на реальных данных после набора матчей.
  2. Источник первичного рейтинга: только самооценка + калибровка, или импорт внешнего (UTR/WTN) на старте? (MVP — самооценка + калибровка.)
  3. Granularity уведомлений: какие события обязательны в MVP vs можно отложить за пределы in-app.
  4. Правила досрочного завершения турнира (снятие игрока, walkover) — детализировать при реализации турниров.
  5. Вес парных матчей ×0.5 — калибруется на данных.
  6. Пороги анти-фарма и дисконта разгромов (w_rep, окно повторов, T, β, капы |Δr|) — заданы как ориентир в §6.4 («Целостность»); калибруются на реальных данных вместе с остальными константами.