Дозакрыты находки ревью по слиянию сущностей

- Правило покрытия получило второй разряд (условный, как у точек), запрет
  вырождения формы и счёт содержательных элементов ряда: скелет из скаляров и
  ряд из null больше не затирают маршрут. Победитель внутри доставки стал
  функцией множества версий — общим помощником с точками, — а провенанс
  поднимается и при совпавшем хеше, иначе отложенная доставка возвращала витрину
  к прежнему содержимому.
- Одно поле не того типа больше не уносит сущность, а пропуски видны в учётной
  записи доставки (миграция 00008, NULL = «не измерялось»); каноническая форма
  считается один раз и вне транзакции; откат бинаря поверх новой схемы отказывает
  на старте; текст ошибки разбора не несёт значений из тела.
- Ревью кода профилем deep (девять проходов) нашло две регрессии и обе закрыты:
  безусловный второй разряд запирал законный досчёт навсегда, а выбор победителя
  был квадратичен по числу присланных версий одного ключа.
This commit is contained in:
av
2026-08-02 16:38:18 +03:00
parent 51a5272c96
commit 8331328134
52 changed files with 4921 additions and 481 deletions
+2
View File
@@ -28,6 +28,7 @@ SQLite (`modernc.org/sqlite`, чистый Go), миграции — goose, фа
│ headers TEXT │ │ updated_at TEXT │
│ derived_layer TEXT │ └──────────────────────────────┘
│ uncovered_sections TEXT │
│ skipped_entities INTEGER? │
└────────────────────────────┘
┊ ┌──────────────────────────┐ ┌──────────────────────────┐
┊ │ workout │ │ record │
@@ -69,6 +70,7 @@ SQLite (`modernc.org/sqlite`, чистый Go), миграции — goose, фа
| `points` | сколько точек дал разбор |
| `headers` | все заголовки запроса JSON-объектом, кроме несущих секреты |
| `uncovered_sections` | секции тела, которых разбор не покрыл, JSON-массивом имён; пустой список — `[]`. Ответ на вопрос «что останется потерянным, если тело удалить»: для `stateOfMind` он необратим, в экспорте Apple секции нет. Ретеншен обязан спрашивать его прежде, чем срезать тело |
| `skipped_entities` | сколько сущностей с собственным `id` разбор пропустил (нет `id`, `id` длиннее предела, метка не разбирается, элемент не объект). Вторая половина ответа на «что потеряется, если тело удалить»: список непокрытых секций про пропущенную сущность молчит. **NULL означает «не измерялось»** и нулю не равен — так выглядят доставки, свёрнутые разбором, который пропусков не считал; читатель, принимающий по счётчику необратимое решение, обязан трактовать NULL как «не удалять» |
| `derived_layer` | слой, выведенный для этой доставки. Нужен не отчётности, а самому выводу: доставка без плотных метрик наследует последний надёжно выведенный слой той же автоматизации, и без хранения этой памяти первая такая доставка после перезапуска осталась бы без слоя |
Индексы: `delivery_received_at` (порядок журнала), `delivery_sha256` (учёт