Files
av 37413bb551 change razbor-metrik-v-obekty заархивирован
Дельты влиты в openspec/specs (parsing, storage), задача убрана из беклога,
план отражает сделанную часть шага 3.

Не закрыт один пункт: живая доставка с телефона не разобрана — поток молчит
с 17:13, пауза началась до перезапуска сервиса.
2026-08-01 19:03:46 +03:00

82 lines
6.9 KiB
Markdown
Raw Permalink 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.
## Why
Приём работает третьи сутки, но дальше архива данные не идут: 89 доставок лежат
телами `.json.gz`, а в SQLite только строки `delivery`. Ни одна из трёх целей
проекта — агент-медик, трекер тренировок, фитнес-игра — не может прочитать
ничего, потому что читать нечего. Каталог, Read API, MCP и OpenAPI упираются в
эту задачу.
Правила разбора не надо изобретать: они выведены измерением на живом потоке и
записаны в `docs/local-research.md` (находки 30, 33, 35, 36, 38, 39, 41).
Задача — перенести их в код, а не спроектировать заново.
## What Changes
- Секция `metrics` тела доставки разбирается в **точки**: метрика, слой, метка
времени, содержимое как пришло.
- **Слой выводится из выравнивания меток**, а не из заголовка HAE: плотная
метрика (≥10 точек) классифицируется сама, редкая наследует преобладающий
слой доставки. Заголовок `automation-aggregation` непригоден — значение
`Default` соответствует трём разным режимам.
- Точки складываются в **часовые объекты** (`bucket`, ключ `метрика + слой +
час`), содержимое — gzip-BLOB. Запись — read-modify-write со слиянием.
- **Идентичность точки — координаты** (`метрика + слой + начало + конец`; у
точки-измерения конец равен началу). Ключ одной формы для всех точек: под
одной меткой лежит до трёх записей сна, и 33 столкновения происходят внутри
одной доставки, где тай-брейк по времени приёма неприменим. `source` в ключ
не входит: он нестабилен и меняется задним числом. При столкновении
выигрывает **более полная** точка, а не последняя пришедшая.
- **Канонизация с округлением** чисел до ~12 значащих цифр; хеш канонической
формы — детектор изменений, а не ключ.
- `sleep_analysis` разводится на два имени: поэпизодное и суточную сводку —
под одним именем HAE шлёт две несовместимые схемы.
- Метка точки разбирается из локального времени со смещением и хранится в UTC
плюс офсет исходной зоны. Эпоха внутри `heartbeatSeries` **не разбирается** —
серия проходит исходными байтами; RFC 3339 встречается только в
`stateOfMind` и нормируется вместе с ним, отдельной задачей.
- Разбор не влияет на код ответа приёма: непонятое содержимое по-прежнему
даёт 200, исход разбора виден в `delivery.parse_status` и в логе.
Не входит в изменение (сознательно, чтобы задача мерджилась целиком):
- **тренировки и секции с собственными `id`** (`workouts`, `stateOfMind`,
прочие `record`) — отдельная задача, у них другая модель хранения;
- **`healthlog reindex`** — отдельная задача. Следствие: разбираются только
доставки, пришедшие после выката; накопленные 89 доедут пересборкой.
Сходимость на них проверяется скриптом поверх архива, а не командой сервиса;
- **словарь категориальных значений** (переведённые строки → коды HealthKit) —
отдельная задача; пока строка хранится дословно и без кода рядом;
- **род агрегации и каталог разрезов** — отдельная задача, она следующая.
## Capabilities
### New Capabilities
- `parsing`: превращение тела доставки Health Auto Export в точки — вывод
слоя, разбор трёх форматов времени, канонизация содержимого, разделение
схем под одним именем метрики. Отдельно от хранения потому, что импорт
родного экспорта Apple будет другим разбором поверх того же хранилища.
- `storage`: идентичность точки, слияние и хранение часовыми объектами —
координатный ключ, правило разрешения столкновений, хеш как детектор
изменений, признак запечатанного часа.
### Modified Capabilities
Нет: `openspec/specs/` пуст, приём кодом существует, но спекой не описан и в
этом изменении не трогается.
## Impact
- Новые пакеты `internal/hae` (разбор), `internal/canon` (каноническая форма,
полнота, хеш — общий дом для разбора и хранения) и расширение
`internal/store` (объекты).
- Миграция `internal/store/migrations/00003_bucket.sql` — первая миграция после
приёма; вместе с ней заводится `docs/database.md` (ER-схема), которую требует
шаг гейта `er-schema`.
- `internal/ingest` получает шаг разбора после записи в архив; контракт приёма
не меняется.
- Появляется `testdata` с реальными пакетами HAE. Значения в них **вычищаются**:
структура, порядок ключей, форматы времени, неразрывные пробелы в именах
устройств и точность чисел сохраняются, измеренные величины заменяются —
данные о здоровье не попадают под контроль версий.