# m04-metrics-engine — план работ (d-dev) > Модуль: MetricsEngine — расчёт метрик тренировки в > `Server.WatchBody.Core` → `src.workout_metrics`. > Алгоритм: `std1\_rules\d\d-dev.md` (итерационная разработка по > модулям). Требования: [module_reqs.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` падал `InvalidCastException`. Заменено на `DailyLogRepository.AsDate(object)`. 2. **`WorkoutRepository.ParseSets`**: ключи агрегата `workout_select` — snake_case (`in_plan`, `weight_kg`); без `JsonNamingPolicy.SnakeCaseLower` поля `WorkoutSet` молча не заполнялись (case-insensitive подчёркивания не игнорирует). Errata m02 (интеграция метрик в sync): 3. **TransactionScope живёт до конца метода**: пересчёт метрик внутри `using var scope` падал «TransactionScope is already complete» (соединения пристраиваются к завершённой ambient-транзакции). Запись пакета вынесена в `ApplyAll` — scope закрывается до пересчёта. 4. **Kit.Helpers.Log.Logger не настроен** (`_pathLogFolder = null` → `Path.Combine` бросает) — в `SyncService` использован стандартный `ILogger`. 5. В DI добавлены `IWorkoutMetricsRepository`, `ProgramStore`, `MetricsEngine`, `MetricsConfig` (секция `Safety` в appsettings). Решение по порогам АД: коридоры документа метрик перекрываются (отмена >150/**90**, «только кардио» 140–150/**90–95**) — проверка валидации `bpElevatedDias ≤ bpCancelDias` не применяется (spec 01).