Files
av d79189be18 docs: документация переведена на канон av-dev-pm
- беклог и план переехали в docs/tasks (38 задач, 11 целей), слаги
  переименованы с транслита на английские, 85 ссылок поправлены
- conventions.md разобран в docs/conventions/, local-research.md — в
  docs/research/, review-journal.md — в docs/review.md с разделом настройки
  конвейера; заведены security.md, adr/ и .pm.json
- шаг docs.py check добавлен в task gate; поведение в architecture.md помечено
  девятью маркерами долга, database.md получил настройки с числовым значением
2026-08-03 17:14:53 +03:00

81 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
Половина живого потока разбором не покрыта. Замер по 118 доставкам архива:
65 несут `metrics`, 27 — `workouts`, 26 — `stateOfMind`. Тренировки и состояние
разума сохраняются в архив и числятся `partial`, но в витрину не попадают:
трекеру (второй потребитель паспорта) взять тренировку неоткуда, агенту-медику
состояние разума — тоже.
Для `stateOfMind` это дороже, чем для метрик: в родном экспорте Apple секции нет
ни одним типом (находка 46), то есть доставки HAE — её **единственный** источник,
и ретеншен архива без разобранной секции нельзя включать вовсе.
## What Changes
- Разбор покрывает две новые секции тела: `workouts` и `stateOfMind`. Остальные
секции с собственными `id` (`ecg`, `symptoms`, `cycleTracking`, `medications`,
`heartRateNotifications`) остаются непокрытыми намеренно — живьём поток их не
приносил ни разу, и модель под них закладывается, а разбор — нет.
- Две новые таблицы: `workout` (заголовок колонками, всё остальное, включая
маршрут и внутренние ряды, — `payload` дословно) и `record` (секции с
собственным `id`, ключ `kind + id`).
- Сущность с собственным `id` **заменяется целиком**, а не сливается по полям.
Правило замены названо явно: приехавшая версия побеждает, если не теряет
содержания сохранённой; иначе сохранённая остаётся, факт считается и идёт в
`WARN`. При равных наборах полей выигрывает версия из более поздней доставки
**журнала**, а не свёрнутая последней, — иначе живая витрина расходилась бы с
пересборкой молча.
- Сущность несёт провенанс — доставку своей версии и её метку приёма.
- Разбор метки времени принимает второй формат — RFC 3339 в UTC, которым HAE шлёт
`stateOfMind` (находка 16).
- Отпечаток витрины (оракул сходимости `reindex`) покрывает тренировки и записи,
а не одни часовые объекты, и снимается одним снимком базы. Отчёт `reindex`
считает «до и после» по каждой единице хранения и называет «покрыта новая
секция» ожидаемым классом расхождения.
- «Отказ разбора — всё или ничего» распространяется на сущности явно, включая
ветку невыводимого слоя и повтор ключа секции.
- Миграция переводит в `pending` доставки, у которых в списке непокрытых секций
стоят ставшие покрытыми имена, — правило «покрыли секцию — пересверните» уже
записано в спеке хранения.
- **Не входит:** отдача тренировок и записей наружу. Read API в проекте пока нет
вовсе; его форма (конверт ответа, выбор слоя, предел размера) проектируется
задачей `read-api-points`, и вводить два эндпоинта раньше конверта значило бы
задать контракт мимоходом.
## Capabilities
### New Capabilities
Новых нет: тренировка и запись — это то же хранилище и тот же разбор, только
другая единица хранения. Отдельная capability создала бы второй словарь для того
же домена.
### Modified Capabilities
- `parsing`: покрытых секций становится три вместо одной; появляется разбор
сущностей с собственным `id` и второй формат метки времени (RFC 3339),
оставленный предыдущей дельтой явно ненормированным; «всё или ничего»
распространяется на сущности.
- `storage`: появляется вторая единица хранения — сущность с собственным `id`, со
своим правилом замены версии; отпечаток витрины перестаёт быть отпечатком одних
часовых объектов; запрет на данные о здоровье в логах распространяется на
содержимое сущностей.
- `reindex`: счётчики отчёта покрывают все единицы хранения, а «покрыта новая
секция» становится названным классом ожидаемого расхождения.
## Impact
- `internal/hae` — разбор двух секций, второй формат метки, новые счётчики
пропусков.
- `internal/store` — таблицы `workout` и `record`, слияние доставки одной
транзакцией вместе с точками, отпечаток витрины.
- `internal/fold` — счётчики и единственный логирующий чекпоинт свёртки.
- `internal/replay` — ничего, кроме того, что отпечаток стал шире: пересборка
зовёт ту же свёртку.
- Миграция `00007` — две таблицы и перевод `partial`-доставок в `pending`.
- `docs/database.md`, `docs/architecture.md`, `docs/local-research.md` — схема,
правило замены версии и находка о поведении тренировки при переприсылке.
- Фикстуры `internal/hae/testdata` и скрипт их сборки `tmp/research/fixtures.py`:
тренировка с маршрутом и состояние разума — на реальных пакетах с вычищенными
измерениями.