VEU.WathBody/1-docs/1-reqs/requirements.md

610 lines
55 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Функциональные требования: Феникс 6X v1
> Шаг 3 процесса разработки (`std1\_process\process.md`).
> Источник: [vision.md](vision.md) (утверждён 2026-10-17, v1.5), разделы
> 1.3 (БЦ), 1.4 (КУ), 2.1–2.4; решения опросников 4.1–4.7; ТЗ
> (`..\..\Техническое задание.md`), дизайн LLM-планирования (разд. 2–3,
> 6–8, 11 — шаг 1); изменение vision 1.1 (2026-10-17) — несколько
> атлетов на сервере, разные модели часов.
> Версия 1.1 — **УТВЕРЖДЕНО** (2026-10-17; изменения vision 1.1, 1.2
> учтены; утверждение — 2026-10-17). Изменение 1 (2026-10-17, указание
> пользователя): интегрирован документ
> `Метрики_тренировок_и_самоконтроля.md` — добавлена Ф-13, правила
> Ф-12 расширены (vision 1.3, БЦ-6, КУ-7).
> Версия 1.2 — черновик (2026-10-17): errata ревью
> (`0-mng\review-2026-10-17.md`, З-9–З-11) + интеграция документа
> `Перерывы_подводные_камни_и_рекомендации.md` — Ф-14 (изменение
> vision 1.4).
> Версия 1.3 — черновик (2026-10-17): волна 2 ревью по решениям
> владельца — Р1-А (пульс покоя/самочувствие в v1), Р2-Б (UUID как PK
> клиентских сущностей), Р3-А (Safety-пороги на телефоне); З-7 (RPE
> 1–10), З-8 (команда «РПЭ N»); интеграция UI/UX-рекомендаций
> (`3-tech\ui-ux-recommendations.md`). Версии 1.2, 1.3 —
> **УТВЕРЖДЕНЫ** (2026-10-17).
> Версия 1.4 — **УТВЕРЖДЕНО** (2026-10-17): ревью №2
> (`0-mng\review-2026-10-17-2.md`) — контур break (З-17), якорь Fenix 6X
> (З-18, изменение vision 1.5), ОВ-10 закрыт (З-19, решение владельца),
> `Safety.HrStrengthMax` (З-20), грамматика «…с половиной» (З-26, решение
> владельца О-2-Б), `Progression.IsoStepKg` (З-27, решение владельца
> О-3-А), режим `--mode migrate` (З-25). Правки ревью №3
> (`0-mng\review-2026-10-17-3.md`): Ф-13.7 — источник не программа
> (З-30), Ф-8.3 — импорт пакета программы (З-31, О-4-Б), вопрос 12.
## 1. Назначение и границы
Версия 1.0 реализует полный цикл «программа → тренировка без рук →
архив → план/факт» (Ф-1–Ф-11, правила прогрессии Ф-12):
- **В границах:** голосовой офлайн-учёт подходов на Android-телефоне;
поле данных на Fenix 6X в профиле «Силовая» (пульс, зоны, Lap);
офлайн-учёт с автосинхронизацией телефон ↔ сервер; забор .fit
из Garmin Connect неофициальным клиентом и парсинг на сервере;
REST API со статическим ключом; веб-история с CSV; программы
(версионированный JSON) с контролем «план/факт» и правилами
прогрессии (без LLM); поддержка нескольких атлетов на сервере —
у каждого свои программы, профиль и данные (изменение vision 1.1);
поддержка разных моделей часов Garmin (мультитаргет-сборка одного
поля данных); самоконтроль и допуск к тренировке: дневник телеметрии,
предстартовые пороги АД/пульса, RPE/боль по сетам, правила
координации 1–6 (Ф-13, изменение 1).
- **Вне границ v1:** LLM-планирование — еженедельный контур (v2,
опросник 4.6), мезоциклы и «генератор+ревьюер» (v2.x); wake-word
«Феникс, записывай» (v2, опросник 4.5); iOS (v2, опросник 4.2);
экспорт FIT из приложения и доставка программ на часы (v2);
трековые виды спорта — бег/вело (v3+, опросник 4.7); полная
автоматическая смена программы без подтверждения (vision 2.4);
саморегистрация атлетов и публичный доступ (vision 2.4, изменение
1.1); контрольные тесты (Руфье, Штанге—Генчи, тест ходьбы) и полная
утренняя телеметрия с правилами 7–10 (v2/v3 — документ метрик, п. 5).
## 2. Состав системы, точки входа и режимы
Система — три компонента. Каждый работает автономно от остальных
(vision 2.2, «независимость частей»). Клиентская часть одноатлетная
(изменение vision 1.1): один телефон + одни часы принадлежат одному
атлету; множество атлетов поддерживается на сервере — каждый своим
API-ключом.
### 2.1. Часы — поле данных «Феникс» (Monkey C, Connect IQ)
Точка входа: добавление поля данных в штатный профиль «Силовая»
(решение — опросник 4.1). Отдельных режимов нет: поле живёт в течение
активности. Кнопки не перехватываются (Lap и Start/Stop обрабатывает
Garmin штатно — Ф-2.5, Ф-2.6). Код поля данных переносимый: сборки под
разные модели Garmin — конфигурациями одного проекта (разметка под
размеры/разрешения экранов); перечень моделей — открытый вопрос 8.
Якорное устройство — Fenix 6X (уточнение владельца 2026-10-17:
в ТЗ указан Fenix 6/260×260 — описка исходника; изменение vision 1.5).
### 2.2. Телефон — приложение «Феникс» (Android, Kotlin)
Точка входа: значок приложения. Режимы — экраны: «Главная», «Активная
тренировка», «Библиотека», «История», «Программа», «Профиль»,
«Настройки». Работает офлайн целиком; сеть нужна только для
синхронизации (Ф-7).
### 2.3. Сервер — «Феникс.Server» (C# / .NET 10, PostgreSQL)
Единая точка входа: CLI `--mode serve|import|fetch`.
| Параметр | Обязательный | Умолчание | Семантика |
|---|---|---|---|
| `--mode serve` | нет | этот режим | REST API + веб-интерфейс |
| `--mode import` | нет | выкл | разовый импорт .fit/TCX/GPX из папки импорта и выход |
| `--mode migrate` | нет | выкл | применение миграций из `2-db\migrations\` и выход (журнал — `src.migration`); коды возврата — как у import |
| `--mode fetch` | нет | выкл | разовый забор .fit из Garmin Connect и выход (расписание — внешний планировщик, открытый вопрос 4) |
| `--config` | нет | `appsettings.json` рядом с исполняемым файлом | путь к файлу настроек |
**Коды возврата (`--mode import|fetch`):** `0` — успех (в т.ч. «новых
файлов нет»); `1` — авария конфигурации; `2` — частичный сбой (часть
файлов не обработана, детали в логе).
## 3. Настройки (конфигурация)
🛡 Все пути, адреса и секреты настраиваемые — не зашиты в код.
**Телефон (`appsettings`/Preferences):**
| Параметр | Семантика | Умолчание |
|---|---|---|
| `Server.BaseUrl` | адрес сервера для синхронизации | — (обязательный) |
| `Server.ApiKey` | статический API-ключ | — (обязательный) |
| `Sync.WifiOnly` | синхронизация только по Wi-Fi: `1`/`0` | `0` |
| `Hr.MaxBpm` | максимальная ЧСС для расчёта зон; пусто — расчёт по формуле (открытый вопрос 1) | пусто |
| `Voice.Engine` | офлайн-движок распознавания (фиксируется шагом 5, открытый вопрос 2) | шаг 5 |
| `Voice.Language` | язык команд | `ru` |
| `Rest.DefaultSec` | отдых по умолчанию, если не задан командой | `120` |
| `Safety.BpCancel` | порог отмены тренировки по АД (сист./диаст.) | `150/90` |
| `Safety.BpCardio` | порог «только кардио» по АД | `140/90` |
| `Safety.RestingHrSitting` | порог пульса сидя для переноса тренировки | `90` |
| `Safety.HrStrengthMax` | порог пульса в силовой части (предупреждение) | `120` |
| `Safety.LightWeekHrRise` | рост пульса покоя (2 дня) для лёгкой недели | `+8` |
**Сервер (`appsettings.json`):**
| Параметр | Семантика | Умолчание |
|---|---|---|
| `Api.Key` | статический API-ключ по умолчанию (владелец) | — (обязательный) |
| `Api.Athletes` | список атлетов: `{ name, apiKey }` — ключ идентифицирует атлета (изменение vision 1.1; управление — через веб-консоль, Ф-9.1) | — (минимум владелец) |
| `Admin.Login / Admin.PasswordHash` | учётные данные владельца для входа в веб-консоль (пароль — только хэш, изменение vision 1.2) | — (обязательные) |
| `Api.Port` | порт HTTPS | `8443` |
| `Db.ConnectionString` | подключение к PostgreSQL | — (обязательный) |
| `Garmin.Login / Garmin.Password` | учётная запись Garmin Connect (решение — опросник 4.3) | — (обязателен для `fetch`) |
| `Garmin.DownloadDir` | папка хранения скачанных .fit | `data\fit` |
| `Import.Dir` | папка импорта fallback (ручной экспорт, опросник 4.3) | `data\import` |
| `Import.ArchiveDir` | папка обработанных файлов импорта | `data\import_done` |
| `Progression.BaseStepKg` | шаг прогрессии веса, кг | `2.5` |
| `Progression.IsoStepKg` | шаг прогрессии изоляции, кг (умолчание 1,0; валидация 0,25–5 — коридор метрик «+0,5–1 кг»; решение владельца О-3-А) | `1.0` |
| `Progression.BackoffPercent` | снижение при недовыполнении, % | `5` |
| `Deload.MaxWeeks` | недель между делоадами | `6` |
| `Safety.BpCancel` | порог отмены тренировки по АД (сист./диаст.) | `150/90` |
| `Safety.BpCardio` | порог «только кардио» по АД | `140/90` |
| `Safety.RestingHrSitting` | порог пульса сидя для переноса тренировки | `90` |
| `Safety.HrStrengthMax` | порог пульса в силовой части (для `red_flags`) | `120` |
| `Safety.LightWeekHrRise` | рост пульса покоя (2 дня) для лёгкой недели | `+8` |
| `Log.Path / Log.Level` | логи | `logs\`, `Info` |
🛡 Пароль Garmin и API-ключ — только в настройках, не в коде; логи их
не содержат (З-2, З-6).
## 4. Функциональные требования
### Ф-1. Голосовой учёт подходов (телефон)
1. Активация — кнопкой микрофона на экране активной тренировки
(решение — опросник 4.5); повторное нажатие выключает прослушивание.
Пока экран активной тренировки открыт, распознавание работает
офлайн, без сети.
2. Грамматика команд (голос → семантика):
- «Подход **W** на **R**» → подход: вес `W` кг × `R` повторений
(вес — целые кг либо «…с половиной» → +0,5 кг, шаг 0,5;
`W` — 0–500, `R` — 1–100; решение владельца О-2-Б);
- «Вес **W**» → рабочий вес `W` кг для следующих подходов;
- «Отдых **S** (секунд)» → таймер отдыха `S` секунд (5–600);
- «РПЭ **N**» → RPE последнего подхода, N 1–10 (Ф-13.3, З-8).
3. Команда вне грамматики или с числом вне диапазона — отклоняется:
короткий сигнал ошибки, состояние не меняется, в лог пишется
распознанный текст.
4. Принятая команда: сохраняется в текущую тренировку, проигрывается
короткий «щелчок» и голосовое подтверждение («Принято, подход
120 на 8» — TTS, офлайн).
5. 🛡 Каждая команда фиксируется в журнале команд: исходный
распознанный текст, семантика, время (секунды от начала тренировки)
— журнал нужен для сопоставления с .fit (Ф-7.6) и не редактируется.
6. Правка записанного подхода — не более 2 касаний (тап по подходу →
правка веса/повторов), КУ-1.
### Ф-2. Поле данных на часах (Monkey C)
1. Метрики на экране: текущий пульс (шрифт 40–48 pt), средний пульс
текущего подхода (от начала подхода до текущего момента), время
текущего подхода (под нагрузкой).
2. Зона пульса — цвет ободка/фона: синий < 50 %, зелёный 50–70 %,
жёлтый 70–85 %, красный > 85 % от `Hr.MaxBpm`; границы — из настроек
поля данных (Connect IQ settings; автоматическая передача с телефона
— открытый вопрос 3).
3. Границы подхода: поле считает подход открытым от штатного события
нового подхода (Strength Set) до события Lap; метрики подхода
обнуляются на границе.
4. Вибрация: однократная лёгкая — конец автономного таймера отдыха
поля (длительность — настройка поля в Garmin Connect Mobile,
умолчание `Rest.DefaultSec`); двойная — выход пульса за красную
зону. Передача длительности с телефона — v2 (errata ОВ-3 от
2026-10-19: канал часов односторонний, часы → телефон); часы не
блокируются (не зависимость от телефона).
5. Кнопка Lap — штатная: фиксирует конец подхода/круга в записи
активности (требование — метки Lap попадают в .fit, КУ-2).
6. Кнопка Start/Stop — штатная пауза/стоп активности; поле не требует
подтверждений и не блокирует штатное сохранение тренировки.
7. 🛡 Поле не пишет в сеть и не хранит историю: только отображение;
запись ведёт сама платформа Garmin.
8. Разметка экрана — на ресурсы макета по семействам разрешений;
функциональное поведение (Ф-2.1–Ф-2.7) одинаково на всех целевых
моделях; функциональность, отсутствующая на модели (например,
вибрация), молча пропускается — поле не падает (изменение vision
1.1).
### Ф-3. Экран активной тренировки (телефон)
1. Статусная строка: связь с часами (зелёный/красный/серый — серый =
связь недоступна, приложение не блокируется), заряд часов (если
передаётся), статус сети (онлайн/офлайн).
2. Пульс: крупное текущее значение + линия за последний подход;
источник — штатная трансляция пульса часов «Broadcast Heart
Rate», приём по BLE Heart Rate Service (errata ОВ-3 от
2026-10-19: companion-канал Connect IQ на fenix 6X недоступен
для полей данных, CIQ ≤ 4.1.7; транспорт платформо-независимый —
любые часы со стандартной трансляцией). Если связи нет —
прочерк; запись голосом работает (Ф-1.1).
3. Таймер отдыха: обратный отсчёт, фон — красный (идёт отдых),
жёлтый (последние 10 с), зелёный (время начинать); старт — командой
«Отдых S» или кнопкой (умолчание `Rest.DefaultSec`).
4. Блок упражнения: текущее упражнение (из активной программы, Ф-11.2)
и текущий рабочий вес.
5. Кнопки нижней панели: микрофон (Ф-1.1), «Сбросить» (отмена текущей
тренировки с подтверждением — тренировка помечается `cancelled`
и сохраняется в историю).
6. На экране одновременно ≤ 4 активных элемента, тёмная тема,
крупные зоны касания (vision 2.2); UI/UX-стандарт
(`3-tech\ui-ux-recommendations.md`): шрифт ≥ 16 pt, контраст WCAG AA,
цели касания ≥ 44 pt, действия одной руки — в нижней трети экрана;
числовой ввод — пикеры с шагом 2,5 кг (не клавиатура), значение по
умолчанию — последний использованный вес; цветовые сигналы
дублируются текстом/иконкой (не только цветом).
### Ф-4. Библиотека упражнений (телефон, синхронизируется на сервер)
1. Сущность «Упражнение»: название (уникальное), двигательная группа
(жим/тяга/ноги/… — из фиксированного справочника, нужна для правил
замены и тоннажа), признак «базовое/изоляция».
2. Операции: добавить, переименовать, удалить. 🛡 Удаление упражнения,
на которое ссылаются программа или тренировки, — только логическое
(`is_active=0`): история остаётся читаемой.
3. 🛡 Идентификаторы упражнений (`exercise_id`) неизменяемы: программы
и факты ссылаются по ID (дизайн LLM-планирования, разд. 2).
4. 🛡 Библиотека принадлежит атлету (изоляция, изменение vision 1.1):
упражнения другого атлета недоступны; общий справочник на всех —
кандидат v2 (открытый вопрос 11).
### Ф-5. История тренировок (телефон)
1. Список завершённых тренировок: дата/время, длительность, средний
пульс, число подходов, названия упражнений.
2. Карточка тренировки: список подходов (упражнение, вес × повторения),
пометки источника («голос», «часы», «правка»), средний пульс
по подходам.
3. Офлайн список строится из локальной БД (без сети).
### Ф-6. Профиль и зоны пульса (телефон)
1. Поля профиля: пол, возраст, вес (ввод однократно, редактируется);
хранение — локально, синхронизируется на сервер (`athlete_state`).
2. Зоны: 50/70/85 % от `Hr.MaxBpm` (п. 3); расчёт по умолчанию —
формула максимальной ЧСС (открытый вопрос 1).
3. Зоны используются телефоном (график, статусы) и передаются на часы
в объёме, доступном каналу (открытый вопрос 3).
### Ф-7. Офлайн-учёт и синхронизация (телефон ↔ сервер)
1. 🛡 Все данные сначала пишутся в локальную БД телефона; сеть не стоит
на пути записи (аналог КУ-3).
2. Очередь `sync_pending`: каждая новая/изменённая сущность получает
`client_guid` (UUID v4, генерирует телефон) и попадает в очередь;
порядок — по времени создания; отправка — пакетами ≤ 50 записей.
🛡 Атлета определяет API-ключ: все записи пакета пишутся на сервере
от его имени; телефон не передаёт идентификатор атлета в данных.
🛡 Каждая клиентская сущность идентифицируется UUID — первичным
ключом и на телефоне, и на сервере (Р2-Б); UUID генерирует телефон
(строки «только .fit» — сервер); ссылки дочерних записей — по тем же
UUID, отдельный маппинг серверных id не нужен.
3. Отправка: `POST /api/v1/sync` (пакет записей + `client_guid` каждой);
успех записи на сервере подтверждается ответом (принятые GUID);
подтверждённые удаляются из очереди. Неудача сети — тихий баннер
«Синхронизация временно недоступна», повтор по восстановлению сети
(и по `Sync.WifiOnly`).
4. 🛡 Идемпотентность: сервер считает запись по `client_guid` — повтор
пакета не создаёт дублей (КУ-3); ответ идемпотентен.
5. После первой успешной синхронизации — тихий баннер «Данные успешно
синхронизированы».
6. Восстановление связи с часами: телефон запрашивает пропущенный
период пульса (в объёме возможностей канала — открытый вопрос 3)
и сопоставляет его с локально записанными подходами по времени
(границы подхода из журнала команд Ф-1.5); пульс, не попавший
в окно подхода, к подходам не относится.
7. Конфликтов правок нет: один пользователь, единственный источник
правки — телефон; сервер не меняет факт-записи (🛡).
### Ф-8. Сервер: забор и парсинг .fit, REST API
1. `--mode fetch` / расписание: неофициальный клиент Garmin Connect под
учётной записью владельца (решение — опросник 4.3) скачивает .fit
новых силовых тренировок в `Garmin.DownloadDir` (список уже
скачанных — по `fit_file_id` в БД; повторный запуск не качает
дубли — 🛡).
2. Парсинг .fit (Garmin FIT SDK): извлекаются `Session` (тип
`strength_training`, время), `Lap` (границы кругов/подходов),
`Record` (дискретный пульс), `Set` (повторения/вес, если есть);
результат — канонический JSON тренировки в `workout_facts`
(дизайн LLM-планирования, разд. 4).
3. Импорт (`--mode import`): файлы из `Import.Dir` — .fit, .tcx,
.gpx (по документу форматов) и пакет программы
`program-import.json` (формат — `3-tech\program-import-format.md`,
закрытый вопрос 12): пакет маппится на `src.program`/
`src.program_version.body` по `external_id`; повторный импорт
пакета с тем же `external_id` — новая версия `program_version`.
Файлы и пакеты парсятся тем же конвейером; обработанные
перемещаются в `Import.ArchiveDir`; необработанные остаются
с записью в логе (код возврата 2). Секция `profile` пакета
в v1 не переносится (Safety-порогами управляет только конфиг
`Safety.*`).
4. Сопоставление факт ↔ голосовой учёт: тренировка .fit сопоставляется
с тренировкой телефона по времени начала (± 15 минут) —
сопоставлённая пара связывается; несопоставленные остаются
раздельными (сопоставление не удаляет и не переписывает данные
— 🛡).
5. REST API (HTTPS, заголовок `X-Api-Key` = ключ атлета; без ключа или
с чужим ключом — `401`): `POST /api/v1/sync` (Ф-7.3);
`GET /api/v1/workouts` (список с пагинацией); `GET /api/v1/workouts/{id}`
(карточка); `GET /api/v1/programs/active` (активная версия программы).
🛡 Каждой запрос видит данные только своего атлета (изоляция,
изменение vision 1.1).
6. 🛡 API пишет факт-данные только из sync и импорта; ручного ввода
на сервере нет.
### Ф-9. Веб-интерфейс сервера — административная консоль владельца
🛡 Доступ в веб — только для владельца, вход по логину и паролю
(`Admin.Login` / `Admin.PasswordHash` из настроек; открытый вопрос 6
закрыт); атлеты работают только через телефон (изменение vision 1.2).
Разделы консоли:
1. **Атлеты** (управление, открытый вопрос 9 закрыт этим решением):
список, добавление атлета (имя + генерация API-ключа), смена ключа,
блокировка (заблокированный ключ → `401` на API).
2. **Тренировки**: таблица по всем атлетам с фильтром по атлету и дате
(дата, длительность, средний пульс, подходы, упражнений), сортировка,
пагинация; карточка тренировки: подходы, отчёт план/факт (Ф-11.4);
**ход тренировки** — таймлайн из .fit (границы подходов по Lap,
дискретный пульс по Record): начало/конец и длительность каждого
подхода, средний и пиковый пульс подхода, отдых после подхода,
пульсограмма за всю тренировку; выгрузка CSV текущей выборки (КУ-4).
3. **Программы**: выбор атлета → список версий (статусы, автор, даты);
создание и правка версий `draft`; смена активной версии (Ф-10.3);
просмотр предложений правил (Ф-12.3) — применение предложения
из консоли помечает версию `author=admin`.
4. **Метрики и динамика**: по атлету и периоду — adherence, тоннаж
по группам, динамика 1ПМ и рабочего веса по упражнениям (графики
по неделям из `workout_metrics`); у каждой тренировки карточки —
место в тренде (лучше/хуже предыдущей).
5. **Планы тренировок** (план ↔ факт): расписание активной версии
программы атлета по неделям и дням; по каждой запланированной
тренировке — планируемые результаты (упражнения, подходы, вес ×
повторения, целевой объём) против фактических (выполнена/пропущена,
подходы, веса, тоннаж, adherence); сводка выполнения плана за неделю
и мезоцикл (доля запланированных/выполненных/пропущенных).
### Ф-10. Программы тренировок (сервер + телефон)
1. Программа — версионированный JSON-документ (схема — дизайн
LLM-планирования, разд. 2): недели → тренировки (день, фокус) →
упражнения (`exercise_id` из библиотеки) → подходы-план (вес,
повторения, `target_rpe`, отдых). Хранение — таблица `programs`
(PostgreSQL); каждая версия неизменяема (🛡). 🛡 Программы принадлежат
атлету — у каждого свои (изменение vision 1.1).
2. В v1 создание/правка версии — на телефоне (редактор программы
атлета, `author=user`) или в административной консоли (владелец,
`author=admin`, изменение vision 1.2); отправка на сервер — при
синхронизации телефона либо сразу из консоли.
3. Ровно одна версия в статусе `active` (🛡); смена активной — явным
действием пользователя; выполненные тренировки ссылаются на версию,
которая была активна на их начало (история не «поплывёт»).
4. Статусы версии: `draft` → `active` → `archived`; переходы —
действиями пользователя.
### Ф-11. Контроль исполнения программы (план/факт)
1. На экране активной тренировки — текущая позиция активной программы:
упражнение, план подходов (вес × повторения), отметка выполненных.
2. Подход фиксируется голосом (Ф-1) и помечается: «по плану» (вес
и повторения = план ± допустимое отклонение: вес ± 2,5 кг,
повторения ± 0) или «отклонение» (всё остальное).
3. По завершении тренировки — отчёт план/факт: по каждому упражнению
план (подходы × вес × повторения) против факта, доля закрытых
подходов (adherence) (КУ-5).
4. Отчёт доступен на телефоне и в веб-интерфейсе (Ф-9.2).
### Ф-12. Правила прогрессии (без LLM, v1)
1. После каждой синхронизированной тренировки MetricsEngine (сервер)
считает по данным этого атлета: adherence (доля закрытых подходов),
тоннаж по упражнению и группе, оценочный 1ПМ (формула — открытый
вопрос 7) — в `workout_metrics` (дизайн LLM-планирования, разд. 4).
🛡 Метрики и правила считаются по каждому атлету независимо
(изоляция, изменение vision 1.1).
2. Микроправка весов на следующее занятие (правила, детерминированно):
- все запланированные подходы закрыты «по плану» при RPE ≤ 7
(2 тренировки подряд) → вес + `Progression.BaseStepKg` (базовые),
+ `Progression.IsoStepKg` (изоляция, З-27/О-3-А);
- RPE ≥ 9 в 2+ сетах → вес − `Progression.BackoffPercent` %;
- повторения достигли верхней границы диапазона (15–20) → вес ↑,
повторения к 12;
- ≥ 2 подходов не дотянуты по повторениям → вес −
`Progression.BackoffPercent` %;
- иначе (частичное выполнение) — вес без изменения.
Источник порогов — документ метрик («Метрики_тренировок_и_
самоконтроля.md», правила 3–5, МакРоберт).
3. Правила применяются только к `active` версии; результат —
предложение следующей версии программы со статусом `draft`
(`author=rules`), показывается атлету в приложении диффом;
применение — подтверждением атлета на телефоне или владельцем
в консоли (Ф-9.3, изменение vision 1.2). 🛡 Автоматической смены
активной версии нет (vision 2.4).
4. Делоад: если `Deload.MaxWeeks` недель подряд без делоада, правило
предлагает делоад-версию (объём −40–50 %) — тем же механизмом
диффа (дизайн LLM-планирования, разд. 6).
5. Красные флаги и лёгкая неделя (документ метрик, правила 1, 6, v1);
источник данных — `daily_log` (тренд пульса покоя и самочувствия
за 2 дня, Р1-А):
- пульс покоя ≥ обычного +8 уд/мин два дня подряд или самочувствие
≤ 2 → следующая тренировка помечается лёгкой (60–70 % весов,
2 сета); 3+ дня — предложение отмены и совет к врачу;
- боль в колене ≥ 1 два занятия подряд → предложение замены
упражнения из той же группы (пара-замена из атрибутов упражнения).
6. Офлайн-режим сервера: правила выполняются при следующем запуске
`serve`/`import` — телефон при этом полнофункционален (учёт и
локальные планы работают без сервера).
### Ф-13. Самоконтроль и допуск к тренировке (телефон; изменение 1)
Источник порогов — документ метрик
(`..\..\Метрики_тренировок_и_самоконтроля.md`, Жижин гл. 2–3, 6;
Аттия гл. 10, 13; МакРоберт; CDC). Состав v1 — MVP-приоритет документа
(его п. 5).
1. **Дневник-минимум (день, дом, 2–3 минуты):** АД (сист./диаст.),
вес тела, пульс покоя (утро, до подъёма — Р1-А), самочувствие 1–5;
экран «Сегодня» с вводом за 2–3 касания. Данные — в `LocalDb`
и на сервер (`daily_log`); пульс покоя и самочувствие — данные
правила 1 (Ф-12.5, КУ-7).
2. **Предстартовый допуск:** перед стартом силовой тренировки — экран
допуска: пульс сидя (с часов, если связаны, иначе ввод вручную)
и АД за сегодня. Пороги:
- АД > 150/90 → тренировка не начинается: предложение прогулки,
кнопка «Отменить» (🛡);
- АД 140–150/90–95 → только лёгкое кардио/дыхание (силовые заблокированы);
- пульс сидя > 90 уд/мин → предложение перенести тренировку
(старт — явным подтверждением «всё равно начать»).
3. **По сетам:** RPE (цель 6–8) — голосом «РПЭ **N**» для последнего
подхода или тапом 1–10; боль в колене 0–10 — тапом, только для
упражнений группы «ноги»; сохраняется с подходом (`workout_set`).
4. **После тренировки:** время возврата пульса к < 90 уд/мин — считается
автоматически из .fit (Record после последнего Lap); показывается
в карточке и попадает в метрики.
5. 🛡 Пороги допуска — конфигурируемые в настройках телефона
(`Safety.*`, умолчания — документ метрик; Р3-А); серверные
`Safety.*` применяются серверными правилами (`red_flags`);
обход блокировки «Отменить» логируется.
6. Талия и сон (утренняя телеметрия) и правила координации 7–10 — v2;
контрольные тесты — v3 (документ метрик, п. 5).
7. **Во время силовой части:** пульс > `Safety.HrStrengthMax`
(умолчание 120) → предупреждение на телефоне (баннер + вибро,
спокойная формулировка по UI/UX-стандарту); на часы порог
передаётся в объёме канала (ОВ-3). Источник — документ метрик
(п. 2.3); значение перенесено в параметр конфигурации
`Safety.HrStrengthMax` (разд. 3, умолчание 120). Программа
тренировок — импортируемые данные атлета (см. открытый вопрос 12);
нормативным источником требований не является (З-30, уточнение
владельца 2026-10-17).
### Ф-14. Перерывы и возврат в режим (телефон + сервер; изменение 2)
Источник — документ `Перерывы_подводные_камни_и_рекомендации.md`
(МакРоберт, CDC «Staying on Track», Жижин гл. 5–6, Аттия). Модель:
`active → break → return → active`.
1. 🛡 **Break вычисляется автоматически:** пропущены все
запланированные сессии за 4 дня подряд → программа переводится
в статус `break` (Жижин гл. 6: эффект тает с 4-го дня). Break ставится
автоматически по пропускам (основной путь); атлет может объявить
перерыв заранее (Ф-14.2, «уезжаю в отпуск») — такой break создаётся
с `source=manual`; произвольного ручного включения без объявления
нет.
2. **Причина запрашивается у атлета:** при срабатывании автомат
показывает вопрос «Вы пропустили тренировки — что случилось?»
с выбором типа (болезнь / отпуск / аврал-недосып / травма /
другое) и предполагаемой даты возврата; до ответа тип —
«не указана» (правила 12–14 применяются после ответа). При типе
«болезнь с температурой» — минимум 3 дня после нормальной
температуры до первой сессии (правило 12). Атлет может объявить
break заранее («уезжаю в отпуск») — запланированный простой
попадает в тот же автомат. Примечание (З-39, решение владельца
2026-10-18): признака «с температурой» в данных `breaks` нет —
в v1 любой break `kind=illness` трактуется правилом возврата как
перезапуск (коэффициент нижней границы, spec 04 m05); различение
«с температурой» — кандидат v2.
3. **Формула возврата** (детерминированная, коэффициент от рабочего
веса, документ перерывов разд. 3.2):
| Простой | Коэффициент | Режим |
|---|---|---|
| 4–6 дней | 100 % | первая сессия: −1 подход, RPE-цель 5–6 |
| 7–13 дней | −10 % | 1–2 недели возврата |
| 14–27 дней | −15 % | 1 вводная неделя (техника, без отказа) |
| 28–59 дней | −25 % | 2 вводные недели |
| 60+ дней / болезнь с температурой / операция | −30…−40 % | перезапуск цикла, контроль АД первые 2 недели |
Кардио (зона 2) возвращается со 100 % длительности сразу
(митохондриальная пластичность); силу — по формуле.
4. **Return:** при первой сессии после break создаётся новая версия
программы с формульными весами (статус `draft`, `author=rules`,
`rationale` — причина снижения); подтверждение — по Ф-12.3. 🛡
Неизменяемость версий не нарушается.
5. **`session_kind`:** каждая тренировка помечается
`normal | return | deload | test` — снижение весов при возврате
не выглядит в истории как регресс.
6. **Домашний режим:** тип break «отпуск, зал недоступен» →
переключатель активирует заготовленный поддерживающий комплекс
(отжимания, приседания без веса, тяга резинки, планка, ходьба)
с пометкой «поддержание, не прогресс» (правило 14).
7. **Правила координации 12–17** (документ перерывов, разд. 3.3):
болезнь с температурой (12), аврал/недосып → средняя тренировка
вместо тяжёлой (13), домашний комплекс (14), break ≥ 14 дней →
ужесточение порогов АД на 2 недели (отмена при > 145/90) (15),
2+ перерыва ≥ 7 дней за месяц → план 2 дня/нед до adherence ≥ 80 %
(16), возврат после break ≥ 14 дней → тест-сессия при RPE ≤ 6 (17).
Считает `ProgressionRules` вместе с правилами 1–11.
### Логирование (телефон и сервер)
1. `Info`: старт/остановка тренировки, каждая команда (Ф-1.5), пакеты
синхронизации (число записей, результат), забор/парсинг .fit
(файл, результат), применённые версии программ.
2. `Error`: все ошибки с текстом исключения (этап, объект, причина).
3. 🛡 В логах нет пароля Garmin, API-ключей и содержимого речи
(только распознанный текст команды — он нужен для диагностики
распознавания).
## 5. Контракты и инварианты
- 🛡 **Независимость частей:** часы, телефон и сервер работают при
отсутствии остальных; ни одна часть не блокирует другую (БЦ-4).
- 🛡 **Офлайн-first:** запись (голос, часы) и просмотр истории никогда
не ждут сеть; сеть — только синхронизация (КУ-3).
- 🛡 **Идемпотентность:** повтор отправки пакета (по `client_guid`)
и повторный `fetch`/`import` не создают дублей (КУ-3, Ф-8.1).
- 🛡 **Неизменяемость истории:** версии программ неизменяемы; факт-записи
сервер не переписывает; удаление упражнения — только логическое.
- 🛡 **Изоляция атлетов:** API-ключ идентифицирует атлета; ни API,
ни веб не отдают атлету чужие данные; метрики, правила и программы
считаются по атлетам независимо (изменение vision 1.1).
- 🛡 **Тайны:** пароль Garmin, API-ключи и хэш пароля владельца — только
в настройках/БД; в логах их нет.
- 🛡 **Безопасность нагрузки:** автоматические изменения программы
ограничены лимитами правил (Ф-12.2, Ф-12.4); смена активной версии —
только вручную (БР-5, vision 2.4).
- **Отмена тренировки:** «Сбросить» сохраняет тренировку как
`cancelled` — молчаливой потери данных нет.
## 6. Трассировка
| Функция | Бизнес-цель | Критерий успеха | Версия |
|---|---|---|---|
| Ф-1 голосовой учёт | БЦ-1 | КУ-1 | v1 |
| Ф-2 поле данных на часах | БЦ-2 | КУ-2 | v1 |
| Ф-3 экран тренировки | БЦ-1, БЦ-2 | КУ-1 | v1 |
| Ф-4 библиотека | БЦ-3 | — | v1 |
| Ф-5 история (телефон) | БЦ-3 | — | v1 |
| Ф-6 профиль и зоны | БЦ-2 | КУ-2 | v1 |
| Ф-7 офлайн + синхронизация | БЦ-4 | КУ-3 | v1 |
| Ф-8 сервер (.fit, API) | БЦ-5 | КУ-2, КУ-4 | v1 |
| Ф-9 веб + CSV | БЦ-5 | КУ-4 | v1 |
| Ф-10 программы | БЦ-3 | КУ-5 | v1 |
| Ф-11 план/факт | БЦ-3 | КУ-5 | v1 |
| Ф-12 правила прогрессии | БЦ-3 | КУ-6 | v1 |
| Ф-13 самоконтроль и допуск | БЦ-6 | КУ-7 | v1 |
| Ф-12 LLM-контур (еженедельно) | БЦ-3 | — | v2 |
| Ф-13 полная телеметрия, правила 7–10 | БЦ-6 | — | v2 |
| Ф-14 перерывы и возврат | БЦ-3, БЦ-6 | КУ-6, КУ-7 | v1 |
| Контрольные тесты | БЦ-6 | — | v3 |
| Wake-word (Ф-1) | БЦ-1 | — | v2 |
| iOS | БЦ-1, БЦ-2 | — | v2 |
| Бег/вело | — | — | v3+ |
## 7. Открытые вопросы
| # | Вопрос | Контекст | Решение |
|---|---|---|---|
| 1 | Формула максимальной ЧСС | Умолчание для зон (Ф-6.2): 220 − возраст (классика) или Танака (208 − 0,7×возраст, точнее для пожилых); можно — ручной ввод `Hr.MaxBpm` | Закрыт шагом 5 |
| 2 | Офлайн-движок распознавания | Vosk (модель ~50 МБ, ru) против sherpa-onnx; грамматика цифр шумного зала; влияет на точность КУ-1. Кандидаты проверяются прототипом | Закрыт шагом 5 |
| 3 | Канал телефон ↔ часы | Connect IQ companion (Communications): что передаётся реально (пульс live, зоны, таймер отдыха, заряд); ограничения канала определяют Ф-2.4, Ф-3.2, Ф-6.3. Прототип на шаге 4 | Закрыт шагом 4; **errata 2026-10-19**: companion-канал на fenix 6X нереализуем (CIQ ≤ 4.1.7) — транспорт = штатная BLE-трансляция пульса, канал односторонний (Ф-3.2) |
| 4 | Расписание `fetch` | cron/systemd-таймер на сервере против внутреннего планировщика; предпочтителен внешний планировщик | Закрыт шагом 5 |
| 5 | Способ сопоставления тренировок телефон ↔ .fit | Окно ± 15 минут (Ф-8.4) против точной метки начала из sync | ✅ Закрыт в этом документе (раздел 4, Ф-8.4) |
| 6 | Доступ в веб-интерфейс | ✅ Закрыт изменением vision 1.2: веб — административная консоль владельца, вход по логину и паролю (раздел 4, Ф-9); атлеты — только телефон | ✅ Закрыт (изменение vision 1.2) |
| 7 | Формула оценочного 1ПМ | Эпли против Бжицки (разд. 4 дизайна LLM-планирования); различие ~5 % на малых повторениях | Закрыт шагом 5 |
| 8 | Перечень моделей часов v1 | Якорь — Fenix 6X, 280×280 (изменение vision 1.1; уточнение владельца 2026-10-17 — vision 1.5); каждая модель — конфигурация сборки (экран, память). Кандидаты: модели с профилем «Силовая» и Connect IQ 4+ у участников; список фиксируется по фактическому парку устройств | Закрыт шагом 4 |
| 9 | Способ заведения атлетов | ✅ Закрыт изменением vision 1.2: раздел «Атлеты» веб-консоли владельца (Ф-9.1) — имя + генерация ключа, смена ключа, блокировка; стартовые данные владельца — в конфиге | ✅ Закрыт (изменение vision 1.2) |
| 10 | Источник .fit для атлетов без учётки владельца | `fetch` идёт под учётной записью владельца (опросник 4.3); другие атлеты: (а) делят доступ к своему Garmin (передача файла в папку импорта), (б) свой экземпляр учётки в конфиге, (в) только ручной импорт. Кандидат для v1 — (в) папка импорта | ✅ Закрыт (ревью №2, О-1-А): v1 — только папка импорта (`--mode import`); `fetch` — под единственной учёткой владельца; собственные учётки атлетов в конфиге — кандидат v2 (tech-requirements, ОВ-10). Уточнение владельца: атлет регистрируется на телефоне (имя), привязка — по uuid клиентского приложения |
| 12 | Импорт пакета программы (`program-import.json`) | Пакет — механизм эксплуатации (уточнение владельца 2026-10-17); формат, маппинг на `src.program`/`program_version.body`, поведение при повторном импорте | ✅ Закрыт (ревью №3, О-4-Б): импорт в v1 через `--mode import`; формат — `3-tech\program-import-format.md`; `external_id` — идентификатор пакета; секция `profile` в v1 игнорируется |
| 11 | Общий справочник упражнений | Сейчас библиотека у каждого атлета своя (Ф-4.4); общий справочник + личные дополнения — кандидат v2 (дубли при малом числе атлетов не критичны) | Открыт (до v2) |