## 1. Версия витрины в хранилище - [x] 1.1 Соединение-щуп: `sql.Conn`, взятый по первому запросу версии, поколение (ULID через `internal/ident`), пересоздание с новым поколением только при `sql.ErrConnDone` — обстоятельства поколение не меняют - [x] 1.2 `Store.StateVersion(ctx)` — `PRAGMA data_version` со щупа, метка вида `<поколение>-<счётчик>`; `Store.VersionedRead` — двойная проба вокруг чтения; щуп закрывается раньше пула в `Close` и не воскресает после него - [x] 1.3 Тесты: неизменившаяся база даёт ту же версию; запись из пула её двигает; переоткрытие базы даёт другую версию; щуп пересоздаётся с новым поколением; после `Close` версия отказывает и `-wal` рядом не остаётся; щуп не удерживает читающий снимок ## 2. Обслуживание WAL - [x] 2.1 `journal_size_limit` в DSN рабочего подключения, с причиной в комментарии (пассивный чекпойнт файл не укорачивает) - [x] 2.2 `Store.CheckpointWAL(ctx)` — `PRAGMA wal_checkpoint(PASSIVE)`, наружу тройка чисел (busy, log, checkpointed) без интерпретации - [x] 2.3 Цикл чекпойнта в `cmd/healthlog`: тик в минуту, `WARN` при «страниц больше порога и перенесено меньше», отказ не убивает цикл - [x] 2.4 Запуск и дренирование в `serve`: горутина ждётся в общем бюджете остановки, отдельного чекпойнта на выходе нет - [x] 2.5 Тесты: чекпойнт переносит страницы в тишине; удерживаемый читатель даёт `checkpointed < log` без ошибки; порог молчит на малом журнале; цикл выходит по отмене ## 3. Условный запрос в транспорте - [x] 3.1 Помощник `httpapi`: разбор `If-None-Match` (список, `W/`, `*`), слабое сравнение, `304` без тела — общий для будущих читающих маршрутов - [x] 3.2 Источник версии передаётся транспорту функцией (как `worker.Notify`), проверка токена чтения остаётся раньше условия - [x] 3.3 Тесты помощника на формах заголовка: пусто, список, `W/`, `*`, мусор ## 4. Каталог отдаёт версию - [x] 4.1 `catalog.Service.Metrics` возвращает снимок вместе с версией: проба до, сборка, проба после; расхождение — версии нет - [x] 4.2 `handleMetrics`: `ETag` из версии, `304` по `If-None-Match` без открытия снимка, ответ без `ETag` при расхождении проб - [x] 4.3 Тесты: два ответа подряд — одна метка и одинаковые байты; после свёртки метка другая; `304` не открывает снимок; `401` раньше `304` ## 5. Приёмочные критерии (рубрика ревью дизайна) - [x] 5.1 Равная метка ⟹ побайтово равный ответ (кроме горизонта — назван в дизайне); обратное направление ошибок не допускается ни в одном тесте - [x] 5.2 Ни один новый лог не несёт значений точек, имён метрик без обрезки и секретов; уровень выбран по адресату - [x] 5.3 `task gate` зелёный; `task verify:archive` сходится (обслуживание WAL и версия не меняют витрину) - [x] 5.4 Поведенческая проверка на своём стенде из исходников (отдельный каталог данных, рабочий контейнер не трогаем): `curl` дважды даёт `304`, после доставки — `200` ## 6. Документация - [x] 6.1 `docs/architecture.md`: версия витрины, условный запрос, обслуживание WAL, отвергнутые чужие решения с причинами - [x] 6.2 `README.md`: строка про условный запрос в примерах чтения - [x] 6.3 `docs/backlog`: задача снята, остаток (предел ответа, измеренная цена первого запроса, готовая машинерия условного запроса) перенесён в `read-api-tochki.md`; наблюдаемость — в `stats-nablyudaemost.md`, цена ветки исчерпанного бюджета — в `ostanovka-i-migraciya-sledy.md`