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

487 lines
43 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.0), разделы
> 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. Назначение и границы
Версия 1.0 реализует полный цикл «программа → тренировка без рук →
архив → план/факт» (Ф-1–Ф-11, правила прогрессии Ф-12):
- **В границах:** голосовой офлайн-учёт подходов на Android-телефоне;
поле данных на Fenix 6 в профиле «Силовая» (пульс, зоны, 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 6.
### 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 fetch` | нет | выкл | разовый забор .fit из Garmin Connect и выход (расписание — внешний планировщик, открытый вопрос 5) |
| `--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` |
**Сервер (`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.BackoffPercent` | снижение при недовыполнении, % | `5` |
| `Deload.MaxWeeks` | недель между делоадами | `6` |
| `Safety.BpCancel` | порог отмены тренировки по АД (сист./диаст.) | `150/90` |
| `Safety.BpCardio` | порог «только кардио» по АД | `140/90` |
| `Safety.RestingHrSitting` | порог пульса сидя для переноса тренировки | `90` |
| `Safety.LightWeekHrRise` | рост пульса покоя (2 дня) для лёгкой недели | `+8` |
| `Log.Path / Log.Level` | логи | `logs\`, `Info` |
🛡 Пароль Garmin и API-ключ — только в настройках, не в коде; логи их
не содержат (З-2, З-6).
## 4. Функциональные требования
### Ф-1. Голосовой учёт подходов (телефон)
1. Активация — кнопкой микрофона на экране активной тренировки
(решение — опросник 4.5); повторное нажатие выключает прослушивание.
Пока экран активной тренировки открыт, распознавание работает
офлайн, без сети.
2. Грамматика команд (голос → семантика):
- «Подход **W** на **R**» → подход: вес `W` кг × `R` повторений
(целые числа; `W` — 0–500, `R` — 1–100);
- «Вес **W**» → рабочий вес `W` кг для следующих подходов;
- «Отдых **S** (секунд)» → таймер отдыха `S` секунд (5–600).
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. Вибрация: однократная лёгкая — конец таймера отдыха, переданного
с телефона (если связь есть); двойная — выход пульса за красную
зону. При отсутствии связи с телефоном вибраций отдыха нет —
часы не блокируются (не зависимость от телефона).
5. Кнопка Lap — штатная: фиксирует конец подхода/круга в записи
активности (требование — метки Lap попадают в .fit, КУ-2).
6. Кнопка Start/Stop — штатная пауза/стоп активности; поле не требует
подтверждений и не блокирует штатное сохранение тренировки.
7. 🛡 Поле не пишет в сеть и не хранит историю: только отображение;
запись ведёт сама платформа Garmin.
8. Разметка экрана — на ресурсы макета по семействам разрешений;
функциональное поведение (Ф-2.1–Ф-2.7) одинаково на всех целевых
моделях; функциональность, отсутствующая на модели (например,
вибрация), молча пропускается — поле не падает (изменение vision
1.1).
### Ф-3. Экран активной тренировки (телефон)
1. Статусная строка: связь с часами (зелёный/красный/серый — серый =
связь недоступна, приложение не блокируется), заряд часов (если
передаётся), статус сети (онлайн/офлайн).
2. Пульс: крупное текущее значение + линия за последний подход;
источник — канал телефон ↔ часы (открытый вопрос 3). Если связи
нет — прочерк; запись голосом работает (Ф-1.1).
3. Таймер отдыха: обратный отсчёт, фон — красный (идёт отдых),
жёлтый (последние 10 с), зелёный (время начинать); старт — командой
«Отдых S» или кнопкой (умолчание `Rest.DefaultSec`).
4. Блок упражнения: текущее упражнение (из активной программы, Ф-11.2)
и текущий рабочий вес.
5. Кнопки нижней панели: микрофон (Ф-1.1), «Сбросить» (отмена текущей
тренировки с подтверждением — тренировка помечается `cancelled`
и сохраняется в историю).
6. На экране одновременно ≤ 4 активных элемента, тёмная тема,
крупные зоны касания (vision 2.2).
### Ф-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-ключ: все записи пакета пишутся на сервере
от его имени; телефон не передаёт идентификатор атлета в данных.
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. Fallback-импорт (`--mode import`): файлы из `Import.Dir` (.fit, .tcx,
.gpх — по документу форматов) парсятся тем же конвейером;
обработанные перемещаются в `Import.ArchiveDir`; необработанные
остаются с записью в логе (код возврата 2).
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` (базовые),
+ `BaseStepKg`/2 (изоляция);
- 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):
- пульс покоя ≥ обычного +8 уд/мин два дня подряд или самочувствие
≤ 2 → следующая тренировка помечается лёгкой (60–70 % весов,
2 сета); 3+ дня — предложение отмены и совет к врачу;
- боль в колене ≥ 1 два занятия подряд → предложение замены
упражнения из той же группы (пара-замена из атрибутов упражнения).
io header fix
5. Офлайн-режим сервера: правила выполняются при следующем запуске
`serve`/`import` — телефон при этом полнофункционален (учёт и
локальные планы работают без сервера).
### Ф-13. Самоконтроль и допуск к тренировке (телефон; изменение 1)
Источник порогов — документ метрик
(`..\..\Метрики_тренировок_и_самоконтроля.md`, Жижин гл. 2–3, 6;
Аттия гл. 10, 13; МакРоберт; CDC). Состав v1 — MVP-приоритет документа
(его п. 5).
1. **Дневник-минимум (день, дом, 2–3 минуты):** АД (сист./диаст.),
вес тела; экран «Сегодня» с вводом за 2–3 касания. Данные —
в `LocalDb` и на сервер (`daily_log`), в сводку метрик атлета.
2. **Предстартовый допуск:** перед стартом силовой тренировки — экран
допуска: пульс сидя (с часов, если связаны, иначе ввод вручную)
и АД за сегодня. Пороги:
- АД > 150/90 → тренировка не начинается: предложение прогулки,
кнопка «Отменить» (🛡);
- АД 140–150/90–95 → только лёгкое кардио/дыхание (силовые заблокированы);
- пульс сидя > 90 уд/мин → предложение перенести тренировку
(старт — явным подтверждением «всё равно начать»).
3. **По сетам:** RPE (цель 6–8) — голосом «РПЭ **N**» для последнего
подхода или тапом 0–10; боль в колене 0–10 — тапом, только для
упражнений группы «ноги»; сохраняется с подходом (`workout_set`).
4. **После тренировки:** время возврата пульса к < 90 уд/мин — считается
автоматически из .fit (Record после последнего Lap); показывается
в карточке и попадает в метрики.
5. 🛡 Пороги допуска — конфигурируемые на сервере (`Safety.*`),
значения по умолчанию — из документа метрик; обход блокировки
«Отменить» логируется.
6. Полная утренняя телеметрия (пульс покоя, талия, сон, самочувствие)
и правила координации 7–10 — v2; контрольные тесты — v3
(документ метрик, п. 5).
### Логирование (телефон и сервер)
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 |
| Контрольные тесты | БЦ-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 |
| 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 6 (изменение vision 1.1); каждая модель — конфигурация сборки (экран, память). Кандидаты: модели с профилем «Силовая» и Connect IQ 4+ у участников; список фиксируется по фактическому парку устройств | Закрыт шагом 4 |
| 9 | Способ заведения атлетов | ✅ Закрыт изменением vision 1.2: раздел «Атлеты» веб-консоли владельца (Ф-9.1) — имя + генерация ключа, смена ключа, блокировка; стартовые данные владельца — в конфиге | ✅ Закрыт (изменение vision 1.2) |
| 10 | Источник .fit для атлетов без учётки владельца | `fetch` идёт под учётной записью владельца (опросник 4.3); другие атлеты: (а) делят доступ к своему Garmin (передача файла в папку импорта), (б) свой экземпляр учётки в конфиге, (в) только ручной импорт. Кандидат для v1 — (в) папка импорта | Закрыт шагом 5 |
| 11 | Общий справочник упражнений | Сейчас библиотека у каждого атлета своя (Ф-4.4); общий справочник + личные дополнения — кандидат v2 (дубли при малом числе атлетов не критичны) | Открыт (до v2) |