VEU.WathBody/1-docs/2-arch/architecture.md

132 lines
10 KiB
Markdown
Raw 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.

# Архитектура: Феникс 6X v1
> Шаг 4 процесса разработки (`std1\_process\process.md`).
> Источники: [vision.md](../1-reqs/vision.md) (v1.2, изменения 1.1–1.2),
> [requirements.md](../1-reqs/requirements.md) (утверждён 2026-10-17),
> дизайн LLM-планирования (разд. 8), документ форматов.
> Схема компонентов: [hld.puml](hld.puml) (+ рендер [hld.svg](hld.svg),
> [hld.png](hld.png)).
> Версия 1.0 — **УТВЕРЖДЕНО** (2026-10-17).
## 1. Компоненты и размещение
### 1.1. Часы атлета — `FenixDataField` (Monkey C, Connect IQ)
Поле данных «Феникс» в штатном профиле «Силовая». Только отображение
(пульс, средний за подход, время, цвет зоны, вибро) — запись `.fit`
ведёт платформа Garmin (инвариант из requirements, Ф-2.7). Один исходный
код, сборки под целевые модели Garmin — конфигурациями (изменение
vision 1.1); перечень моделей — открытый вопрос 8 требований.
Связь с телефоном — companion-канал Connect IQ (`WatchChannel`,
компонент телефона): live-пульс, зоны, таймер отдыха передаются
в объёме возможностей канала (открытый вопрос 3); отсутствие связи
ничего не ломает — часы автономны.
### 1.2. Телефон атлета — приложение «Феникс» (Android, Kotlin)
- `TrainingUi` — экраны (Jetpack Compose — кандидат, фиксируется
шагом 5): активная тренировка, история, программы, профиль;
тёмная тема, крупные зоны касания.
- `VoiceInput` — офлайн-STT (движок — открытый вопрос 2) + грамматика
команд (Ф-1.2) + TTS-подтверждения; журнал команд для сопоставления
с .fit.
- `WorkoutCore` — логика тренировки и программ: подходы, таймеры,
план/факт, редактор программ; единственный писатель `LocalDb`.
- `LocalDb` — Room/SQLite: текущая тренировка, история, программы,
библиотека, профиль, очередь.
- `SyncQueue` — исходящие записи с `client_guid`, пакеты ≤ 50,
идемпотентная отправка `POST /api/v1/sync`; тихие баннеры статуса.
- `WatchChannel` — companion-канал Connect IQ.
🛡 Офлайн-first: сеть стоит только на пути `SyncQueue`; все остальные
компоненты работают без сети (requirements, раздел 5).
### 1.3. Личный сервер — `Феникс.Server` (C# / .NET 10, PostgreSQL)
Проекты в `3-app\`:
| Компонент | Проект (`3-app\2-cli\`) | Ответственность |
|---|---|---|
| `RestApi` | `VEU.WathBody.Server.Web` | `POST /api/v1/sync`, `GET workouts`, `GET programs/active`; ключ атлета → `athlete_id`; HTTPS |
| `AdminWeb` | `VEU.WathBody.Server.Web` | Консоль владельца (логин/пароль): атлеты, тренировки + ход, программы, планы, метрики, CSV |
| `FitFetcher` | `VEU.WathBody.Server.Fetch` | Клиент Garmin Connect (неофициальный, опросник 4.3); `--mode fetch`; дедупликация по `fit_file_id` |
| `FitParser` | `VEU.WathBody.Server.Import` | Garmin FIT SDK: Session/Lap/Record/Set → `workout_facts`; `--mode import` (fallback: .fit/.tcx/.gpx из папки) |
| `MetricsEngine` | `VEU.WathBody.Server.Core` | adherence, тоннаж по группам, 1ПМ, пульс-метрики → `workout_metrics` |
| `ProgressionRules` | `VEU.WathBody.Server.Core` | Правила прогрессии и делоада → draft-версии программ (`author=rules`) |
| `ProgramStore` | `VEU.WathBody.Server.Core` | Версионированный JSON программ; неизменяемость версий; `active` — ровно один на атлета |
| БД | схема `src` \| `ref` | PostgreSQL: атлеты, тренировки/факты, метрики, программы, журнал LLM (v2) |
Общая библиотека — `Kit.Helpers` (`3-app\1-lib`, std1). Взаимодействие
с LLM (v2, `LlmPlanner`) — по правилам std1 `c-llm`; в v1 компонент
не реализуется (на схеме — серым).
### 1.4. Внешние системы (жёлтым на схеме)
- **Garmin Connect** — облако: источник `.fit` (забор `FitFetcher`
по расписанию — внешний планировщик, открытый вопрос 4).
- **Garmin (устройства)** — запись `.fit` на часах, companion-канал.
## 2. Поток цикла обработки
1. **Тренировка.** Атлет запускает «Силовую» на часах (`FenixDataField`
показывает пульс/зоны/подходы) и активирует голосовой ввод на телефоне
(`VoiceInput` → команды → `WorkoutCore` → `LocalDb`). Часы и телефон
независимы: любой из них работает без другого (🛡).
2. **Фиксация плана.** `WorkoutCore` показывает позицию активной
программы (`RestApi → ProgramStore`, при наличии сети; локальная
копия — офлайн), помечает подходы «по плану/отклонение».
3. **Синхронизация.** `SyncQueue` досылает новые записи на `RestApi`
при появлении сети; сервер пишет их по `client_guid` (идемпотентно)
от имени атлета ключа.
4. **Трек.** Часы синхронизируются с Garmin Connect; `FitFetcher`
по расписанию забирает новые `.fit`, `FitParser` раскладывает
в `workout_facts`; сопоставление с тренировкой телефона — по времени
начала ± 15 минут (Ф-8.4).
5. **Метрики и правила.** `MetricsEngine` считает adherence/тоннаж/1ПМ;
`ProgressionRules` формируют draft-версию следующей программы
(микроправки ±2,5 кг / −5 %, делоад).
6. **Подтверждение.** Draft показывается атлету на телефоне (дифф) или
владельцу в `AdminWeb`; после подтверждения версия становится
`active` (🛡 автоматической смены нет).
7. **Наблюдение.** Владелец видит в `AdminWeb` ход тренировок,
планы/факт, динамику; CSV — выгрузка выборки.
## 3. Границы и контракты
| Граница | Контракт | Открытые вопросы |
|---|---|---|
| Телефон ↔ сервер | REST/HTTPS, `X-Api-Key` (атлет), `client_guid`-идемпотентность, пакеты ≤ 50; формат записей — JSON (документ форматов) | — (спецификация — шаг 5) |
| Телефон ↔ часы | companion-канал Connect IQ: live-пульс, зоны, таймер отдыха; деградация без связи | ОВ-3 (шаг 4) |
| Сервер ↔ Garmin Connect | неофициальный клиент, учётка владельца; деградация — папка импорта `--mode import` | — |
| Сервер ↔ LLM (v2) | structured output, валидатор, офлайн-fallback на правила (дизайн LLM-планирования) | до v2 |
## 4. Точки расширения
- **Модели часов** — новая конфигурация сборки `FenixDataField`
(разметка под экран), код не меняется (изменение vision 1.1).
- **LLM-планирование (v2)** — `LlmPlanner` потребляет сводку
`MetricsEngine` и предлагает версии через `ProgramStore`;
`ProgressionRules` остаются базовым режимом (офлайн-fallback).
- **Трековые виды спорта (v3+)** — расширение `FitParser` (телеметрия)
и `WorkoutCore` (ввод темпа/дистанции); пульс/синхронизация/архив
не меняются (опросник 4.7).
- **Общий справочник упражнений (v2)** — надстройка над изолированными
библиотеками атлетов (ОВ-11 требований).
## 5. Архитектурные инварианты (🛡)
1. **Независимость частей** — часы, телефон, сервер работают при
отсутствии остальных; ни один путь записи не проходит через сеть.
2. **Офлайн-first** — источник истины на телефоне — `LocalDb`; сервер
— архив и планирование.
3. **Идемпотентность** — `client_guid` на телефоне ↔ сервере,
`fit_file_id` на заборе, архивация файлов импорта: повторы не создают
дублей.
4. **Изоляция атлетов** — ключ → `athlete_id`; все выборки API и метрики
— в пределах атлета; веб — только владелец.
5. **Неизменяемость истории** — версии программ и факт-записи не
переписываются; корректировка = новая версия.
6. **LLM не источник истины** — в v1 решений LLM нет; в v2 — только
предложения через валидатор и подтверждение (БР-5).