# m10-phone-ui — план работ (d-dev) > Модуль: TrainingUi — экраны телефона (Compose поверх ядра m09). > Алгоритм: `std1\_rules\d\d-dev.md`. Требования: > [module_reqs.md](module_reqs.md). > Зависимости: m09 (WorkoutCore/LocalDb/SyncQueue/ApiClient/ > SettingsRepository — DONE); голос — m11, часы — m12 (заглушки). | Шаг | Что делаем | Статус | |---|---|---| | 0 | Модульные документы: module_reqs.md / readme.md / SKILL.md | ✅ 2026-10-19 | | 1 | Список единиц (экраны/VM/сервисы/тесты) | ✅ 2026-10-19 | | 1.3 | **Пользователь!** утверждение списка (+ решения по объёму v1) | ✅ 2026-10-19 | | 2 | Спецификации: api_specs (экраны, состояние, сервисы) | ✅ 2026-10-19 | | 2.4 | **Пользователь!** утверждение спецификаций | ⬜ | | 3 | Реализация + тесты + сборка | ✅ 2026-10-19 | | 3.4 | **Пользователь!** утверждение реализации (закрытие модуля) | ✅ 2026-10-19 | | 4 | Завершение: план, примечания | ✅ 2026-10-19 | Статусы: ⬜ не начат · 🔄 в работе · ✅ готов. ## Примечания по ходу работ 1. **Состав реализации** (`app/src/main/java/com/watchbody/phone/`): `WatchBodyApp` (Application + ручной DI-контейнер), `ui/`: MainActivity + навигация 5 вкладок, Theme (тёмная Material 3), Components (StepperPicker/IntPicker с «—» = «не измерено» — merge дневника не затирает пустое; SegmentControl), SyncBanner (WorkInfo + countFlow), экраны Today/Admission/Workout/History/ Programs/Profile/Library/Settings с ViewModel-ями; `core/`: ProgramView, ZoneCalculator, SettingsValidator, RestTimer, PlanFact (чистые). Compose-плагин Kotlin 2.1.10, BOM 2024.12.01, navigation-compose 2.8.5. 2. **Сборка/тесты**: BUILD SUCCESSFUL; APK 12 МБ; unit-тесты **36/36** (m09 CoreLogic 10 + SyncPayload 8; m10: ProgramView 5, ZoneCalculator 4, SettingsValidator 5, RestTimer 2, PlanFact 2). 3. **Живой смок контура программы** (2026-10-19, сервер 5000, тест-ключ test-key-ivan-0001, админ test-admin): sync flat-draft program_version → accepted → admin activate (`?athleteId=286`) → GET `/api/v1/programs/active` — 200, camelCase-ключи, `weeks` на верхнем уровне body. Ошибочная вложенная форма из первого захода отвергнута контрактом (см. п. 4). 4. **Errata контракта, вскрыты смоком** (исправлено, спеки синхронизированы): (а) `GET programs/active` — ключи camelCase, пример m02 spec 05 показывал snake_case (исправлен + errata); (б) `program_version` sync-контракт: поля программы на верхнем уровне data, обязательное числовое `version` — `PayloadFactory. programVersion` m09 переписана (была вложенная `body`); (в) `profile.sex` ∈ `m|f` (валидатор m02) — экран профиля исправлен с `male/female`. Всё — в readme m09 п. 6 и спеках. 5. **UI-решения по ходу**: переименование упражнения — новый `WorkoutCore.renameExercise` (🛡 UUID не меняется, Ф-4.3; `saveExercise` для переименования создавал бы новый UUID, а REPLACE по UNIQUE(name) затёр бы старую строку — проверка имени в ViewModel до записи); вес в форме — пикер с шагом 2,5 кг от плана/последнего фактического; поля дневника «—» = не измерено (merge, Ф-13.1); таймер отдыха — фазы цвет+текст 🛡, автостарт не делается (старт — кнопка/голос m11, Ф-3.3). 6. **Не проверено в среде** (нет эмулятора/устройства): запуск APK и ручной сценарий владельца (критерий 2 приёмки) — на стороне владельца; сетевые вызовы программы проверены curl-ом против живого сервера (п. 3).