# Спецификация: вызов MetricsEngine из sync (m02) > Ф-12.1: «после каждой синхронизированной тренировки MetricsEngine > считает метрики». Точка вызова — `SyncService.Process` > (Server.WatchBody.Web) после `scope.Complete()`. ## 1. Поток 1. `WebAppStartup`: `MetricsEngine` регистрируется синглтоном (те же репозитории, что у sync); `MetricsConfig` — синглтоном из конфига. 2. `SyncService.Process`: собирает список уникальных `workout_id`, затронутых пакетом (элемент `entity=workout`, успех = id из `workout_insert`/повтор существующего). 3. После `scope.Complete()` (транзакция пакета зафиксирована) — для каждого затронутого `workout_id`: `metricsEngine.ComputeForWorkout(athleteId, workoutId)` в `try/catch (Exception)`: - успех — Debug-лог; - исключение — Warning-лог с `workout_id` и сообщением, обработка пакета продолжается (🛡4: sync остаётся принятым, ответ 200 не меняется). 4. Пакет без `workout`-элементов → MetricsEngine не вызывается. ## 2. Пересчёт Повторный sync того же пакета (идемпотентность) повторяет расчёт — upsert обновляет строку, `computed_at` свежее (🛡2, приёмка №2). ## 3. Что не меняется Контракт POST /api/v1/sync (spec m02 03-sync-post) — без изменений: метрики в ответ sync не входят; телефон читает их отдельным средством (workout card / v2 endpoint — вне m04).