Интеграция документа перерывов (vision 1.4, Ф-14: auto-break + вопрос атлету, формула возврата, правила 12-17, breaks/session_kind) + волна 1 errata ревью (З-5,6,9-14) + З-2 архитектура

This commit is contained in:
virus-home 2026-10-17 19:40:40 +05:00
parent 0554f176ba
commit 1034c152f5
12 changed files with 451 additions and 24 deletions

View File

@ -0,0 +1,214 @@
# Ревью документации от 2026-10-17 — задания к исполнению
> **Исполнитель:** агент сопровождения документации WatchBody.
> **Источник:** архитектурная проверка документации шагов 1–6 на соответствие
> канону std1 (`I:\Kit\Kit.DevAi\std1`: процесс `_process\process.md`, правила
> `_rules\d\d-db.md`, `d-dev.md`, `d-req.md`, `d-drg.md`, `_rules\glossary.md`)
> и исходной задаче (`Техническое задание.md`, `Требование.md`, документы
> «Метрики…», «Архитектура LLM…», «Форматы…»).
> **Состояние репозитория на момент проверки:** коммит `0554f17`, все ссылки
> на строки даны по нему; после правок ориентироваться по разделам, а не строкам.
> **Статусная модель заданий:** ⬜ не начато → 🔄 в работе → ✅ готово
> (утверждено владельцем). Отметки проставляются в разделе 6 по мере работы.
> **Общие правила исполнения:**
> 1. Правки вносятся новыми версиями утверждённых документов (перечень версий —
> раздел 5). Каждая новая версия — точка утверждения **Пользователь!** (d-dev-4).
> 2. Содержание утверждённых документов меняется только в объёме заданий;
> инварианты 🛡 не ослабляются; трассировка «Ф → БЦ → КУ → версия» (раздел 6
> requirements) сохраняется.
> 3. Задания, зависящие от решений Р1–Р3 (раздел 1), не вносятся до выбора
> владельцем. Остальные можно выполнять сразу.
> 4. Код (`3-app\`, `2-db\`) не трогается — задание касается только `1-docs\`
> и корневого `readme.md`.
> 5. ~~Push не выполняется (учётные данные remote не настроены)~~ — уточнение
> исполнителя 2026-10-17: push настроен и работает (credentials в
> Credential Manager); коммиты пушатся после каждой волны.
## 0. Сводка ревью
Документация в целом соответствует std1 и исходной задаче: шаги 1–6 пройдены
с точками утверждения, все 9 позиций MVP ТЗ покрыты (Ф-1–Ф-9), трассировка
полная, стеки соответствуют `Требование.md`, решения LLM-дизайна и документа
метрик перенесены. Найдено: **4 существенных дефекта (P1)** — два логических
противоречия «v1-требования ↔ v1-данные», неактуальная архитектура после
изменения 1.3 и разрыв схемы синхронизации; **4 средних (P2)**; **7 мелких
(P3, гигиена)**. Позиции внедрения MVP документа метрик (п. 5) и порядок
шагов 1–3 LLM-дизайна перенесены в требования верно, кроме данных правила 1
(см. Р1).
## 1. Решения владельца (блокируют волну 2)
| Код | Вопрос | Варианты | Рекомендация ревью |
|---|---|---|---|
| **Р1** | Данные правила 1 (пульс покоя +8 два дня / самочувствие ≤ 2) отсутствуют в v1: `daily_log` v1 хранит только АД и вес; `hr_resting`, `wellbeing` помечены v2 — а Ф-12.5, БЦ-6 и КУ-7 требуют их в v1 | **А:** добавить `hr_resting`, `wellbeing` в v1-дневник (2 касания ввода) — КУ-7 закрывается целиком. **Б:** правило 1 → v2 (в v1 правила 2–6), скорректировать КУ-7. **В:** прокси — хранить предстартовый пульс сидя в `daily_log` и считать тренд по нему | **А** |
| **Р2** | Идентификация клиентских сущностей: протокол sync требует `client_guid` для каждой записи, но у `exercise`, `program_version`, `athlete_profile` его нет; ключи — серверные `serial`, телефон офлайн не может узнать server id для ссылок (`workout_set.exercise_id`, `workout.program_version_id`) | **А:** добавить `client_guid UNIQUE` в эти таблицы + расширить ответ sync маппингом `{entity, client_guid, id}`. **Б:** UUID как PK всех клиентских сущностей. **В:** натуральные ключи (имя упражнения у атлета) | **А** (минимальная правка БД, идемпотентность сохраняется) |
| **Р3** | Safety-пороги применяются на телефоне офлайн (экран допуска, Ф-13.2), но определены только в серверном `appsettings.json`; канал доставки на телефон не задан | **А:** продублировать пороги в телефонном Preferences (умолчания — документ метрик); серверные `Safety.*` — для серверных правил/`red_flags`. **Б:** синхронизируемая сущность `settings` (сервер — истина; до первой синхронизации — умолчания телефона) | **А** для v1 (один владелец; расхождение источников задокументировать, общая сущность — кандидат v2) |
Если владелец не выбрал вариант — подготовить диффы по рекомендованному и
вынести на утверждение как предложение (самовольно вариант не менять).
## 2. Задания P1 (существенные)
### З-1. Интеграция данных правила 1 в v1 ⬜ *(после Р1; по Р1-А)*
1. `1-docs\1-reqs\requirements.md`:
- Ф-13.1 — дневник-минимум дополнить: пульс покоя (утро), самочувствие 1–5;
- Ф-13.6 — v2-перечень сузить до «талия, сон» (пульс покоя и самочувствие
переносятся в v1);
- Ф-12.5 — указать источник данных правила 1 (`daily_log`, тренд 2 дня).
2. `1-docs\2-db\src\tables.md`: `src.daily_log` — `hr_resting`, `wellbeing`
убрать из пометки «v2», описать как поля v1.
3. `1-docs\2-db\src\functions.md`: `daily_log_upsert` — параметры
`p_hr_resting`, `p_wellbeing`.
4. `1-docs\3-tech\tech-requirements.md`: п. 2.2 (`daily_log`) — поля v1;
чек-лист шага 7 не меняется.
### З-2. Актуализация архитектуры под Ф-13 ⬜
1. `1-docs\2-arch\architecture.md`:
- шапка: источники — vision **v1.3**, requirements v1.1; версия документа →
1.1 с примечанием «актуализация под Ф-13 (изменение vision 1.3)»;
- раздел 1.2 (телефон): в `WorkoutCore` добавить ответственность «предстартовый
допуск (пороги Safety, экран допуска) и дневник самоконтроля
(`daily_log`)»; в `TrainingUi` — экраны «Сегодня» и «Допуск»;
- раздел 2 (поток): шаг 0 перед «Тренировка» — допуск (пороги АД/пульса;
блокировка/лёгкое кардио; обход блокировки логируется);
- таблица компонентов: строка БД — добавить `daily_log` к перечню.
2. `1-docs\2-arch\hld.puml`: отразить допуск/дневник подписью в `WorkoutCore`
(без новых компонентов и связей — гранулярность схемы не меняется);
перерендерить `hld.svg` + `hld.png` навыком `std1\_skills\dgm-hld`
(`python std1\_skills\dgm-hld\render.py 1-docs\2-arch\hld.puml`), рендер
рядом с исходником (d-drg-11).
3. Утверждение — **Пользователь!**
### З-3. Синхронизация: `client_guid` и маппинг id ⬜ *(после Р2; по Р2-А)*
1. `1-docs\2-db\src\tables.md`: `src.exercise`, `src.program_version` —
колонка `client_guid uuid NOT NULL UNIQUE` (идемпотентность sync, Ф-7.4);
`src.athlete_profile` — upsert по `athlete_id` уже идемпотентен, решение
(без `client_guid`) зафиксировать явно в комментарии.
2. `1-docs\3-tech\tech-requirements.md`:
- п. 2.1 — ответ `POST /api/v1/sync`: помимо `accepted`, маппинг
`{entity, client_guid, id}` для созданных записей (телефон хранит
соответствие локальный id ↔ серверный);
- п. 2.2 — `exercise`, `program_version`: поле `client_guid`.
3. `1-docs\1-reqs\requirements.md`: Ф-7.2/Ф-7.3 — дополнить маппингом id
(одной формулировкой: «ответ содержит соответствие client_guid → серверный
id; телефон использует его для ссылок дочерних записей»).
### З-4. Safety-пороги телефона ⬜ *(после Р3; по Р3-А)*
1. `1-docs\3-tech\tech-requirements.md`, п. 4.1 (телефон): параметры
`Safety.BpCancelSys/Dias` (умолч. 150/90), `Safety.BpCardioSys/Dias`
(140/90), `Safety.RestingHrSitting` (90) с валидацией — как в п. 4.2;
примечание: умолчания из документа метрик; серверные `Safety.*` — источник
для серверных правил (`red_flags`), совпадение значений — ответственность
владельца конфигурации (ограничение v1 задокументировать, общая сущность —
кандидат v2).
2. `1-docs\1-reqs\requirements.md`: раздел 3 (телефонные настройки) —
дополнить теми же параметрами; Ф-13.5 — переформулировать: «пороги допуска
— конфигурируемые в настройках телефона; серверные `Safety.*` применяются
серверными правилами».
## 3. Задания P2
### З-5. `src.workout`: уникальность `fit_file_id` и `fit_workout_id` ⬜
`1-docs\2-db\src\tables.md`:
- у описания поля `fit_file_id` убрать «UNIQUE» — оставить составной
`UNIQUE(athlete_id, fit_file_id)` (изоляция атлетов);
- колонку `fit_workout_id` удалить: факты пишутся в ту же строку
(`facts_json`), отдельная строка «только .fit» создаётся с собственным
`client_guid` (сервер) — ссылочная колонка не используется.
### З-6. Sync-enum: фантом и пропуск ⬜
`1-docs\3-tech\tech-requirements.md`, п. 2.1: из перечня `entity` убрать
`set_note` (сущности нет ни в БД, ни в требованиях); добавить `daily_log`.
(Совместить с З-3.2 — одна правка п. 2.1.)
### З-7. Диапазон RPE ⬜
Согласовать: Ф-13.3 и tech-requirements п. 2.2 — «0–10»; `tables.md` —
`CHECK (rpe BETWEEN 1 AND 10)`. Рекомендация: единый диапазон **1–10**
(RPE 0 в документе метрик не используется; голос «РПЭ N» — N 1–10): в
Ф-13.3 «тапом 1–10», в tech-requirements 2.2 — «int? 1–10». При выборе
владельцем 0–10 — правится CHECK в tables.md.
### З-8. Грамматика Ф-1.2: команда «РПЭ N» ⬜
`1-docs\1-reqs\requirements.md`, Ф-1.2: добавить четвёртую команду
«РПЭ **N**» → RPE последнего подхода (N 1–10, по З-7); согласовать с Ф-13.3
и решением ОВ-2 в tech-requirements (грамматика уже включает «РПЭ»).
## 4. Задания P3 (гигиена)
| Код | Задание | Где |
|---|---|---|
| З-9 | Удалить мусорную строку «io header fix»; в Ф-12 перенумеровать второй «5.» (Офлайн-режим сервера) в «6.» | requirements.md:381–384 |
| З-10 | Исправить «.gp**х**» → «.gpx» (кириллическая буква) | requirements.md:267 |
| З-11 | Шапки-источники привести к vision **v1.3**: requirements (сейчас «v1.0»), tech-requirements (сейчас «v1.2»). Для architecture — в З-2 | requirements.md:4, tech-requirements.md:4 |
| З-12 | URL репозитория: `readme.md` — `VEU/VEU.WathBody.git`, `plan.md` — `VEU/WatchBody.git`. Сверить с фактическим `git remote -v` и привести оба файла к нему (сейчас remote, вероятно, не настроен — тогда принять `VEU/WatchBody.git` и исправить опечатку «WathBody») | readme.md:3–4, plan.md:3–4 |
| З-13 | Маркер «MM» в tables.md вне словаря d-db-2 (`CRUD/SELECT/NO/CUSTOM`): оставить как есть, в комментарий строки добавить «типовой набор связи ММ — d-db-3»; о необходимости маркера «MM» сообщить владельцу std1 (вне репозитория) | src/tables.md:14 |
| З-14 | Добавить в readme БД раздел «План миграции» (требование d-db-1): создание схем `src`/`ref`, вставка значений `ref.exercise_group`, порядок применения psql-файлов из `2-db\`, версионирование миграций | 1-docs/2-db/readme.md |
| З-15 | Сообщить владельцу std1 (вне репозитория): process.md шаг 6 указывает артефакты в `1-docs\d-db\`, противореча шагу 2 того же процесса и правилу d-db-1 (`1-docs\2-db\`); проект применил верный путь | — |
## 5. Порядок исполнения и версии
**Волна 1 — errata, выполняется сразу (З-5–З-14 без Р-зависимостей:**
З-5, З-6, З-9, З-10, З-11, З-12, З-13, З-14**).** Правки представляются
владельцу одним диффом → утверждение → коммит
«Ревью документации: errata (Ф-12 нумерация, .gpx, версии источников,
fit_file_id, sync-enum)». Версии: requirements → 1.2 (errata),
tables.md → новая редакция (дата), tech-requirements → 1.2, 2-db readme →
новая редакция.
**Волна 2 — после решений Р1–Р3 (З-1, З-2, З-3, З-4, З-8).** Все правки
одним пакетом «Актуализация по ревью: Ф-13 в архитектуре, данные правила 1,
client_guid/маппинг sync, Safety телефона» → рендер hld → утверждение
владельцем (одно утверждение на пакет) → коммит. Версии: requirements → 1.3,
architecture → 1.1, tech-requirements → 1.3, tables/functions → новая
редакция (число функций не меняется — меняются параметры `daily_log_upsert`
и колонки `client_guid`).
**После каждой волны — самопроверка (раздел 7) и отметки в разделе 6.**
## 6. Чек-лист
| Задание | P | Зависит от | Статус |
|---|---|---|---|
| Р1–Р3 решения владельца | — | — | ⬜ |
| З-1 правило 1: данные v1 | 1 | Р1 | ⬜ |
| З-2 архитектура + hld.puml + рендер | 1 | — (текст), рендер после правок | 🔄 (2026-10-17: Ф-13+Ф-14 внесены; черновик v1.1 — на утверждении) |
| З-3 client_guid + маппинг id | 1 | Р2 | ⬜ |
| З-4 Safety-пороги телефона | 1 | Р3 | ⬜ |
| З-5 fit_file_id / fit_workout_id | 2 | — | ✅ (2026-10-17) |
| З-6 sync-enum (set_note/daily_log) | 2 | — | ✅ (2026-10-17) |
| З-7 диапазон RPE | 2 | — | ⬜ |
| З-8 грамматика «РПЭ N» | 2 | З-7 | ⬜ |
| З-9 Ф-12: «io header fix», нумерация | 3 | — | ✅ (2026-10-17) |
| З-10 .gpх → .gpx | 3 | — | ✅ (2026-10-17) |
| З-11 шапки-источники → vision v1.3 | 3 | — | ✅ (2026-10-17, сразу v1.4) |
| З-12 URL репозитория | 3 | — | ✅ (2026-10-17, remote = VEU/VEU.WathBody.git, оба файла выверены) |
| З-13 маркер «MM» + письмо std1 | 3 | — | 🔄 (комментарий добавлен; письмо std1 — вне репо) |
| З-14 план миграции в 2-db readme | 3 | — | ✅ (2026-10-17) |
| З-15 сообщение владельцу std1 о process.md | 3 | — | ⬜ |
## 7. Самопроверка после правок
1. **requirements.md:** трассировка раздела 6 не нарушена (каждая Ф имеет БЦ/КУ);
ссылки на ОВ актуальны; нумерация Ф-12 сплошная; «РПЭ N» есть и в Ф-1.2,
и в Ф-13.3 с одним диапазоном.
2. **architecture.md / hld.puml:** шапка ссылается на vision v1.3 и
requirements текущей версии; Ф-13 присутствует в разделах 1.2, 2 и в таблице
компонентов; рендер (`hld.svg`/`hld.png`) свежее `.puml`; стиль — d-drg-3,
палитра и легенда — d-drg-4 (жёлтый — внешнее, серый — не в v1).
3. **tables.md / functions.md:** «Набор функций» в сводной таблице ↔ строки
«Списка функций» совпадают 1:1; словарь маркеров соблюдён; составной
`UNIQUE(athlete_id, fit_file_id)` — единственное описание уникальности;
`daily_log_upsert` содержит все поля v1.
4. **tech-requirements.md:** enum `entity` = перечень п. 2.2 без фантомов;
`Safety.*` есть в 4.1 и 4.2 с согласованными умолчаниями; ответ sync
описывает маппинг id.
5. **Общее:** git-статус чистый после коммитов; в логах/доках нет секретов;
push не выполнялся.

View File

@ -1,7 +1,7 @@
# Функциональные требования: Феникс 6X v1 # Функциональные требования: Феникс 6X v1
> Шаг 3 процесса разработки (`std1\_process\process.md`). > Шаг 3 процесса разработки (`std1\_process\process.md`).
> Источник: [vision.md](vision.md) (утверждён 2026-10-17, v1.0), разделы > Источник: [vision.md](vision.md) (утверждён 2026-10-17, v1.3), разделы
> 1.3 (БЦ), 1.4 (КУ), 2.1–2.4; решения опросников 4.1–4.7; ТЗ > 1.3 (БЦ), 1.4 (КУ), 2.1–2.4; решения опросников 4.1–4.7; ТЗ
> (`..\..\Техническое задание.md`), дизайн LLM-планирования (разд. 2–3, > (`..\..\Техническое задание.md`), дизайн LLM-планирования (разд. 2–3,
> 6–8, 11 — шаг 1); изменение vision 1.1 (2026-10-17) — несколько > 6–8, 11 — шаг 1); изменение vision 1.1 (2026-10-17) — несколько
@ -263,7 +263,7 @@ Garmin штатно — Ф-2.5, Ф-2.6). Код поля данных перен
результат — канонический JSON тренировки в `workout_facts` результат — канонический JSON тренировки в `workout_facts`
(дизайн LLM-планирования, разд. 4). (дизайн LLM-планирования, разд. 4).
3. Fallback-импорт (`--mode import`): файлы из `Import.Dir` (.fit, .tcx, 3. Fallback-импорт (`--mode import`): файлы из `Import.Dir` (.fit, .tcx,
.gpх — по документу форматов) парсятся тем же конвейером; .gpx — по документу форматов) парсятся тем же конвейером;
обработанные перемещаются в `Import.ArchiveDir`; необработанные обработанные перемещаются в `Import.ArchiveDir`; необработанные
остаются с записью в логе (код возврата 2). остаются с записью в логе (код возврата 2).
4. Сопоставление факт ↔ голосовой учёт: тренировка .fit сопоставляется 4. Сопоставление факт ↔ голосовой учёт: тренировка .fit сопоставляется
@ -378,8 +378,7 @@ Garmin штатно — Ф-2.5, Ф-2.6). Код поля данных перен
- боль в колене ≥ 1 два занятия подряд → предложение замены - боль в колене ≥ 1 два занятия подряд → предложение замены
упражнения из той же группы (пара-замена из атрибутов упражнения). упражнения из той же группы (пара-замена из атрибутов упражнения).
io header fix 6. Офлайн-режим сервера: правила выполняются при следующем запуске
5. Офлайн-режим сервера: правила выполняются при следующем запуске
`serve`/`import` — телефон при этом полнофункционален (учёт и `serve`/`import` — телефон при этом полнофункционален (учёт и
локальные планы работают без сервера). локальные планы работают без сервера).
@ -414,6 +413,57 @@ io header fix
и правила координации 7–10 — v2; контрольные тесты — v3 и правила координации 7–10 — v2; контрольные тесты — v3
(документ метрик, п. 5). (документ метрик, п. 5).
### Ф-14. Перерывы и возврат в режим (телефон + сервер; изменение 2)
Источник — документ `Перерывы_подводные_камни_и_рекомендации.md`
(МакРоберт, CDC «Staying on Track», Жижин гл. 5–6, Аттия). Модель:
`active → break → return → active`.
1. 🛡 **Break вычисляется автоматически:** пропущены все
запланированные сессии за 4 дня подряд → программа переводится
в статус `break` (Жижин гл. 6: эффект тает с 4-го дня). Ручного
включения break нет — только расчёт.
2. **Причина запрашивается у атлета:** при срабатывании автомат
показывает вопрос «Вы пропустили тренировки — что случилось?»
с выбором типа (болезнь / отпуск / аврал-недосып / травма /
другое) и предполагаемой даты возврата; до ответа тип —
«не указана» (правила 12–14 применяются после ответа). При типе
«болезнь с температурой» — минимум 3 дня после нормальной
температуры до первой сессии (правило 12). Атлет может объявить
break заранее («уезжаю в отпуск») — запланированный простой
попадает в тот же автомат.
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), пакеты 1. `Info`: старт/остановка тренировки, каждая команда (Ф-1.5), пакеты
@ -464,6 +514,7 @@ io header fix
| Ф-13 самоконтроль и допуск | БЦ-6 | КУ-7 | v1 | | Ф-13 самоконтроль и допуск | БЦ-6 | КУ-7 | v1 |
| Ф-12 LLM-контур (еженедельно) | БЦ-3 | — | v2 | | Ф-12 LLM-контур (еженедельно) | БЦ-3 | — | v2 |
| Ф-13 полная телеметрия, правила 7–10 | БЦ-6 | — | v2 | | Ф-13 полная телеметрия, правила 7–10 | БЦ-6 | — | v2 |
| Ф-14 перерывы и возврат | БЦ-3, БЦ-6 | КУ-6, КУ-7 | v1 |
| Контрольные тесты | БЦ-6 | — | v3 | | Контрольные тесты | БЦ-6 | — | v3 |
| Wake-word (Ф-1) | БЦ-1 | — | v2 | | Wake-word (Ф-1) | БЦ-1 | — | v2 |
| iOS | БЦ-1, БЦ-2 | — | v2 | | iOS | БЦ-1, БЦ-2 | — | v2 |

View File

@ -1,8 +1,8 @@
# Vision: Феникс 6X — контроль силовых тренировок (телефон + Garmin Fenix 6 + личный сервер) # Vision: Феникс 6X — контроль силовых тренировок (телефон + Garmin Fenix 6 + личный сервер)
> Версия 1.3 — **УТВЕРЖДЕНО** (2026-10-17; вопросы опросника 4.1–4.7 > Версия 1.4 — **УТВЕРЖДЕНО** (2026-10-17; вопросы опросника 4.1–4.7
> отвечены, ответы применены; утверждение — 2026-10-17; изменения > отвечены, ответы применены; утверждение — 2026-10-17; изменения
> 1.1–1.3 — указания пользователя от 2026-10-17). > 1.1–1.4 — указания пользователя от 2026-10-17).
> Изменение 1.1: серверная часть > Изменение 1.1: серверная часть
> поддерживает нескольких атлетов — у каждого свои программы, профиль > поддерживает нескольких атлетов — у каждого свои программы, профиль
> и данные, с собственных телефона и часов (пп. 2.2, 2.4, 3.1); > и данные, с собственных телефона и часов (пп. 2.2, 2.4, 3.1);
@ -23,6 +23,12 @@
> (пороги АД/пульса), RPE и боль в колене по сетам; правила > (пороги АД/пульса), RPE и боль в колене по сетам; правила
> координации Ф-12 расширены 11 детерминированными правилами по книгам > координации Ф-12 расширены 11 детерминированными правилами по книгам
> (пп. 2.2, 2.3, 3.3). > (пп. 2.2, 2.3, 3.3).
> Изменение 1.4 (документ «Перерывы_подводные_камни_и_рекомендации.md»,
> 2026-10-17): добавлена Ф-14 «Перерывы и возврат в режим» — автомат
> `active → break → return → active` (детекция по пропускам или метка
> пользователя), формула снижения весов по длительности простоя
> (−10 %…перезапуск цикла), метка `session_kind`, домашний
> поддерживающий комплекс, правила координации 12–17 (пп. 2.1, 2.2).
> Источник идеи: техническое задание > Источник идеи: техническое задание
> «Феникс 6X» (Garmin Edition) от 17.10.2026 > «Феникс 6X» (Garmin Edition) от 17.10.2026
> ([Техническое задание.md](../../Техническое%20задание.md)); требование > ([Техническое задание.md](../../Техническое%20задание.md)); требование
@ -325,6 +331,15 @@ Garmin, наш продукт фиксирует результаты без р
МакРоберт, CDC); состав v1 — MVP-приоритет документа (его п. 5): МакРоберт, CDC); состав v1 — MVP-приоритет документа (его п. 5):
дневник-минимум + правила координации 1–6; утренняя телеметрия дневник-минимум + правила координации 1–6; утренняя телеметрия
полностью и правила 7–10 — v2, контрольные тесты — v3. полностью и правила 7–10 — v2, контрольные тесты — v3.
- **Ф-14. Перерывы и возврат в режим** (изменение 1.4) — конечный автомат
`active → break → return → active`: break вычисляется автоматически
(пропуск всех запланированных сессий 4 дня), причина запрашивается
у атлета (болезнь/отпуск/аврал/травма); при возврате программа переводится в режим
возврата по формуле длительности простоя (100 %…−40 %, вводные
недели), создаётся новая версия с `rationale`; тренировки помечаются
`session_kind` (normal/return/deload/test); «отпуск без зала» —
домашний поддерживающий комплекс; правила координации 12–17
(документ перерывов, разд. 3.2–3.3).
### 2.2. Объём первоначально запланированной версии ### 2.2. Объём первоначально запланированной версии
@ -332,8 +347,8 @@ Garmin, наш продукт фиксирует результаты без р
**v1.0 = полный MVP-цикл ТЗ (Ф-1–Ф-9) + программы: Ф-10, Ф-11 и правила **v1.0 = полный MVP-цикл ТЗ (Ф-1–Ф-9) + программы: Ф-10, Ф-11 и правила
прогрессии/координации (основа Ф-12) — шаг 1 порядка внедрения прогрессии/координации (основа Ф-12) — шаг 1 порядка внедрения
LLM-планирования, без LLM; Ф-13 в MVP-составе документа метрик LLM-планирования, без LLM; Ф-13 в MVP-составе документа метрик
(дневник-минимум, допуск по АД/пульсу, правила 1–6); LLM-контур (дневник-минимум, допуск по АД/пульсу, правила 1–6); Ф-14 — перерывы
Ф-12 — v2**. и возврат (изменение 1.4); LLM-контур Ф-12 — v2**.
Разработка — в порядке перечня MVP ТЗ: часы → связь с телефоном → экран Разработка — в порядке перечня MVP ТЗ: часы → связь с телефоном → экран
тренировки → голосовой ввод → локальная БД → сервер → забор .fit → тренировки → голосовой ввод → локальная БД → сервер → забор .fit →
синхронизация → история и веб. синхронизация → история и веб.

View File

@ -26,13 +26,16 @@ vision 1.1); перечень моделей — открытый вопрос 8
### 1.2. Телефон атлета — **Phone.WatchBody** (Android, Kotlin) ### 1.2. Телефон атлета — **Phone.WatchBody** (Android, Kotlin)
- `TrainingUi` — экраны (Jetpack Compose — кандидат, фиксируется - `TrainingUi` — экраны (Jetpack Compose — кандидат, фиксируется
шагом 5): активная тренировка, история, программы, профиль; шагом 5): активная тренировка, история, программы, профиль,
тёмная тема, крупные зоны касания. «Сегодня» (дневник) и «Допуск»; тёмная тема, крупные зоны касания.
- `VoiceInput` — офлайн-STT (движок — открытый вопрос 2) + грамматика - `VoiceInput` — офлайн-STT (движок — открытый вопрос 2) + грамматика
команд (Ф-1.2) + TTS-подтверждения; журнал команд для сопоставления команд (Ф-1.2) + TTS-подтверждения; журнал команд для сопоставления
с .fit. с .fit.
- `WorkoutCore` — логика тренировки и программ: подходы, таймеры, - `WorkoutCore` — логика тренировки и программ: подходы, таймеры,
план/факт, редактор программ; единственный писатель `LocalDb`. план/факт, редактор программ; 🛡 предстартовый допуск (пороги
Safety, экран допуска) и дневник самоконтроля (`daily_log`);
автомат перерывов и формула возврата (Ф-14); единственный писатель
`LocalDb`.
- `LocalDb` — Room/SQLite: текущая тренировка, история, программы, - `LocalDb` — Room/SQLite: текущая тренировка, история, программы,
библиотека, профиль, очередь. библиотека, профиль, очередь.
- `SyncQueue` — исходящие записи с `client_guid`, пакеты ≤ 50, - `SyncQueue` — исходящие записи с `client_guid`, пакеты ≤ 50,
@ -53,9 +56,9 @@ vision 1.1); перечень моделей — открытый вопрос 8
| `FitFetcher` | `Server.WatchBody.Fetch` | Клиент Garmin Connect (неофициальный, опросник 4.3); `--mode fetch`; дедупликация по `fit_file_id` | | `FitFetcher` | `Server.WatchBody.Fetch` | Клиент Garmin Connect (неофициальный, опросник 4.3); `--mode fetch`; дедупликация по `fit_file_id` |
| `FitParser` | `Server.WatchBody.Import` | Garmin FIT SDK: Session/Lap/Record/Set → `workout_facts`; `--mode import` (fallback: .fit/.tcx/.gpx из папки) | | `FitParser` | `Server.WatchBody.Import` | Garmin FIT SDK: Session/Lap/Record/Set → `workout_facts`; `--mode import` (fallback: .fit/.tcx/.gpx из папки) |
| `MetricsEngine` | `Server.WatchBody.Core` | adherence, тоннаж по группам, 1ПМ, пульс-метрики, красные флаги (АД/пульс/боль — документ метрик) → `workout_metrics` | | `MetricsEngine` | `Server.WatchBody.Core` | adherence, тоннаж по группам, 1ПМ, пульс-метрики, красные флаги (АД/пульс/боль — документ метрик) → `workout_metrics` |
| `ProgressionRules` | `Server.WatchBody.Core` | Правила прогрессии, делоада и координации 1–6 (документ метрик) → draft-версии программ (`author=rules`) | | `ProgressionRules` | `Server.WatchBody.Core` | Правила прогрессии, делоада и координации 1–11 (документ метрик) + 12–17 перерывы (документ перерывов): детекция `break` по пропускам, формула возврата → draft-версии (`author=rules`) |
| `ProgramStore` | `Server.WatchBody.Core` | Версионированный JSON программ; неизменяемость версий; `active` — ровно один на атлета | | `ProgramStore` | `Server.WatchBody.Core` | Версионированный JSON программ; неизменяемость версий; `active` — ровно один на атлета |
| БД | схема `src` \| `ref` | PostgreSQL: атлеты, тренировки/факты, метрики, программы, журнал LLM (v2) | | БД | схема `src` \| `ref` | PostgreSQL: атлеты, тренировки/факты + `session_kind`, дневник (`daily_log`), перерывы (`breaks`), метрики, программы, журнал LLM (v2) |
Общая библиотека — `Kit.Helpers` (`3-app\1-lib`, std1). Взаимодействие Общая библиотека — `Kit.Helpers` (`3-app\1-lib`, std1). Взаимодействие
с LLM (v2, `LlmPlanner`) — по правилам std1 `c-llm`; в v1 компонент с LLM (v2, `LlmPlanner`) — по правилам std1 `c-llm`; в v1 компонент
@ -69,6 +72,11 @@ vision 1.1); перечень моделей — открытый вопрос 8
## 2. Поток цикла обработки ## 2. Поток цикла обработки
0. **Допуск и перерывы.** Перед стартом — экран допуска (АД/пульс;
красные флаги блокируют или переводят в лёгкое кардио; обход
логируется); автомат перерывов ставит `break` по пропускам и
запрашивает причину у атлета; при возврате — формульная версия
(Ф-14).
1. **Тренировка.** Атлет запускает «Силовую» на часах (`FenixDataField` 1. **Тренировка.** Атлет запускает «Силовую» на часах (`FenixDataField`
показывает пульс/зоны/подходы) и активирует голосовой ввод на телефоне показывает пульс/зоны/подходы) и активирует голосовой ввод на телефоне
(`VoiceInput` → команды → `WorkoutCore` → `LocalDb`). Часы и телефон (`VoiceInput` → команды → `WorkoutCore` → `LocalDb`). Часы и телефон

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

After

Width:  |  Height:  |  Size: 176 KiB

View File

@ -31,7 +31,7 @@ node "Часы атлета\n(Garmin, Connect IQ)" as Watch {
node "Телефон атлета\n(Android, Kotlin)" as Phone { node "Телефон атлета\n(Android, Kotlin)" as Phone {
component "VoiceInput\nофлайн-STT + грамматика команд" as VoiceInput component "VoiceInput\nофлайн-STT + грамматика команд" as VoiceInput
component "TrainingUi\nэкран тренировки, таймеры, история" as TrainingUi component "TrainingUi\nэкран тренировки, таймеры, история" as TrainingUi
component "WorkoutCore\nлогика тренировки и программ" as WorkoutCore component "WorkoutCore\nлогика тренировки, допуск,\nдневник, перерывы" as WorkoutCore
component "LocalDb\nRoom/SQLite" as LocalDb component "LocalDb\nRoom/SQLite" as LocalDb
component "SyncQueue\nочередь + идемпотентность" as SyncQueue component "SyncQueue\nочередь + идемпотентность" as SyncQueue
component "WatchChannel\ncompanion-канал Connect IQ" as WatchChannel component "WatchChannel\ncompanion-канал Connect IQ" as WatchChannel

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -28,6 +28,24 @@
| [src\tables.md](src/tables.md) | таблицы слоя `src` | | [src\tables.md](src/tables.md) | таблицы слоя `src` |
| `{слой}\functions.md` | списки функций (после утверждения таблиц) | | `{слой}\functions.md` | списки функций (после утверждения таблиц) |
## План миграции (d-db-1)
1. Схемы: `CREATE SCHEMA IF NOT EXISTS src; CREATE SCHEMA IF NOT EXISTS ref;`
2. Справочники: `ref\exercise_group.psql` — таблица + вставка 5 значений
(идемпотентно, `ON CONFLICT (code) DO NOTHING`).
3. Таблицы `src`: порядок — `athlete` → `athlete_profile` → `exercise` →
`exercise_replacement` → `program` → `program_version` → `workout` →
`workout_set` → `command_log` → `daily_log` → `workout_metrics` →
`breaks` (FK-зависимости).
4. Функции: файлы `{слой}\функции.psql` по сущностям в том же порядке
(`CREATE OR REPLACE` — перезапуск безопасен).
5. Версионирование: каталог `2-db\migrations\` — `NNN_описание.sql`
по возрастанию; каждая миграция идемпотентна (IF NOT EXISTS /
OR REPLACE); факт применения — одна запись на файл в
`src.migration` (id, filename, applied_at).
6. Сборка: скрипт `2-dbuild.ps1` применяет миграции по номеру
(утилита `Server.WatchBody.Import --mode migrate`).
## Таблица `llm_calls` (v2) ## Таблица `llm_calls` (v2)
Журнал вызовов LLM-планировщика (дизайн LLM-планирования, разд. 8) Журнал вызовов LLM-планировщика (дизайн LLM-планирования, разд. 8)

View File

@ -7,6 +7,10 @@
> они для сервисов владельца): изоляция атлетов. > они для сервисов владельца): изоляция атлетов.
> Статусы: `PLAN` — только в списке (спецификаций и кода нет). > Статусы: `PLAN` — только в списке (спецификаций и кода нет).
> **УТВЕРЖДЕНО** (2026-10-17) — список функций к реализации утверждён. > **УТВЕРЖДЕНО** (2026-10-17) — список функций к реализации утверждён.
> Новая редакция (2026-10-17): + `breaks` (break_insert/update/select),
> `session_kind` в workout_insert/update — интеграция документа
> перерывов (Ф-14, изменение vision 1.4). Утверждение добавки —
> Пользователь!
## Список функций ## Список функций
@ -32,8 +36,8 @@
| program_version | program_version_update | PLAN | p_athlete_id, p_id, p_status | int — число строк | - | - | 🛡 меняется только статус; переходы draft→active→archived; активной становится ровно одна (сдвиг прежней в archived) | | program_version | program_version_update | PLAN | p_athlete_id, p_id, p_status | int — число строк | - | - | 🛡 меняется только статус; переходы draft→active→archived; активной становится ровно одна (сдвиг прежней в archived) |
| program_version | program_version_select | PLAN | p_athlete_id, p_program_id, p_status, p_author | TABLE — строки (без body) | - | - | body — отдельным get (крупный jsonb) | | program_version | program_version_select | PLAN | p_athlete_id, p_program_id, p_status, p_author | TABLE — строки (без body) | - | - | body — отдельным get (крупный jsonb) |
| program_version | program_version_get | PLAN | p_athlete_id, p_id | TABLE — одна строка с body | - | - | — | | program_version | program_version_get | PLAN | p_athlete_id, p_id | TABLE — одна строка с body | - | - | — |
| workout | workout_insert | PLAN | p_athlete_id, p_client_guid, p_program_version_id, p_started_at | int — id | - | - | повтор `client_guid` возвращает существующий id (🛡 идемпотентность) | | workout | workout_insert | PLAN | p_athlete_id, p_client_guid, p_program_version_id, p_started_at, p_session_kind | int — id | - | - | повтор `client_guid` возвращает существующий id (🛡 идемпотентность) |
| workout | workout_update | PLAN | p_athlete_id, p_id, p_ended_at, p_status, p_avg_hr, p_fit_workout_id, p_facts_json, p_fit_file_id, p_hr_recovery_s | int — число строк | - | - | факты .fit дописывает FitParser; клиент — только ended/status/avg_hr | | workout | workout_update | PLAN | p_athlete_id, p_id, p_ended_at, p_status, p_session_kind, p_avg_hr, p_facts_json, p_fit_file_id, p_hr_recovery_s | int — число строк | - | - | факты .fit дописывает FitParser; клиент — только ended/status/session_kind/avg_hr |
| workout | workout_select | PLAN | p_athlete_id, p_status, p_date_from, p_date_to | TABLE — карточки + подходы (агрегат jsonb) | - | - | мультикурсор/агрегат: карточка + sets (d-db-6) | | workout | workout_select | PLAN | p_athlete_id, p_status, p_date_from, p_date_to | TABLE — карточки + подходы (агрегат jsonb) | - | - | мультикурсор/агрегат: карточка + sets (d-db-6) |
| workout | workout_get | PLAN | p_athlete_id, p_id | TABLE — одна строка + sets + command_log | - | - | полная карточка (Ф-9.2) | | workout | workout_get | PLAN | p_athlete_id, p_id | TABLE — одна строка + sets + command_log | - | - | полная карточка (Ф-9.2) |
| workout_set | workout_set_insert | PLAN | p_athlete_id, p_client_guid, p_workout_id, p_exercise_id, p_seq, p_weight_kg, p_reps, p_done_at, p_source, p_in_plan, p_rpe, p_knee_pain | int — id | - | - | идемпотентность по client_guid | | workout_set | workout_set_insert | PLAN | p_athlete_id, p_client_guid, p_workout_id, p_exercise_id, p_seq, p_weight_kg, p_reps, p_done_at, p_source, p_in_plan, p_rpe, p_knee_pain | int — id | - | - | идемпотентность по client_guid |
@ -44,3 +48,6 @@
| daily_log | daily_log_select | PLAN | p_athlete_id, p_date_from, p_date_to | TABLE — строки | - | - | для сводки метрик и AdminWeb | | daily_log | daily_log_select | PLAN | p_athlete_id, p_date_from, p_date_to | TABLE — строки | - | - | для сводки метрик и AdminWeb |
| workout_metrics | workout_metrics_upsert | PLAN | p_athlete_id, p_workout_id, p_adherence, p_tonnage, p_tonnage_by_group, p_e1pm_by_exercise, p_hr_metrics, p_red_flags | int — id | - | - | 1:1, пересчёт MetricsEngine | | workout_metrics | workout_metrics_upsert | PLAN | p_athlete_id, p_workout_id, p_adherence, p_tonnage, p_tonnage_by_group, p_e1pm_by_exercise, p_hr_metrics, p_red_flags | int — id | - | - | 1:1, пересчёт MetricsEngine |
| workout_metrics | workout_metrics_select | PLAN | p_athlete_id, p_workout_id, p_date_from, p_date_to | TABLE — строки | - | - | графики AdminWeb (Ф-9.4) | | workout_metrics | workout_metrics_select | PLAN | p_athlete_id, p_workout_id, p_date_from, p_date_to | TABLE — строки | - | - | графики AdminWeb (Ф-9.4) |
| breaks | break_insert | PLAN | p_athlete_id, p_client_guid, p_kind, p_source, p_started_on, p_note | int — id | - | - | auto-ставит сервер по пропускам; manual — телефон |
| breaks | break_update | PLAN | p_athlete_id, p_id, p_ended_on | int — число строк | - | - | закрытие break первой сессией после возврата |
| breaks | break_select | PLAN | p_athlete_id, p_kind, p_date_from, p_date_to | TABLE — строки | - | - | журнал перерывов; CSV-выгрузка (документ перерывов разд. 4) |

View File

@ -11,7 +11,7 @@
| `src.athlete` | сущность | athlete_insert, athlete_update, athlete_select, athlete_get | CUSTOM | без `_delete`: блокировка вместо удаления (`is_blocked`); запись/чтение — владелец и сервисы сервера, не атлет | | `src.athlete` | сущность | athlete_insert, athlete_update, athlete_select, athlete_get | CUSTOM | без `_delete`: блокировка вместо удаления (`is_blocked`); запись/чтение — владелец и сервисы сервера, не атлет |
| `src.athlete_profile` | сущность | athlete_profile_upsert, athlete_profile_get | CUSTOM | 1:1 к `athlete`; upsert вместо insert/update (одна запись) | | `src.athlete_profile` | сущность | athlete_profile_upsert, athlete_profile_get | CUSTOM | 1:1 к `athlete`; upsert вместо insert/update (одна запись) |
| `src.exercise` | сущность | exercise_insert, exercise_update, exercise_delete, exercise_select, exercise_get | CRUD | `_delete` — логический (`is_active=0`, requirements Ф-4.2); функции фильтруют `is_active` по умолчанию | | `src.exercise` | сущность | exercise_insert, exercise_update, exercise_delete, exercise_select, exercise_get | CRUD | `_delete` — логический (`is_active=0`, requirements Ф-4.2); функции фильтруют `is_active` по умолчанию |
| `src.exercise_replacement` | связь ММ | exercise_replacement_insert, exercise_replacement_delete | MM | пара-замена для правила 6 (документ метрик); выборка — внутри `exercise_select` | | `src.exercise_replacement` | связь ММ | exercise_replacement_insert, exercise_replacement_delete | MM | типовой набор связи ММ — d-db-3; пара-замена для правила 6 (документ метрик); выборка — внутри `exercise_select` |
| `src.program` | сущность | program_insert, program_select, program_get | CUSTOM | контейнер версий; без update/delete — жизненный цикл ведут версии | | `src.program` | сущность | program_insert, program_select, program_get | CUSTOM | контейнер версий; без update/delete — жизненный цикл ведут версии |
| `src.program_version` | сущность | program_version_insert, program_version_update, program_version_select, program_version_get | CRUD | `body` jsonb (схема дизайна LLM-планирования разд. 2); 🛡 неизменяемость: `_update` меняет только `status` | | `src.program_version` | сущность | program_version_insert, program_version_update, program_version_select, program_version_get | CRUD | `body` jsonb (схема дизайна LLM-планирования разд. 2); 🛡 неизменяемость: `_update` меняет только `status` |
| `src.workout` | сущность | workout_insert, workout_update, workout_select, workout_get | CRUD | `client_guid` UNIQUE — идемпотентность; `facts_json` — факты .fit | | `src.workout` | сущность | workout_insert, workout_update, workout_select, workout_get | CRUD | `client_guid` UNIQUE — идемпотентность; `facts_json` — факты .fit |
@ -19,6 +19,7 @@
| `src.command_log` | сущность | command_log_insert, command_log_select | CUSTOM | журнал голосовых команд (Ф-1.5, 🛡 не редактируется); update/delete нет | | `src.command_log` | сущность | command_log_insert, command_log_select | CUSTOM | журнал голосовых команд (Ф-1.5, 🛡 не редактируется); update/delete нет |
| `src.daily_log` | сущность | daily_log_upsert, daily_log_select | CUSTOM | день уникален для атлета (`UNIQUE(athlete_id, date)`) — upsert | | `src.daily_log` | сущность | daily_log_upsert, daily_log_select | CUSTOM | день уникален для атлета (`UNIQUE(athlete_id, date)`) — upsert |
| `src.workout_metrics` | сущность | workout_metrics_upsert, workout_metrics_select | CUSTOM | 1:1 к `workout`, считает сервер (MetricsEngine) — телефон только читает; upsert (пересчёт) | | `src.workout_metrics` | сущность | workout_metrics_upsert, workout_metrics_select | CUSTOM | 1:1 к `workout`, считает сервер (MetricsEngine) — телефон только читает; upsert (пересчёт) |
| `src.breaks` | сущность | break_insert, break_update, break_select | CUSTOM | перерывы (Ф-14); закрытие = update (`ended_at`); delete нет — журнал |
## Описания ## Описания
@ -99,13 +100,13 @@
| `started_at` | timestamptz NOT NULL | начало (UTC) | | `started_at` | timestamptz NOT NULL | начало (UTC) |
| `ended_at` | timestamptz NULL | конец | | `ended_at` | timestamptz NULL | конец |
| `status` | text NOT NULL DEFAULT 'active' | `active` \| `finished` \| `cancelled` (Ф-3.5) | | `status` | text NOT NULL DEFAULT 'active' | `active` \| `finished` \| `cancelled` (Ф-3.5) |
| `session_kind` | text NOT NULL DEFAULT 'normal' | `normal` \| `return` \| `deload` \| `test` — интерпретация снижения весов (Ф-14.5) |
| `avg_hr` | int NULL | средний пульс | | `avg_hr` | int NULL | средний пульс |
| `fit_workout_id` | int NULL | сопоставленная тренировка .fit — см. ниже |
| `facts_json` | jsonb NULL | канонический факт из .fit (`workout-facts/1`, tech-requirements 2.3); заполняет FitParser | | `facts_json` | jsonb NULL | канонический факт из .fit (`workout-facts/1`, tech-requirements 2.3); заполняет FitParser |
| `fit_file_id` | text NULL UNIQUE | дедупликация fetch (Ф-8.1) | | `fit_file_id` | text NULL | дедупликация fetch (Ф-8.1); уникальность — составная `UNIQUE(athlete_id, fit_file_id)` |
| `hr_recovery_s` | int NULL | возврат пульса к <90 (Ф-13.4, из .fit) | | `hr_recovery_s` | int NULL | возврат пульса к <90 (Ф-13.4, из .fit) |
`UNIQUE(athlete_id, fit_file_id)`; сопоставление телефон ↔ .fit — окно `UNIQUE(athlete_id, fit_file_id)` — единственная уникальность `fit_file_id`; сопоставление телефон ↔ .fit — окно
±15 минут по `started_at` (Ф-8.4): факты пишутся в ту же строку ±15 минут по `started_at` (Ф-8.4): факты пишутся в ту же строку
(`facts_json`), либо создается строка «только .fit» без `client_guid` (`facts_json`), либо создается строка «только .fit» без `client_guid`
пары — тогда `client_guid` генерирует сервер. пары — тогда `client_guid` генерирует сервер.
@ -156,6 +157,19 @@
| `wellbeing` | int NULL CHECK (1–5) | самочувствие (v2) | | `wellbeing` | int NULL CHECK (1–5) | самочувствие (v2) |
| `client_guid` | uuid NOT NULL UNIQUE | идемпотентность | | `client_guid` | uuid NOT NULL UNIQUE | идемпотентность |
### `src.breaks` — перерывы (Ф-14)
| Поле | Тип | Описание |
|---|---|---|
| `id` | serial PK | — |
| `athlete_id` | int NOT NULL | → `src.athlete.id` |
| `kind` | text NULL | ответ атлета: `illness` \| `vacation` \| `workload` \| `injury` \| `other`; NULL — ещё не отвечен (правила 12–14 ждут ответа) |
| `source` | text NOT NULL DEFAULT 'auto' | 🛡 всегда `auto` — break вычисляется по пропускам (Ф-14.1) |
| `started_on` | date NOT NULL | первый пропущенный день |
| `ended_on` | date NULL | возврат (заполняется первой сессией после break) |
| `note` | text | причина-текст пользователя |
| `client_guid` | uuid NOT NULL UNIQUE | идемпотентность (для manual) |
### `src.workout_metrics` — метрики тренировки (MetricsEngine, 1:1) ### `src.workout_metrics` — метрики тренировки (MetricsEngine, 1:1)
| Поле | Тип | Описание | | Поле | Тип | Описание |

View File

@ -1,12 +1,15 @@
# Технические требования: WatchBody («Феникс 6X») v1 # Технические требования: WatchBody («Феникс 6X») v1
> Шаг 5 процесса разработки (`std1\_process\process.md`). > Шаг 5 процесса разработки (`std1\_process\process.md`).
> Источники: [vision.md](../1-reqs/vision.md) v1.2, > Источники: [vision.md](../1-reqs/vision.md) v1.4,
> [requirements.md](../1-reqs/requirements.md) (утверждён, раздел 7 — > [requirements.md](../1-reqs/requirements.md) (утверждён, раздел 7 —
> открытые вопросы), [architecture.md](../2-arch/architecture.md) > открытые вопросы), [architecture.md](../2-arch/architecture.md)
> (утверждена), дизайн LLM-планирования, документ форматов. > (утверждена), дизайн LLM-планирования, документ форматов.
> Версия 1.1 — **УТВЕРЖДЕНО** (2026-10-17; интеграция документа метрик — > Версия 1.1 — **УТВЕРЖДЕНО** (2026-10-17; интеграция документа метрик —
> Ф-13, Safety-пороги, daily_log — утверждена вместе с документом). > Ф-13, Safety-пороги, daily_log — утверждена вместе с документом).
> Версия 1.2 — черновик (2026-10-17): errata ревью (З-6, З-11) +
> интеграция документа перерывов (Ф-14: сущность `break`,
> `session_kind`). Утверждение — Пользователь!
## 1. Решения открытых вопросов (закрытие раздела 7 requirements) ## 1. Решения открытых вопросов (закрытие раздела 7 requirements)
@ -31,7 +34,7 @@
```json ```json
{ {
"client_guid": "uuid-v4", "client_guid": "uuid-v4",
"entity": "workout | exercise | program_version | profile | workout_set | set_note", "entity": "workout | workout_set | exercise | program_version | profile | daily_log | break",
"op": "upsert", "op": "upsert",
"data": { ...схема сущности... } "data": { ...схема сущности... }
} }
@ -47,12 +50,16 @@
- `exercise`: `exercise_id` (uuid), `name`, `group` (push|pull|legs|core|…), - `exercise`: `exercise_id` (uuid), `name`, `group` (push|pull|legs|core|…),
`is_base` (bool), `is_active` (bool); `is_base` (bool), `is_active` (bool);
- `workout`: `workout_id` (uuid), `started_at`/`ended_at` (UTC ISO 8601), - `workout`: `workout_id` (uuid), `started_at`/`ended_at` (UTC ISO 8601),
`status` (active|finished|cancelled), `program_version_id` (nullable), `status` (active|finished|cancelled), `session_kind`
(normal|return|deload|test — Ф-14.5), `program_version_id` (nullable),
`avg_hr`; `avg_hr`;
- `workout_set`: `workout_id`, `exercise_id`, `seq`, `weight_kg`, `reps`, - `workout_set`: `workout_id`, `exercise_id`, `seq`, `weight_kg`, `reps`,
`done_at`, `source` (voice|watch|edit), `in_plan` (bool), `rpe` (int? 0–10), `done_at`, `source` (voice|watch|edit), `in_plan` (bool), `rpe` (int? 0–10),
`knee_pain` (int? 0–10, только группа «ноги»); `knee_pain` (int? 0–10, только группа «ноги»);
- `profile`: `sex`, `birth_year`, `weight_kg`, `hr_max` (nullable); - `profile`: `sex`, `birth_year`, `weight_kg`, `hr_max` (nullable);
- `break`: `kind` (NULL до ответа атлета), `started_on`, `note`,
`planned_return` — break ставится автоматически (детекция по
пропускам ≥ 4 дней), причина запрашивается у атлета (Ф-14.1–2);
- `daily_log`: `date` (YYYY-MM-DD), `bp_sys`, `bp_dias`, `weight_kg` - `daily_log`: `date` (YYYY-MM-DD), `bp_sys`, `bp_dias`, `weight_kg`
(Ф-13.1; v2 добавит `hr_resting`, `waist_cm`, `sleep_h`, `wellbeing`); (Ф-13.1; v2 добавит `hr_resting`, `waist_cm`, `sleep_h`, `wellbeing`);
- `program_version`: JSON-схема программы из дизайна LLM-планирования - `program_version`: JSON-схема программы из дизайна LLM-планирования

View File

@ -0,0 +1,93 @@
# Перерывы в тренировках и «подводные камни»: правила из книг + рекомендации для программного комплекса
> Дата: 2026-10-17. Источники: МакРоберт «Думай! 2» (циклирование, болезнь, авралы), CDC/Tufts «Growing Stronger» (гл. «Staying on Track»: отпуск/болезнь/срывы), Жижин (гл. 5–6: перерывы, переутомление), Аттия «Пережить» (обратимость адаптации), «10 ошибок начинающих».
> Связано: `program-trenirovok.json` (v4), `Метрики_тренировок_и_самоконтроля.md`, `Архитектура_LLM_планирования_тренировок.md`.
## 1. Что говорят книги о простоях
| Ситуация | Правило | Источник |
|---|---|---|
| Перерыв **4+ дня** между тренировками | «сводит тренировочный эффект к нулю» — нельзя допускать регулярно | Жижин гл. 6 |
| Простуда (1–2 пропущенные тренировки) | Пропустить; при возврате **сбросить 5–10 кг** со штанги, 1–2 недели на возврат к прежним весам. Но если силы сохранились — можно продолжить с прежними: «опыт подскажет» | МакРоберт, гл. о циклах |
| Аврал на работе, недосып, пропущенная неделя | Возобновить **с меньшими весами**, 2 недели на возврат к прежнему уровню | МакРоберт |
| Плановый отдых между циклами | 7–10 дней; после — «фаза разгона»: веса **−10–15%** от освоенных, упор на технику, 1–2 «вводные» недели | МакРоберт (также «Руки титана»: 10-дневные паузы между фазами) |
| Отпуск/болезнь/семья/работа — **1–2+ недели** | «Не чувствовать вины. Вернуться к тренировкам как можно быстрее. Вернуться туда же, где остановился, **может и не получиться — чуть снизить веса**. Потерянное вернётся» | CDC «Staying on Track» |
| Врач запретил нагрузки (болезнь/операция) | Тренировки не начинать до разрешения (PAR-Q — входной скрининг) | CDC (PAR-Q) |
| Митохондриальная форма (зона 2) | «Не используете — теряете», но митохондрии пластичны: регулярный возврат восстанавливает их быстрее, чем набиралось с нуля | Аттия гл. 10 |
| Регулярность как принцип | «Тренироваться ровно и стабильно, без перерывов» — но жизнь вносит перерывы, поэтому их надо **циклировать по науке**, а не героически пересиливать | МакРоберт |
**Вывод из книг:** перерыв — это не провал, а штатная часть цикла. Система координации должна не «прощать» пропуски, а **автоматически переводить программу в режим возврата** с заведомо заниженными весами.
## 2. Смежные «подводные камни» (из тех же книг)
1. **Перетренированность** (Жижин гл. 5–6; МакРоберт): ухудшение настроения, раздражительность, сбитый сон, нежелание тренироваться, утренний пульс +8–10, застой результатов. Лечение — не «пробить», а отдых/лёгкая неделя. У 45+ с гипертонией перетр = прямая угроза АД.
2. **Застой (плато)** (МакРоберт): не добавлять упражнения, а менять число рабочих сетов (1↔2↔3), пересмотреть отдых/сон/еду, при необходимости — полный перерыв на неделю и новый цикл.
3. **Недосып и стресс на работе** (МакРоберт): «нет смысла тренироваться тяжело, если вас преследуют стрессы» — тяжёлая тренировка при <7 ч сна урезается по интенсивности.
4. **Тренировка через боль** («10 ошибок» №4; Шварценеггер кн. 5): острая боль = стоп; боль, проходящая после разминки, — тоже сигнал; колено: 2 занятия подряд с болью → замена упражнения.
_Психологические эффекты (чувство вины после пропуска, «растерянность» после возврата) — осознаваемы пользователем и в автоматическую координацию не тащим; учитываются только в тоне сообщений UI. Ниже — измеримые проблемы._
5. **Возврат «с места в карьер»** (МакРоберт, CDC): типовая травма возвращенца — старт с прежних весов после 2+ недель пропуска. Измеримо: `break_days ≥14` → формульное снижение весов (раздел 3.2).
6. **Праздники/отпуск = еда** (Аттия, Жижин гл. 7): вес за отпуск уходит вверх. Измеримо: тренд веса тела — корректируется дефицит по правилу 8–9 из файла метрик.
7. **Длительная мышечная боль** (МакРоберт): боль дольше недели. Измеримо: флаг боли ≥1 два занятия подряд → облегчённая вторая тренировка (60–70%, 2–3 сета).
8. **Замена упражнений при отсутствии зала** (командировка, отпуск): измеримо по метке типа break = vacation/no_gym → домашний поддерживающий комплекс с ожиданием «поддержание, не прогресс» (Жижин).
## 3. Рекомендации для программного комплекса «Феникс 6X»
### 3.1. Модель состояний пользователя
Ввести конечный автомат поверх программы: `active → break → return → active`.
- **`break`** — ставится автоматически: пропущены все запланированные сессии за N дней (N = 4 по Жижину для «эффект начинает таять»; 10–14 дней — «полноценный простой»), либо вручную пользователем («болею», «отпуск», «аврал», «травма») с указанием типа и предполагаемой даты возврата.
- **`return`** — при первой сессии после break; программа на 1–2 недели (или по формуле ниже) переводится в режим возврата.
### 3.2. Формула снижения при возврате (детерминированная, для валидатора)
`коэффициент весов = f(дней простоя)`, от рабочего веса:
| Простой | Коэффициент | Комментарий |
|---|---|---|
| 4–6 дней | 100%, но первая сессия −1 подход в упражнении и −RPE (цель 5–6) | «опыт подскажет»: если всё легко — сразу к прежним (МакРоберт) |
| 7–13 дней | −10% | простойуд + простуда-граница (МакРоберт: −5–10 кг ≈ −10%) |
| 14–27 дней | −15%, и 1 неделя «вводная» (техника, без отказа) | «фаза разгона» МакРоберта |
| 28–59 дней | −25%, 2 вводные недели | консервативная экстраполяция + CDC «снизить веса» |
| 60+ дней / после болезни с температурой / после операции | перезапуск цикла: −30…−40%, возврат к вводному комплексу месяца 1, обязательный контроль АД первые 2 недели | новый цикл (МакРоберт), CDC: «restart slowly» |
Отдельно: **кардио-зона 2 восстанавливается быстрее силы** (Аттия, митохондриальная пластичность) — возвращать кардио можно с 100% длительности сразу, силу — по формуле.
### 3.3. Правила координации (добавка к 11 правилам из файла метрик)
12. `тип break = болезнь с температурой` → минимум 3 полных дня после нормальной температуры до первой сессии; первая сессия — 50% объёма.
13. `тип break = аврал/недосып` (без пропуска всех сессий) → на этой неделе вместо «большой» тренировки ставится «средняя»: тяжёлые подходы −1, RPE-цель −1 (МакРоберт).
14. `тип break = отпуск с залом недоступен` → выдаётся домашний поддерживающий комплекс (отжимания, приседания без веса/до стула, тяга резинки, планка, ходьба) с пометкой «поддержание, не прогресс» (Жижин: домашний тренинг слабее — ожидания занижаем).
15. `break ≥14 дней` → при возврате контрольные пороги АД ужесточаются на 2 недели (отмена при >145/90 вместо 150/90) — возврат после простоя при гипертонии контролируем строже (Жижин гл. 3).
16. `2+ перерыва ≥7 дней за месяц` → план сокращается структурно: 2 дня/нед вместо 3 (CDC: устойчивость важнее объёма), пока adherence не вернётся к 80%+.
17. `возврат после break ≥14 дней` → тест-сессия: работаем по RPE ≤6, вес = формула, по факту пульса/АД решение о следующей.
### 3.4. UX-рекомендации
- При отметке «болею/отпуск» — сразу показать рассчитанный план возврата (тип, дата, коэффициент весов).
- После break в `rationale` новой версии программы фиксируется причина снижения (журнал изменений) — это же получает LLM-планировщик в сводке.
- «Домашний режим» — заготовленный мини-план, активируется одним переключателем.
- Возврат после травмы (мениск!) — через чек-лист боли (0–10 по упражнениям с ногами) на первой тест-сессии.
### 3.5. Что фиксировать в БД
- Таблица `breaks`: тип (illness/vacation/workload/injury/manual), дата начала/конца, источник (auto/manual), причина-текст.
- В `sessions` — флаг `session_kind: normal | return | deload | test`, чтобы история весов интерпретировалась правильно (снижение при возврате ≠ регресс).
- Связь выполненных сессий с версией программы остаётся (архитектура), но при `return` создаётся **новая версия** программы с формульными весами — неизменяемость версий не нарушается.
## 4. Возможности форматов планов: где это живёт
| Формат | Поддержка простоев/возврата | Вывод |
|---|---|---|
| **Свой JSON программы** (`program-trenirovok.json`) | **Полная.** Поля: `status: break/return` у версии, `session_kind: normal/return/deload/test` у сессии, `weight_factor` у SetPlan, таблица `breaks` в БД. Коэффициенты возврата (3.2) — просто SetPlan с пересчитанными весами в новой версии: неизменяемость версий не нарушается | Основное место логики |
| **FIT (activity)** — запись тренировки | Понятия «перерыв» нет: файл = одна выполненная активность. Метка `return`/`deload` — в имени/notes сессии; факт привязывается к `program_version` на сервере | Хранит факт, не план |
| **FIT (workout)** — план на часы | Один файл = одна тренировка (шаги/повторы/отдых). Расписание недель держит Garmin Connect, но «неделя возврата −15%» не выражается — только готовые workout-файлы с уже пересчитанными весами в шагах | Экспорт сессии `return` возможен: вес зашит в шаги; семантика «это возврат» остаётся в JSON |
| **ZWO / ERG / MRC** | Только интервальные шаги мощность/пульс; силовых данных и циклирования нет | Не подходит |
| **CSV (отчёты)** | Выгрузка журнала `breaks` + adherence | Отчётность |
**Итог:** вся логика простоев — сущность прикладного слоя (JSON + БД + валидатор). Внешние форматы получают только следствие — конкретную сессию с пересчитанными весами; это соответствует п. 5.3 «Форматы_тренировок_и_рекомендации.md» (внутренний формат — JSON, FIT — канал доставки на Garmin).
## 5. Резюме одной строкой
Простой — штатная фаза цикла: система сама детектирует его по пропускам или меткам пользователя, сама считает сниженные веса по длительности простоя (−10%…перезапуск), сама ужесточает контроль АД на возврате; логика живёт в собственном JSON и БД, в форматы Garmin уходит уже готовая сессия.