- три решения опровергнуты экспериментами: повтор при SQLITE_BUSY не сходится без _txlock=immediate (242 из 800 против 800 из 800), разбор в map[string]any держит 197 МиБ против 54, канонизация без json.Number теряет литерал - канонизации назначен дом: общий internal/canon вместо hae, иначе импорт экспорта Apple потребует второй реализации и хеш-детектор станет бесполезен - вывод слоя вернул шаг наследования, WARN сравнивается только с надёжным заголовком; в bucket возвращены units и границы содержимого
7.2 KiB
7.2 KiB
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 со слиянием. - Идентичность точки — координаты (
метрика + слой + метка).sourceв ключ не входит: он нестабилен и меняется задним числом. При столкновении выигрывает более полная точка, а не последняя пришедшая. - Канонизация с округлением чисел до ~12 значащих цифр; хеш канонической формы — детектор изменений, а не ключ.
sleep_analysisразводится на два имени: поэпизодное и суточную сводку — под одним именем HAE шлёт две несовместимые схемы.- Метка точки разбирается из локального времени со смещением и хранится в UTC
плюс офсет исходной зоны. Эпоха внутри
heartbeatSeriesне разбирается — серия проходит исходными байтами; RFC 3339 встречается только вstateOfMindи нормируется вместе с ним, отдельной задачей. - Разбор не влияет на код ответа приёма: непонятое содержимое по-прежнему
даёт 200, исход разбора виден в
delivery.parse_statusи в логе.
Не входит в изменение (сознательно, чтобы задача мерджилась целиком):
- тренировки и секции с собственными
id(workouts,stateOfMind, прочиеrecord) — отдельная задача, у них другая модель хранения; healthlog reindex— отдельная задача. Следствие: разбираются только доставки, пришедшие после выката; накопленные 89 доедут пересборкой. Сходимость на них проверяется скриптом поверх архива, а не командой сервиса;- словарь категориальных значений (переведённые строки → коды HealthKit) — отдельная задача; пока строка хранится дословно и без кода рядом;
- род агрегации и каталог разрезов — отдельная задача, она следующая;
- хранение поэпизодного
sleep_analysis— вынуто ревью дизайна блокеромidentichnost-epizodnyh-metrik: координатный ключ его схлопывает (измерено: 31 координата в 21 доставке из 89 задвоена внутри одной доставки, где тай-брейк по времени приёма неприменим в принципе). Точки разбираются и считаются, но не сохраняются; тела в архиве, подберёт пересборка. Суточной сводки это не касается.
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. Значения в них вычищаются: структура, порядок ключей, форматы времени, неразрывные пробелы в именах устройств и точность чисел сохраняются, измеренные величины заменяются — данные о здоровье не попадают под контроль версий.