# m09-phone-core — план работ (d-dev) > Модуль: Phone.WatchBody — ядро телефона (Room LocalDb, WorkoutCore, > SyncQueue, ApiClient); UI — m10. > Алгоритм: `std1\_rules\d\d-dev.md`. Требования: > [module_reqs.md](module_reqs.md). > Зависимости: m02 (REST sync-контракт, DONE), m03/m04/m05 (серверные > вычисления — телефон только пишет/читает). | Шаг | Что делаем | Статус | |---|---|---| | 0 | Модульные документы: module_reqs.md / readme.md / SKILL.md | ✅ 2026-10-19 | | 1 | Список единиц (классы/методы/конфиг/тесты) | ✅ 2026-10-19 | | 1.3 | **Пользователь!** утверждение списка (+ решение по SDK) | ✅ 2026-10-19 | | 2 | Спецификации: api_specs (LocalDb-схема, WorkoutCore, SyncQueue, контракт) | ✅ 2026-10-19 | | 2.4 | **Пользователь!** утверждение спецификаций | ✅ 2026-10-19 | | 3 | Реализация + тесты | ✅ 2026-10-19 | | 3.4 | **Пользователь!** утверждение реализации (закрытие модуля) | ✅ 2026-10-19 | | 4 | Завершение: план, примечания | ✅ 2026-10-19 | Статусы: ⬜ не начат · 🔄 в работе · ✅ готов. ## Примечания по ходу работ 1. **Toolchain** (установлен агентом 2026-10-19 в `C:\tools`): JDK 21 (Temurin), Android SDK (platforms;android-36, build-tools 36.0.0), Gradle — wrapper 8.11.1 в проекте. Сборка: `JAVA_HOME`/`ANDROID_HOME` + `./gradlew :app:assembleDebug :app:testDebugUnitTest`. Итог: BUILD SUCCESSFUL, APK 4,2 МБ, unit-тесты **18/18** (CoreLogicTest 10 + SyncPayloadTest 8). 2. **local.properties**: `sdk.dir` — только прямые слэши (`C:/tools/android-sdk`); экранированные бэкслэши дают IOException «Синтаксическая ошибка в имени файла» в SdkLocator (Windows). 3. **Room**: snake_case-имена колонок — через `@ColumnInfo` (31 аннотация, включая PK `sync_pending.client_guid`); Kotlin-поля camelCase, индексы/FK ссылаются на snake_case. 4. **Errata контракта (смок 2026-10-19 против живого сервера, порт 5000, тест-ключ test-key-ivan-0001)**: сервер требует `client_guid`-UUID у ВСЕХ сущностей — daily_log («2026-10-19») и profile («profile») отвергнуты («client_guid отсутствует или не uuid»). Фикс: стабильные `guid`-колонки daily_log/profile (WorkoutCore хранит существующий guid при правках, в конверт идёт он); specs 01/04 синхронизированы, фикстуры тестов обновлены. 5. **Смок итог**: пакет 5 сущностей (exercise/workout/workout_set/ daily_log/break manual) → `accepted` все 5; повтор того же пакета — идемпотентен (workout_set — 1 строка, daily_log — upsert по дате с заменой id на guid); все строки проверены в docker-БД `watchbody-pg` (метрики workout посчитаны: тоннаж 640). Попутно: `break.kind` — только illness/vacation/workload/injury/other («rest» отклонён 400); `break.ended_on` сервер не разбирает — локальное закрытие перерыва в v1 не синхронизируется (возврат выводит серверный BreakEngine), кандидат v2 (spec 04). 6. **Расширение модулем m10** (решение гейта 1.3 m10, утверждено 2026-10-19): `AppSettings`/`SettingsRepository` дополняются ключами `hrMaxBpm` (Hr.MaxBpm) и `hrStrengthMax` (Safety.HrStrengthMax) — оба из tech-req 4.1, в m09 не были заведены; в `SyncWorker` добавляется `scheduleNow(context)` (немедленная отправка без ограничения Wi-Fi — кнопка «Синхронизировать сейчас»; уникальное имя work и ретраи не меняются). Поведение ядра m09 не меняется. По ходу шага 3 m10 добавлены: DAO-чтения для UI (`activeWorkoutFlow`/`setsFlow`/`lastSetForExercise`/ `exercisesFlow`/`activeProgramFlow`/`versions`/`dailyLogFlow`/ `profileFlow`/`countFlow` — чтение напрямую UI разрешено, писатель один); `logSet` — параметр `source` (форма m10 = `edit`, было зашито `voice`); `renameExercise(id, newName)` (🛡 UUID не меняется, Ф-4.3); **errata контракта** — `PayloadFactory.programVersion` переписана под m02 spec 03: поля программы на верхнем уровне data (вложенного `body` нет) + обязательное числовое `version` (значение считает БД); зафиксировано `profile.sex ∈ m|f` (валидатор m02). Живой смок m10 подтвердил оба исправления (accepted + GET active с `weeks` на верхнем уровне).