- свёртка спрашивает журнал, встречалось ли имя строго раньше по паре (received_at, id), и пишет WARN с атрибутом uncovered_new; повторные молчат. Признак выводится, а не хранится — реестр был бы второй копией факта - добавлена подкоманда `healthlog uncovered`: перечень накопленного, чтение только на чтение, экранированные имена и названные границы носителя - синк документации: ADR о выводе новизны из журнала, две записи в журнал дефектов, два правила промоутом в конвенции, терминал оператора назван адресатом недоверенного входа
139 lines
13 KiB
Markdown
139 lines
13 KiB
Markdown
## 1. Хранилище: два запроса по существующей колонке
|
||
|
||
- [x] 1.1 `store.SectionsSeenBefore(ctx, names, before DeliveryRef) (map[string]struct{}, error)` —
|
||
какие из имён встречались в доставках, стоящих в журнале строго раньше
|
||
`before`; сравнение парой `(received_at, id)`, чтение через `json_each`,
|
||
`EXISTS` на имя (ранний выход для виденного), вне транзакции записи
|
||
- [x] 1.2 `store.UncoveredSections(ctx, limit)` — перечень: имя, число доставок,
|
||
первая и последняя встреча (метка и идентификатор доставки), порядок по
|
||
имени, предел строк с числом остатка; доставки всех статусов разбора
|
||
- [x] 1.3 Тесты хранилища: имя из более ранней доставки виденное, из более
|
||
поздней — нет, своя же строка в счёт не идёт, доставки одной секунды
|
||
разводятся идентификатором; перечень сходится с независимо посчитанным
|
||
ожиданием; отказавшая доставка в перечень входит; предел вывода срабатывает
|
||
- [x] 1.4 Замер стоимости на **синтетическом** журнале в `./tmp` (~105 тысяч
|
||
доставок, годовой объём): время запроса для виденного и для нового имени;
|
||
число печатается, метод замера записывается рядом с результатом
|
||
|
||
## 2. Свёртка: признак новизны и эскалация
|
||
|
||
- [x] 2.1 Проверка новизны сразу после разбора — до ветвления на успех и отказ,
|
||
**вне транзакции записи**; берётся только при непустом списке непокрытых
|
||
- [x] 2.2 Отказ запроса не роняет свёртку и исходом доставки не становится: все
|
||
имена считаются новыми, признак несостоявшейся сверки идёт в запись
|
||
- [x] 2.3 Новые имена в `Stats` и в атрибутах чекпоинта; ветвь `WARN` ставится
|
||
первой в `switch` (решение кода — спека нормирует поведение, а не позицию)
|
||
- [x] 2.4 Новые имена доезжают до пути отказа (`parseResidue` → `fail`) и
|
||
печатаются его записью; отложенный исход их не называет
|
||
- [x] 2.5 Тесты свёртки: первая встреча даёт `WARN` с атрибутом новых секций,
|
||
вторая доставка молчит, новое имя рядом с виденным, новая секция не
|
||
маскируется перезаписью точек, отказ слоя называет новое имя, отложенный
|
||
исход не называет, отказ сверки объявляет всё новым и не роняет свёртку
|
||
|
||
## 3. Команда перечня
|
||
|
||
- [x] 3.1 Подкоманда `healthlog uncovered --config <path>`: `store.OpenForRead`,
|
||
человекочитаемый вывод в stdout, ненулевой код и причина на отказ открытия
|
||
- [x] 3.2 Имя печатается экранированным (`%q`); пустой перечень — отдельная
|
||
строка и нулевой код; предел строк с числом остатка
|
||
- [x] 3.3 Регистрация подкоманды в `main.go` и в шапке пакета
|
||
- [x] 3.4 Тесты команды: перечень на базе с секциями, пустой перечень, имя с
|
||
управляющим символом, базы по пути нет, имён больше предела
|
||
|
||
## 4. Документация
|
||
|
||
- [x] 4.1 `docs/architecture.md` — раздел «Неразобранные секции доставки»
|
||
дополнен событием, командой и границами носителя
|
||
- [x] 4.2 `docs/research/apple-health.md` — перечень не виденных живьём секций
|
||
сверен с множеством покрытых имён в `internal/hae` поимённо
|
||
- [x] 4.3 `README.md` / `CLAUDE.md` — команда названа там, где перечислены
|
||
прочие подкоманды (если перечислены)
|
||
|
||
## 5. Приёмка
|
||
|
||
- [x] 5.1 `task gate` зелёный
|
||
- [x] 5.2 `task verify:archive` — повторный прогон живого архива даёт то же
|
||
состояние (правило наблюдения затрагивает путь свёртки)
|
||
- [x] 5.3 Поведенческая верификация: сервис поднят, подсунута доставка с
|
||
выдуманной секцией — ровно одна запись с атрибутом новых секций; вторая
|
||
доставка молчит; `healthlog uncovered` на живой базе
|
||
|
||
## 6. Отработка ревью кода (профиль `deep`)
|
||
|
||
- [x] 6.1 Отказ **слияния** доносит новизну до записи об отказе: он пишет имя в
|
||
учёт так же, как отказ разбора, и терял событие навсегда (найдено тремя
|
||
проходами с оракулом). Тест `TestFoldОтказСлиянияНазываетНовуюСекцию`
|
||
- [x] 6.2 Вывод команды называет границы носителя в **обоих** исходах; пустая
|
||
ветвь больше не говорит за весь поток
|
||
- [x] 6.3 Учётная запись с неразбираемым списком не роняет ни сверку, ни
|
||
перечень: проверка стоит внутри аргумента `json_each`, а не условием в
|
||
`WHERE` (порядок вычисления SQLite не обещает)
|
||
- [x] 6.4 Причина несостоявшейся сверки идёт в запись рядом с признаком
|
||
- [x] 6.5 Сообщение чекпоинта приведено к словарю: `delivery folded, new
|
||
uncovered section`
|
||
- [x] 6.6 Число доставок считается `count(DISTINCT d.id)`: обрезка имён по 64
|
||
байтам давала дубли внутри одной доставки
|
||
- [x] 6.7 Замер перемерян и исправлен: ранний выход есть только у давно
|
||
приезжающей секции; у только что появившейся — почти полный проход.
|
||
Числа живут в одном месте (`design.md`), код и `architecture.md` ссылаются
|
||
- [x] 6.8 Сквозной тест команды: перечень сходится с посчитанным по **телам**
|
||
архива независимо от кода команды (оракул К3)
|
||
|
||
## Критерии приёмки задачи
|
||
|
||
Из `docs/tasks/items/unseen-sections-check.md`; оракулы К1–К3 уточнены после
|
||
ревью предложения — наблюдаемый признак события есть **атрибут новых секций**, а
|
||
не присутствие имени в записи (имя непокрытой секции стоит в записи каждой
|
||
доставки, которая её принесла, и так было до этого изменения).
|
||
|
||
- [x] К1 имя секции, которого разбор раньше не встречал, порождает событие
|
||
уровня выше рутины — оракул: тест на доставке с выдуманной секцией, ровно
|
||
одна запись уровня `WARN` с этим именем **в атрибуте новых секций**
|
||
- [x] К2 то же имя во второй доставке события больше не порождает — оракул: тот
|
||
же тест на двух доставках подряд, у второй атрибут новых секций пуст
|
||
- [x] К3 список всего, что поток когда-либо приносил и разбор не покрыл,
|
||
достаётся одной командой, без ручного SQL — оракул: перечень собран из
|
||
**тел** `internal/hae/testdata` независимо от кода команды и сходится со
|
||
строками её вывода (имя, число доставок, границы); дешёвой добавкой —
|
||
сверка с `SELECT DISTINCT` по `delivery.uncovered_sections` на живой базе.
|
||
**Живая база проверку не пропустила по своей причине:** её схема (версия 8)
|
||
отстала от бинаря (10) — контейнер не пересобирался, — и `OpenForRead`
|
||
строго отказал, как и заказано. Сверка сделана на поднятом сервисе с
|
||
отдельной базой; на живой базе непокрытых секций сегодня нет вовсе
|
||
(`SELECT DISTINCT` даёт пустое множество), так что сверять было бы нечего
|
||
- [x] К4 перечень не виденных живьём секций в `docs/research/apple-health.md`
|
||
сходится с множеством покрытых имён в `internal/hae` — оракул: глазами,
|
||
сверка двух списков поимённо
|
||
- [x] К5 повторный прогон живого архива даёт то же состояние — оракул:
|
||
`task verify:archive`
|
||
|
||
## Приёмочные критерии из рубрики ревью (профиль `design`)
|
||
|
||
Рубрика прохода `review-rubric`, порождённая до чтения предложения. Пункты,
|
||
которые предложение не закрывало, отработаны правкой спеки и дизайна; здесь они
|
||
остаются проверяемыми свойствами.
|
||
|
||
- [x] Р1 признак «впервые» есть функция префикса журнала: перестановка порядка
|
||
свёртки не переносит событие с доставки на доставку
|
||
- [x] Р2 узел не читает состояние, которое сам же пишет в этом шаге:
|
||
собственная строка исключена сравнением, а не порядком записи
|
||
- [x] Р3 идемпотентность и отсутствие второй копии факта: повторная свёртка даёт
|
||
тот же состав имён, хранимого реестра нет
|
||
- [x] Р4 наблюдательный механизм не роняет поток: отказ сверки не меняет
|
||
`parse_status` и не превращает `pending` в `failed`
|
||
- [x] Р5 запрос новизны выполняется вне транзакции записи; в транзакции записи
|
||
обращений к `delivery` с `json_each` нет
|
||
- [x] Р6 стоимость названа числом с методом замера (задача 1.4), а не посылкой
|
||
«список почти всегда пуст»
|
||
- [x] Р7 событие однократно на имя: сто доставок подряд с одним новым именем
|
||
дают ровно одну запись повышенного уровня; уровень назван поимённо (`WARN`)
|
||
- [x] Р8 признак и перечень честны относительно носителя: обрезка списка,
|
||
обнуление пересборкой и уход имени после покрытия названы в спеке
|
||
- [x] Р9 имя секции — недоверенный вход: экранировано на выводе, ограничено по
|
||
длине разбором, данных о здоровье в выводе нет
|
||
- [x] Р10 CLI читает и только читает: без миграций и записи, расхождение версии
|
||
схемы — явный отказ
|
||
- [x] Р11 вывод детерминирован, ограничен пределом и отличает пустоту от отказа
|
||
- [x] Р12 оракулы не пришпилены к числу, производному от размера корпуса, и не
|
||
тавтологичны (К3 перестроен от тел архива)
|