свёртка доставки в объекты и сшивка с приёмом
- internal/fold — свёртка по идентификатору доставки, тело из архива: тот же код, каким пойдёт пересборка витрины - приём зовёт свёртку на context.WithoutCancel с собственным дедлайном; исход разбора на код ответа не влияет - слой доставки хранится в delivery.derived_layer и наследуется от ПРЕДШЕСТВУЮЩЕЙ доставки автоматизации: без границы по времени свёртка переставала быть функцией от префикса журнала (1737 объектов против 1742) - task verify:archive — сходимость на живом архиве, 99 доставок из 99
This commit is contained in:
+15
-5
@@ -410,14 +410,24 @@ hour метки выровнены на час heart_rate 00:00:00
|
||||
(находка 31). Правило:
|
||||
|
||||
1. **Плотная метрика** (не меньше десяти точек в доставке) классифицируется
|
||||
**сама по себе** по выравниванию своих меток. У десяти несуммированных
|
||||
точек шанс всем лечь на ровную минуту исчезающе мал.
|
||||
**сама по себе** по выравниванию своих меток — по **самому мелкому**
|
||||
встретившемуся, а не преобладающему: метка ровно на часе одновременно
|
||||
является и минутной, и у плотных метрик они перемешаны (`active_energy` —
|
||||
1320 минутных и 21 часовая). У десяти несуммированных точек шанс всем лечь
|
||||
на ровную минуту исчезающе мал.
|
||||
2. **Редкая метрика** (меньше десяти точек) наследует **преобладающий слой
|
||||
доставки** — самый мелкий среди плотных. У неё выравнивание ничего не
|
||||
доказывает, а Apple многие редкие показатели пишет прямо на границе часа.
|
||||
3. Плотных метрик в доставке нет вовсе — слой наследуется от предыдущей
|
||||
доставки той же автоматизации; если её не было, берём заголовок
|
||||
(`Minutes` → `minute`, `Hours` → `hour`, иначе `raw`).
|
||||
3. Плотных метрик в доставке нет вовсе — слой наследуется от **предшествующей**
|
||||
доставки той же автоматизации; если её не было, берём **надёжный** заголовок
|
||||
(`Minutes` → `minute`, `Hours` → `hour`). Иначе точки не сохраняются вовсе:
|
||||
молчаливый `raw` создал бы призрачный разрез, который поедет в каталог и в
|
||||
правило Read API «самый мелкий слой, покрывающий диапазон».
|
||||
|
||||
Слово «предшествующей» в третьем пункте несёт вес: слой обязан быть функцией от
|
||||
**префикса журнала**. Наследование от последней доставки вообще делает свёртку
|
||||
зависящей от истории, и пересборка даёт не то состояние, что живой приём —
|
||||
поймано прогоном архива, 1737 объектов против 1742 (docs/review-journal.md).
|
||||
|
||||
Классифицировать доставку целиком нельзя: при перенастройке автоматизации
|
||||
приезжают **смешанные доставки**, где часть метрик уже минутная, а часть ещё
|
||||
|
||||
+4
-2
@@ -26,7 +26,8 @@ SQLite (`modernc.org/sqlite`, чистый Go), миграции — goose, фа
|
||||
│ parse_status TEXT │ │ sealed INTEGER │
|
||||
│ points INTEGER │ │ created_at TEXT │
|
||||
│ headers TEXT │ │ updated_at TEXT │
|
||||
└────────────────────────────┘ └──────────────────────────────┘
|
||||
│ derived_layer TEXT │ └──────────────────────────────┘
|
||||
└────────────────────────────┘
|
||||
```
|
||||
|
||||
Связь `bucket.first_delivery_id → delivery.id` **внешним ключом не объявлена**
|
||||
@@ -50,9 +51,10 @@ SQLite (`modernc.org/sqlite`, чистый Go), миграции — goose, фа
|
||||
| `parse_status` | `pending` / `parsed` / `failed`. Код ответа приёма от него **не зависит**: сохранили — значит приняли |
|
||||
| `points` | сколько точек дал разбор |
|
||||
| `headers` | все заголовки запроса JSON-объектом, кроме несущих секреты |
|
||||
| `derived_layer` | слой, выведенный для этой доставки. Нужен не отчётности, а самому выводу: доставка без плотных метрик наследует последний надёжно выведенный слой той же автоматизации, и без хранения этой памяти первая такая доставка после перезапуска осталась бы без слоя |
|
||||
|
||||
Индексы: `delivery_received_at` (порядок журнала), `delivery_sha256` (учёт
|
||||
повторов).
|
||||
повторов), `delivery_automation_layer` (поиск последнего слоя автоматизации).
|
||||
|
||||
## `bucket` — часовой объект точек
|
||||
|
||||
|
||||
+23
-1
@@ -24,4 +24,26 @@
|
||||
|
||||
---
|
||||
|
||||
Пока пусто — конвейер заведён 2026-08-01, задач через него не проходило.
|
||||
## 2026-08-01 — свёртка не воспроизводилась при пересборке журнала
|
||||
|
||||
- **Где:** `internal/store/delivery.go`, `LastDerivedLayer`
|
||||
- **Симптом:** прогон живого архива (99 доставок) вторым проходом дал 1742
|
||||
объекта вместо 1737, а координат сна 182 вместо 174. Нашёл тест сходимости
|
||||
на шаге apply — не ревью.
|
||||
- **Причина:** доставка без плотных метрик наследует слой автоматизации.
|
||||
Запрос брал последний выведенный слой **вообще**, а не последний до этой
|
||||
доставки, поэтому при пересборке доставка наследовала слой «из будущего».
|
||||
Свёртка переставала быть функцией от префикса журнала.
|
||||
- **Почему не поймали:** формулировка «наследует последний надёжно выведенный
|
||||
слой той же автоматизации» звучит однозначно и в спеке, и в дизайне —
|
||||
пропущенное слово «предшествующей» не выглядит пропуском. Проходы `specs` и
|
||||
`architecture` сверяли код со спекой и понятиями, а инвариант
|
||||
«`import + replay` даёт то же состояние» ни один из них не проверял на
|
||||
конкретном правиле: он записан в архитектуре как свойство системы, а не как
|
||||
критерий для каждого узла, читающего состояние.
|
||||
- **Что меняем:** в рубрику `healthlog-review-rubric` и в проход `ops` — вопрос
|
||||
«читает ли узел состояние, которое сам же меняет, и остаётся ли он функцией
|
||||
от префикса журнала». Дешевле правила: любой запрос к `delivery` из свёртки
|
||||
обязан иметь границу по `received_at` разбираемой доставки. Тест сходимости
|
||||
на живом архиве (`internal/fold/replay_test.go`) остаётся постоянным —
|
||||
именно он это поймал.
|
||||
|
||||
Reference in New Issue
Block a user