VEU.WathBody/1-docs/4-modules/m05-progression-rules/api_specs/02-micro-adjust.md

77 lines
5.5 KiB
Markdown
Raw Permalink 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.

# Спецификация 02: микроправка весов — ProgressionRules.EvaluateAfterWorkout
Источники: Ф-12.2–12.3, документ метрик правила 3–5, ограничения —
решение гейта 1.3 (module_reqs разд. 2.4).
## 1. Сигнатура
```csharp
public ProgramVersion? EvaluateAfterWorkout(int athleteId, Guid workoutId)
```
Возврат: созданная draft-версия или null (изменений нет / черновик
уже существует). Дедупликация: rules-draft со статусом `draft` и
`based_on_version ≥` номера активной версии блокирует новые
предложения (покрывает и пользовательские draft поверх активной).
## 2. Входные данные
- тренировка `workout` + подходы `workout_set` (репозиторий m01);
- предыдущие тренировки атлета (`WorkoutFilter`, по убыванию
`started_at`) — для правила 1 («2 тренировки подряд»);
- активная версия `ProgramStore.GetActive(athleteId)` — нет → null;
- библиотека атлета `IExerciseRepository.Select` (с `ReplacementIds`)
— сопоставление и ограничения.
Сопоставление план↔факт: `body.exercises[].exercise_id` ==
`src.exercise.name` (exact, Ordinal); упражнение не найдено → решение
не генерируется, пометка в rationale. `workout_set.exercise_id`
(uuid) → `src.exercise` → имя → элемент body.
## 3. Решения по упражнениям (проверяются по порядку, срабатывает первое)
По каждому упражнению тренировки, присутствующему в активной версии
и активному (`is_active=true`):
| # | Условие | Действие |
|---|---|---|
| 1 | в двух последних тренировках **этого упражнения** все плановые подходы `in_plan=true` и `rpe ≤ 7` (rpe во всех подходах указан) | плановый вес `+BaseStepKg` (`is_base=true`) / `+IsoStepKg` (`is_base=false`) |
| 2 | в последней тренировке упражнения `rpe ≥ 9` в ≥ 2 подходах | плановый вес `× (1 − BackoffPercent/100)` |
| 3 | все плановые подходы закрыты «по плану» и плановые повторения ≥ 15 (верхняя граница диапазона 15–20) | вес `+шаг прогрессии`, плановые повторения → 12 |
| 4 | в последней тренировке ≥ 2 подходов с `reps` < плановых | плановый вес `× (1 − BackoffPercent/100)` |
| 5 | иначе | без изменения |
- «Предыдущая тренировка упражнения» — предыдущая по `started_at`
тренировка атлета, содержащая подходы этого упражнения.
- Результат веса округляется к 0.5 кг (`Math.Round(x*2)/2`).
- Подходы `source=edit` в расчёт не берутся (правка = новая запись,
история: берём исходные voice/watch записи).
- Вес плана `null` (калибровка) → решение не генерируется.
## 4. Ограничения (module_reqs разд. 2.4)
- `is_active=false` → не попадает в draft; если в активной версии
сессия состоит только из неактивных — сессия исключается;
- у исключаемого упражнения есть активная пара-замена
(`ReplacementIds`, замена активна, та же группа) → подставляется
замена (имя замены в `exercise_id` body, веса исходного плана,
пометка в rationale). Примечание (З-40): «та же группа» гарантируется
`exercise_replacement_insert` (проверка на вставке пары); повторная
runtime-проверка в `ApplyExclusion` не дублируется;
- `body.profile.limitations` переносится без изменений.
## 5. Формирование draft
1. Нет ни одного решения с изменением и ни одного исключения/замены →
`null` (черновик не нужен).
2. Deep-copy активного body (JsonNode), применение решений к week 1+
(все недели: v1 ротации нет — одно упражнение = один плановый вес).
3. Дедупликация: существует rules-draft с `based_on_version` == номер
активной версии → `null` (черновик не плодится между sync).
4. `ProgramStore.CreateDraft(athleteId, newBody, "rules", rationale)`;
rationale — человекочитаемо: по упражнению строка
«жим: 100 → 102.5 (+2.5: 2 тренировки подряд закрыты при RPE≤7)»,
«присед: исключено (is_active=false), замена — жим ногами».
🛡 Активная версия не меняется; активации нет (vision 2.4).