VEU.WathBody/1-docs/4-modules/m04-metrics-engine
virus-home f232d592a4 Этап 7: итерации 1–2 — m01–m05 серверной части + обе волны ревью 2026-10-18 (З-35…З-42)
Модули (d-dev, все закрыты): m01-server-db (38 psql-функций, 13 репозиториев,
psql 123/123), m02-rest-api (sync/workouts/programs + middleware),
m03-program-store (версии draft→active→archived), m04-metrics-engine
(метрики + 6 красных флагов, Safety.*), m05-progression-rules
(ProgressionRules + BreakEngine: правила 1–5, делоад, перерывы/возврат,
фильтр ограничений is_active/замены; Web 81/81 + Core 56/56).

Волны ревью: З-35 external_id до артефактов (миграция 003, program_insert
rev.2 с DROP старой сигнатуры); З-36 --mode migrate (Program.cs,
MigrationRunner, Db.MigrationsDir); З-37 tech-req 4.2 к факту (BpElevated*,
фактические параметры, camelCase); З-38 шлюзы m05; З-39 примечание Ф-14.2
(illness → перезапуск); З-40/З-42 документация к факту (dto, SqlHelperAbstract,
spec m05/m04); З-41(а) is_active=false в sync → exercise_delete (Ф-4.2).

Инфраструктура: docker watchbody-pg (PostgreSQL 17, порт 5433); Kit.Helpers
в 3-app/1-lib (не изменялся). Эксплуатационные файлы (program-trenirovok.*,
program-import.json, presentation-trenirovok.html, журнал метрик) в коммит
не включены.
2026-10-18 20:11:40 +05:00
..
api_specs Этап 7: итерации 1–2 — m01–m05 серверной части + обе волны ревью 2026-10-18 (З-35…З-42) 2026-10-18 20:11:40 +05:00
SKILL.md Этап 7: итерации 1–2 — m01–m05 серверной части + обе волны ревью 2026-10-18 (З-35…З-42) 2026-10-18 20:11:40 +05:00
module_reqs.md Этап 7: итерации 1–2 — m01–m05 серверной части + обе волны ревью 2026-10-18 (З-35…З-42) 2026-10-18 20:11:40 +05:00
readme.md Этап 7: итерации 1–2 — m01–m05 серверной части + обе волны ревью 2026-10-18 (З-35…З-42) 2026-10-18 20:11:40 +05:00

readme.md

m04-metrics-engine — план работ (d-dev)

Модуль: MetricsEngine — расчёт метрик тренировки в Server.WatchBody.Core → src.workout_metrics. Алгоритм: std1\_rules\d\d-dev.md (итерационная разработка по модулям). Требования: module_reqs.md. Зависимости: m01 (репозитории, DONE), m02 (sync — точка вызова, DONE), m03 (ProgramStore — активная версия для adherence, DONE).

Шаг Что делаем Статус
0 Модульные документы: module_reqs.md / readme.md / SKILL.md ✅ 2026-10-18
1 Список единиц (классы/методы/конфиг/тесты) ✅ 2026-10-18
1.3 Пользователь! утверждение списка ✅ 2026-10-18
2 Спецификации: api_specs (контракт расчёта, пороги, флаги) ✅ 2026-10-18
2.4 Пользователь! утверждение спецификаций ✅ 2026-10-18
3 Реализация + тесты ✅ 2026-10-18 (тесты Web 61/61 + Core 55/55 на момент закрытия модуля; после З-35 — 56/56)
3.4 Пользователь! утверждение реализации (закрытие модуля) ✅ 2026-10-18
4 Завершение: план, примечания ✅ 2026-10-18 — Модуль завершён (2026-10-18). Следующий по плану этапа 7 — m05-progression-rules (итерация 2).

Статусы: ⬜ не начат · 🔄 в работе · ✅ готов.

Примечания по ходу работ

Errata m01 (найдено тестами m04, исправлено):

  1. DailyLogRepository (и break-часть файла): Npgsql возвращает колонки date как DateOnly, а не DateTime — каст через Convert<DateTime> падал InvalidCastException. Заменено на DailyLogRepository.AsDate(object).
  2. WorkoutRepository.ParseSets: ключи агрегата workout_select — snake_case (in_plan, weight_kg); без JsonNamingPolicy.SnakeCaseLower поля WorkoutSet молча не заполнялись (case-insensitive подчёркивания не игнорирует).

Errata m02 (интеграция метрик в sync):

  1. TransactionScope живёт до конца метода: пересчёт метрик внутри using var scope падал «TransactionScope is already complete» (соединения пристраиваются к завершённой ambient-транзакции). Запись пакета вынесена в ApplyAll — scope закрывается до пересчёта.
  2. Kit.Helpers.Log.Logger не настроен (_pathLogFolder = null → Path.Combine бросает) — в SyncService использован стандартный ILogger<SyncService>.
  3. В DI добавлены IWorkoutMetricsRepository, ProgramStore, MetricsEngine, MetricsConfig (секция Safety в appsettings).

Решение по порогам АД: коридоры документа метрик перекрываются (отмена >150/90, «только кардио» 140–150/90–95) — проверка валидации bpElevatedDias ≤ bpCancelDias не применяется (spec 01).