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

- Правило покрытия получило второй разряд (условный, как у точек), запрет
  вырождения формы и счёт содержательных элементов ряда: скелет из скаляров и
  ряд из 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
+37
View File
@@ -73,3 +73,40 @@
печатать. Гейт при этом не трогаем: цена ежедневной минуты выше цены такой
протухшей константы, а после этой задачи прогон стал ещё и единственным, кто
проверяет настоящий проигрыватель журнала.
## 2026-08-02 — чекпоинт кода прошёл без трёх проходов, и ровно они нашли всё
- **Где:** конвейер, а не код: коммит `f8200f7` («тренировки и записи с
собственным `id`»), шаг 7 скилла `healthlog-task-pipeline`, профиль `deep`.
- **Симптом:** изменение было закоммичено и заархивировано как прошедшее ревью.
Дозапуск трёх пропущенных проходов на **уже закоммиченном** коде дал девять
причин, семь из которых пошли в работу с прогнанными оракулами: скелет из
`null` затирает маршрут молча и необратимо; одно поле не той формы уносит
тренировку, а доставка при этом числится разобранной; откат бинаря поверх
новой схемы стартует без слова; победитель внутри доставки зависит от порядка
элементов на проводе; провенанс устаревает на каждой повторной присылке;
канонизация идёт внутри транзакции вопреки собственному комментарию (768 МиБ
пика, 5.019 с удержания блокировки); тело в 8 МиБ целиком уезжает в текст
ошибки и оттуда в `WARN`.
- **Причина:** сабагент, проводивший задачу, на чекпоинте кода запустил не все
проходы профиля `deep` — не отработали `adversary`, `ops` и архитектурный.
Отчёт триажа при этом был выпущен и выглядел полным: он агрегирует то, что
ему подали, и о непоступивших проходах не знает. Секция границ покрытия
обязана была это назвать, но она заполняется тем же триажем — то есть
единственный, кто мог заметить пропуск, узнаёт о нём из того же источника,
который его допустил.
- **Почему не поймали:** пропуск прохода **не отличим от прохода без находок**.
Гейт зелёный, спеки сошлись, applicative-проходы отработали — снаружи это
выглядит как чистое ревью. Все семь находок принадлежат ровно тем классам,
которые applicative-проходы не достают по построению: враждебно
сконструированный вход (`adversary`), поведение под откатом и конкуренцией
(`ops`), второй способ делать уже сделанное (архитектура). Recall чек-листа
равен длине чек-листа, а этих пунктов в чек-листах нет и быть не может.
- **Что меняем:** отчёт ревью обязан перечислять запущенные проходы **поимённо
и с исходом**, а оркестратор задачи — сверять этот перечень с составом
профиля до того, как коммитить; непущенный проход идёт в границы покрытия
строкой «не запускался», а не отсутствует. Правилом линтера это не
выражается, автоматической проверки нет — но пропуск, названный в отчёте,
стоит одной строки, а пропуск молчащий стоил семи находок и отдельной задачи
на их дозакрытие. Состав проходов и профилей при этом не трогаем: они
сработали ровно так, как задуманы, — их просто не позвали.