Шаг 4 утверждён; шаг 5: tech-requirements.md (закрытие ОВ, форматы обмена, конфигурация) + спецификация Kit.Helpers

This commit is contained in:
virus-home 2026-10-17 18:54:37 +05:00
parent 112f55c43c
commit 2b3ce40673
4 changed files with 179 additions and 3 deletions

View File

@ -12,8 +12,8 @@
| 1. Инициация разработки | `1-docs\1-reqs\vision.md` (утверждён 2026-10-17, v1.0), `1-docs\0-mng\plan.md` | ✅ |
| 2. Создание рабочей области | `readme.md` системы, структура репозитория, `.gitignore` | ✅ |
| 3. Разработка функциональных требований | `1-docs\1-reqs\requirements.md` (утверждён 2026-10-17) | ✅ |
| 4. Разработка архитектуры | `1-docs\2-arch\architecture.md`, `hld.puml` (+ рендер) | 🔄 |
| 5. Разработка технических требований | `1-docs\3-tech\tech-requirements.md`, спецификации библиотек | ⬜ |
| 4. Разработка архитектуры | `1-docs\2-arch\architecture.md`, `hld.puml` (+ рендер) (утверждена 2026-10-17) | ✅ |
| 5. Разработка технических требований | `1-docs\3-tech\tech-requirements.md`, спецификации библиотек | 🔄 |
| 6. Проектирование баз данных | решение о составе данных (фиксируется здесь) | ⬜ |
| 7. Итерационная разработка | `3-app\` (код), `1-docs\4-modules\` (документы модулей) | ⬜ |

View File

@ -6,7 +6,7 @@
> дизайн LLM-планирования (разд. 8), документ форматов.
> Схема компонентов: [hld.puml](hld.puml) (+ рендер [hld.svg](hld.svg),
> [hld.png](hld.png)).
> Версия: черновик 1 (2026-10-17).
> Версия 1.0 — **УТВЕРЖДЕНО** (2026-10-17).
## 1. Компоненты и размещение

View File

@ -0,0 +1,39 @@
# Спецификация потребления: Kit.Helpers
> Спецификация разделяемой библиотеки в объёме потребления системы
> VEU.WathBody (шаг 5 процесса). Библиотека развивается вне проекта
> (`3-app\1-lib\Kit.Helpers`, net10.0, Npgsql 10) — здесь фиксируется
> только контракт использования; исходники не изменяются.
## Потребляемые компоненты (v1)
| Компонент | Использует | Назначение в VEU.WathBody |
|---|---|---|
| `Db.Postgres.SqlServerHelper` | `FitParser`, `MetricsEngine`, `ProgramStore`, `RestApi`, `AdminWeb` | Единственный путь к PostgreSQL: выполнение хранимых функций слоя `src`/`ref` (шаг 6) |
| `SqlServerHelperFluent` | те же | Fluent-обёртка вызовов функций |
| `Log.Logger` | все серверные компоненты | Файловые логи; `Log.Path`, уровень из конфигурации |
| `JsonHelper` | `ProgramStore`, `FitParser` | Сериализация/десериализация версий программ и фактов (`facts_json`) |
## Контракты
- **БД:** соединение — из `Db.ConnectionString`; каждый вызов функции БД —
короткое соединение (без долгоживущих транзакций); ошибки — исключения
библиотеки, оборачиваются компонентом в лог + код ответа API.
- **Логи:** уровень из конфигурации; в лог не пишутся ключи, пароли
и хэши (инвариант «Тайны», requirements раздел 5); ротация — средствами
Logger по дням.
- **JSON:** `JsonHelper` — единственная точка сериализации программ
(стабильный порядок полей — диффы версий сравнимы).
## Не потребляется в v1
`Cache` (нет кэшируемых справочников большого объёма), `Http.*`
(Fetcher реализует собственный клиент Garmin — FluentHttpClient может
быть подключён в итерации 2 после прототипа), `Session`, `HttpContext`.
## Версии и обновления
Фиксируется прямой ссылкой на проект `3-app\1-lib\Kit.Helpers` (monorepo);
обновления библиотеки — только после прогона тестов сервера
(`c-tst`); Breaking-изменения API библиотеки — повод пересмотреть
настоящую спецификацию.

View File

@ -0,0 +1,137 @@
# Технические требования: VEU.WathBody («Феникс 6X») v1
> Шаг 5 процесса разработки (`std1\_process\process.md`).
> Источники: [vision.md](../1-reqs/vision.md) v1.2,
> [requirements.md](../1-reqs/requirements.md) (утверждён, раздел 7 —
> открытые вопросы), [architecture.md](../2-arch/architecture.md)
> (утверждена), дизайн LLM-планирования, документ форматов.
> Версия: черновик 1 (2026-10-17).
## 1. Решения открытых вопросов (закрытие раздела 7 requirements)
| # ОВ | Решение | Обоснование |
|---|---|---|
| 1. Максимальная ЧСС | Формула Танаки: `HRmax = 208 − 0,7 × возраст`; ручной ввод `Hr.MaxBpm` перекрывает расчёт | Точнее для 45+ (профиль пользователя); ручной ввод — окончательная истина |
| 2. Офлайн-STT | **Vosk** (малая русская модель ~50 МБ) + узкая грамматика команд (Ф-1.2: числа и слова «подход/вес/отдых/секунд»); TTS-подтверждения — системный Android TTS | Работа без сети, малый словарь → устойчивость к шуму; sherpa-onnx — запасная кандидатура, если точность < КУ-1 на контрольной тренировке |
| 3. Канал телефон ↔ часы | Connect IQ **Communications SDK** (companion-канал через приложение Garmin Connect Mobile на телефоне). Передаётся: текущий пульс (≈1 Гц), зона, таймер отдыха (секунды до конца), заряд часов. Ограничение: на телефоне должно быть установлено и залогинено Garmin Connect Mobile; канал может не доставлять — обе стороны деградируют молча (Ф-2.4, Ф-3.2) | Единственный штатный канал сторонних приложений Garmin; проверка объёма передачи — прототипом в итерации 1 шага 7 (критерий: пульс и таймер доходят при экране часов активным) |
| 4. Расписание fetch | Внешний планировщик ОС: systemd timer (Linux-сервер) / Планировщик задач (Windows) вызывает `--mode fetch`; внутри сервера планировщика нет | Проще, надёжнее, не дублирует средства ОС |
| 5. Формула 1ПМ | **Эпли**: `1ПМ = w × (1 + reps/30)` | Стандарт индустрии; при reps ≤ 10 различие с Бжицки < 2 % |
| 6. UI телефона | **Jetpack Compose** + Material 3 (тёмная тема по умолчанию) | Актуальный стек Kotlin; крупные зоны касания — Min touch target 48 dp (Ф-3.6) |
| 7. Модели часов v1 | Таргет `fenix6` (Fenix 6/6S/6X, включая Pro/Sapphire — одно разрешение 260×260); добавление моделей — новыми таргетами по фактическому парку (шаг 7) | Якорное устройство ТЗ; 6S/6X отличаются размером корпуса, не экраном |
| 8. Сопоставление телефон ↔ .fit | Окно ± 15 минут по времени начала (Ф-8.4); при нескольких кандидатах — ближайший по времени | ✅ закрыт в requirements |
## 2. Форматы обмена
### 2.1. Конверт синхронизации (телефон → сервер)
`POST /api/v1/sync`, JSON-массив записей (пакет ≤ 50), каждая:
```json
{
"client_guid": "uuid-v4",
"entity": "workout | exercise | program_version | profile | workout_set | set_note",
"op": "upsert",
"data": { ...схема сущности... }
}
```
Ответ: `{ "accepted": ["guid1", ...] }` (все или ничего — пакет
атомарен; 409 при конфликте версии программы — перезапись на телефоне
после перечитывания). Повтор пакета безопасен: сервер отвечает тем же
`accepted` по уже принятым GUID (идемпотентность, Ф-7.4).
### 2.2. Сущности `data` (ключевые поля)
- `exercise`: `exercise_id` (uuid), `name`, `group` (push|pull|legs|core|…),
`is_base` (bool), `is_active` (bool);
- `workout`: `workout_id` (uuid), `started_at`/`ended_at` (UTC ISO 8601),
`status` (active|finished|cancelled), `program_version_id` (nullable),
`avg_hr`;
- `workout_set`: `workout_id`, `exercise_id`, `seq`, `weight_kg`, `reps`,
`done_at`, `source` (voice|watch|edit), `in_plan` (bool);
- `profile`: `sex`, `birth_year`, `weight_kg`, `hr_max` (nullable);
- `program_version`: JSON-схема программы из дизайна LLM-планирования
(разд. 2) + `athlete_id` (выводится из ключа, в теле не передаётся).
### 2.3. Канонический JSON тренировки (сервер, из .fit)
Из `FitParser` → `workout_facts.facts_json`: `session` (тип, старт,
длительность), `laps[]` (индекс, старт, длительность), `records[]`
(`t_offset_s`, `hr`), `sets[]` (упражнение/повторы/вес — если писались),
`fit_file_id`, `device`. Схема версионная: `"schema": "workout-facts/1"`.
### 2.4. CSV-выгрузка (AdminWeb)
Разделитель — `;`, кодировка UTF-8 c BOM (Excel), заголовок первой
строкой: `athlete;date;duration_min;avg_hr;sets;exercises;adherence_pct`.
Строка — тренировка; выборка = текущий фильтр таблицы.
## 3. Спецификация потребления Kit.Helpers
Библиотека — `3-app\1-lib\Kit.Helpers` (std1, net10.0, Npgsql 10);
потребление без изменений исходников (полная спецификация —
[kit-helpers-spec.md](kit-helpers-spec.md)):
- `Db.Postgres.SqlServerHelper` — доступ к PostgreSQL (единственный
путь к БД из кода сервера; строка подключения — `Db.ConnectionString`);
- `Log.Logger` — файловые логи сервера (`Log.Path`, уровень `Info`);
- `JsonHelper` — сериализация фактов/программ;
- остальное (Cache, Http, Session) в v1 не потребляется.
## 4. Схема конфигурации и правила валидации
### 4.1. Телефон (Preferences)
| Параметр | Тип | Валидация |
|---|---|---|
| `Server.BaseUrl` | string | абсолютный https:// URL — иначе экран настроек не даёт сохранить |
| `Server.ApiKey` | string | непустой |
| `Sync.WifiOnly` | bool | — |
| `Hr.MaxBpm` | int? | 120–240 или пусто (расчёт по Танаке) |
| `Rest.DefaultSec` | int | 30–600 |
### 4.2. Сервер (`appsettings.json`)
| Параметр | Тип | Валидация (startup, `--mode *`) |
|---|---|---|
| `Api.Key` | string | непустой; уникальность среди `Api.Athletes[].apiKey` |
| `Api.Athletes` | list | `{name}` непусто, `{apiKey}` непустой; дубликаты ключей — фатально |
| `Admin.Login / Admin.PasswordHash` | string | непустые; хэш — PBKDF2 (100k итераций), префикс `pbkdf2$` |
| `Db.ConnectionString` | string | тест подключения при старте — фатально при неудаче |
| `Garmin.Login/Password` | string | обязательны для `--mode fetch` (иначе код возврата 1) |
| `Garmin.DownloadDir / Import.Dir / Import.ArchiveDir` | path | создание при старте; недоступность — фатально |
| `Progression.BaseStepKg` | double | 0,5–10 |
| `Progression.BackoffPercent` | int | 1–20 |
| `Deload.MaxWeeks` | int | 3–12 |
| `Log.Path / Log.Level` | — | уровень: Debug/Info/Error |
Валидация конфигурации выполняется при старте любого режима; ошибки —
в лог и stderr, код возврата 1 (раздел 2.3 requirements).
## 5. Технические решения по компонентам
- **`FitFetcher`**: реализация логина Garmin Connect (SSO-последовательность)
на C# — по контракту семьи библиотек `garminconnect` (подписи запросов,
OAuth1-заголовок); при блокировке — решение ОВ шага 7 (fallback: папка
импорта уже работает независимо). Пометка прототипа итерации 1.
- **`FitParser`**: официальный **Garmin FIT SDK for C#**; парсинг —
потоковый, файл читается один раз; сбой одного файла не останавливает
пакет (код возврата 2).
- **`SyncQueue`**: таблица Room `sync_pending(guid, entity, payload,
created_at)`; отправка — WorkManager (сетевое ограничение учитывает
`Sync.WifiOnly`); ретрай — экспоненциально, 1 мин → 64 мин.
- **`VoiceInput`**: Vosk-API (Kaldi) как service в процессе приложения;
грамматика — JSON-словарь `[«подход», «вес», «отдых», «на», «секунд»,
0–999]`; числа — постобработка («сто двадцать» → 120).
- **`AdminWeb`**: Razor Pages, сессия по cookie после логина;
пароль — PBKDF2 (см. 4.2); графики — Chart.js (локальный ассет,
без CDN).
- **HTTPS**: обратный прокси (nginx/Caddy) терминирует TLS; Kestrel —
только localhost — вопрос развёртывания, вне кода.
## 6. Чек-лист шага 7 (проверки прототипами)
1. Companion-канал: пульс/таймер доходят на телефон (ОВ-3) — итерация 1.
2. Логин Garmin Connect из C# (FitFetcher) — итерация 2.
3. Vosk: точность команд в шумном зале ≥ КУ-1 — итерация 2.
4. Сопоставление телефон ↔ .fit на реальной тренировке — итерация 3.