- `healthlog reindex` собирает витрину из журнала (тела архива + учёт доставок) в ОТДЕЛЬНЫЙ файл базы, строго по `(received_at, id)`; рабочую базу читает без наката миграций и не трогает вовсе. Подмену делает человек при остановленном сервисе: переименование поверх открытого дескриптора портит базу молча. - Журналом считается архив, а не таблица доставок: тело без учётной записи заводится заново (метка из ULID, размер и хеш по распакованному телу), запись без тела переносится, но не сворачивается. Оракул сходимости встроен — два отпечатка и «объектов было/стало»; пустой журнал успехом не считается. - Прогон живого архива переехал на новый пакет: второго проигрывателя журнала в проекте не осталось, а его утверждение о ключе сна перестало быть константой, протухающей с каждой доставкой.
6.8 KiB
6.8 KiB
Журнал проскочивших дефектов
Сюда попадает дефект, который прошёл ревью и всплыл позже. Записывается сразу, а не ретроспективно: со временем теряется не сам факт, а причина непоймания — единственное, ради чего журнал существует.
Реализованные задачи, находки ревью и решения сюда не пишутся: у них есть коммит, спека и беклог. Здесь только промахи конвейера.
Форма записи:
## 2026-08-01 — <краткое последствие>
- **Где:** internal/store/bucket.go:120
- **Симптом:** <как обнаружилось, кем и когда>
- **Почему не поймали:** <какой проход обязан был найти и что ему помешало>
- **Что меняем:** <правило прохода, шаг гейта, конвенция — либо «ничего, цена
поимки выше цены дефекта»>
Последний пункт важнее остальных. Вывод «ничего не меняем» — законный исход: не всякий дефект стоит того, чтобы усложнять ради него ревью каждой задачи.
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) остаётся постоянным — именно он это поймал.
2026-08-02 — прогон живого архива был красным и об этом никто не знал
- Где:
internal/fold/replay_test.go(перенесён вinternal/replay/archive_test.go) - Симптом: первый же запуск
task verify:archiveв задаче про пересборку далкоординат sleep_analysis 222, измерено 174. Проверено прогоном прежней редакции теста на том же архиве: она даёт ровно те же 222, 2049 объектов и тот же отпечаток — значит тест покраснел не от изменений задачи, а сам, когда архив дорос с 94 доставок до 116. - Причина: утверждение было пришпилено к числу, производному от корпуса (174 координаты сна). Корпус растёт с каждой доставкой, то есть константа протухает по расписанию телефона. Проверяемое свойство при этом другое и от размера корпуса не зависит: ключ по интервалу не схлопывает записи до ключа по метке (222 координаты против 218 меток).
- Почему не поймали: прогон живого архива намеренно не входит в
task gate(минута работы, данные есть только на этой машине). У проверки, которую гейт не гоняет, краснота никому не видна — она обнаруживается только следующей задачей, которая до неё дотянется. Ни один проход ревью прогон не запускал: проходы читают код, а не гоняют опциональные команды. - Что меняем: утверждение переписано на само свойство (координат строго
больше, чем различных меток), измеренные числа остались в
t.Logf. Правило общее и годится в конвенции: в проверке на живом корпусе нельзя утверждать число, производное от размера корпуса — утверждать надо инвариант, а число печатать. Гейт при этом не трогаем: цена ежедневной минуты выше цены такой протухшей константы, а после этой задачи прогон стал ещё и единственным, кто проверяет настоящий проигрыватель журнала.