Files
healthlog/openspec/changes/archive/2026-08-02-katalog-i-rod-agregacii/tasks.md
T
av d79189be18 docs: документация переведена на канон av-dev-pm
- беклог и план переехали в docs/tasks (38 задач, 11 целей), слаги
  переименованы с транслита на английские, 85 ссылок поправлены
- conventions.md разобран в docs/conventions/, local-research.md — в
  docs/research/, review-journal.md — в docs/review.md с разделом настройки
  конвейера; заведены security.md, adr/ и .pm.json
- шаг docs.py check добавлен в task gate; поведение в architecture.md помечено
  девятью маркерами долга, database.md получил настройки с числовым значением
2026-08-03 17:14:53 +03:00

11 KiB
Raw Blame History

1. Схема

  • 1.1 Миграция 00009_bucket_catalog.sql — покрывающий индекс bucket(metric, layer, hour_utc, first_ts, last_ts, points, units)
  • 1.2 Обновить docs/database.md: индекс и зачем он

2. Хранилище

  • 2.1 store.ReadCatalog(ctx, store.CatalogWindow) — весь вход каталога одной транзакцией чтения: разрезы всех метрик, общие часы пары слоёв по каждой метрике, объекты окна обоих слоёв
  • 2.2 Разрезы — одним запросом metric, layer, units, min(first_ts), max(last_ts), sum(points), группировка вместе с единицами (расхождение видно, а не выбирается молча)
  • 2.3 Общие часы — самые свежие часы с объектами обоих слоёв, от свежих к старым, не больше window
  • 2.4 Объекты окна — пакетом, один запрос на метрику на оба слоя (hour_utc IN (…)), а не по объекту за раз
  • 2.5 Тест: разрезы и общие часы отвечают по индексу (EXPLAIN QUERY PLAN не содержит обхода таблицы)
  • 2.6 Тест: число обращений к базе на метрику не зависит от размера окна
  • 2.7 Тест: CommonHours отдаёт не больше window и именно свежие часы

3. Значение точки

  • 3.1 hae.PointValue(raw) — число точки: qty, при его отсутствии Avg; разбор через *json.Number, ноль — значение, отсутствие ключа и null — нет значения, нечисловое и не влезающее в float64 (ErrRange, ±Inf) — нет значения
  • 3.2 В док-комментарии сказать, что словарь пустоты canon сюда не применяется, и почему
  • 3.3 Тесты на реальных формах точки из testdata: heart_rate с Min/Avg/Max без qty, {"qty":0}, точка без числового поля, 1e400

4. Измерение рода

  • 4.1 Пакет internal/catalog: тип рода (cumulative/instant/unknown, пустое значение невыразимо) и основание измерения (hours/compared/agreeing/conflicting/границы окна)
  • 4.2 Именованная константа допуска 1e-9 с измеренной ценой в комментарии и один предикат close(a, b), которым выражены все три сравнения
  • 4.3 Пригодность часа: ровно одна точка со значением у часового объекта, её метка совпадает с началом часа, ≥2 точек со значением у минутного, сумма отличима от среднего
  • 4.4 Сумма минутных значений считается в порядке возрастания метки
  • 4.5 Правило метрики: ≥3 согласных и 0 противоречащих, иначе unknown
  • 4.6 Окно 48 самых свежих общих часов
  • 4.7 Сборка каталога: разрезы из снимка + род из измерения; строки слоя, разошедшиеся единицами, схлопываются в один элемент, множество единиц метрики отсортировано
  • 4.8 Чекпоинт WARN при conflicting > 0: имя метрики и числа основания, без значений точек
  • 4.9 Тесты: накопительная, мгновенная, нулевой час, двухточечный часовой объект, одноточечный минутный, невыровненная часовая метка, противоречие, единственный слой, только слой day, история длиннее окна, разошедшиеся единицы, идемпотентность двух вызовов

5. HTTP

  • 5.1 Проверка токена одна на оба контура, параметризованная списком; схема строгая (Bearer), пустой список = выключено
  • 5.2 Предупреждение на старте о выключенной проверке чтения
  • 5.3 Редакция сохраняемых заголовков доставки чистит токены обоих контуров
  • 5.4 GET /api/v1/metrics — форма ответа из дизайна: style, hours, compared, agreeing, conflicting, first_hour, last_hour; срезы пустые, а не nil; границы окна — указатели; порядок детерминирован
  • 5.5 Тесты: 401 без токена, 401 с токеном приёма, 401 с голым значением без схемы, отдача при выключенной проверке, пустая витрина — сравнением байтов ответа с литералом
  • 5.6 config.example.toml и config.docker.toml: read_tokens перестал быть заделом на будущее, цена пустого списка названа комментарием

6. Проверка на живом архиве

  • 6.1 Прогон измерения в internal/replay/archive_test.go: свойства, а не числа — конфликтующих свидетельств ноль; накопительные и мгновенные метрики разошлись по родам; ни одна метрика не измерена по слою raw
  • 6.2 Печать измеренного рода по метрикам и стоимости каталога в t.Logf
  • 6.3 task verify:archive зелёный, отпечаток витрины не изменился

7. Документация и беклог

  • 7.1 docs/architecture.md: метод измерения, окно, порог, допуск, почему род не хранится, почему родов два, а не четыре, где нужен xFilesFactor и какой у него подвох с полярностью
  • 7.2 docs/architecture.md: форма каталога приведена к реализованной
  • 7.3 docs/local-research.md: находка с результатом измерения на живом корпусе
  • 7.4 Блокер «тай-брейк при равной полноте точек» в беклог, с вариантами, ценой и рекомендацией
  • 7.5 Пометка в docs/tasks/items/read-api-points.md: порог неполного ведра, его полярность и предел размера ответа решаются там
  • 7.6 Пометка в docs/tasks/items/token-and-secret-management.md: контуров теперь два
  • 7.7 Убрать задачу из беклога, обновить индекс

8. Дозакрыто по ревью кода

  • 8.0 Горизонт окна: часы позже now + час в сверку не входят, данные из будущего пишутся WARN
  • 8.0 Единицы обеих сторон обязаны совпасть — иначе уверенный ложный род
  • 8.0 Содержимое разжимается только у часов, прошедших отбор по учётным колонкам
  • 8.0 Имя метрики в логе обрезано, множество единиц ограничено потолком
  • 8.0 Метрика с пустым именем показывается, а не выбрасывается сентинелом
  • 8.0 Отмена снаружи не пишется как сбой сервиса
  • 8.0 Байтовый тест непустого ответа и повтора запроса

9. Приёмочные критерии (рубрика ревью предложения)

  • 9.1 Вердикт — чистая функция состояния витрины и окна: не зависит от порядка строк SQL, порядка точек в объекте и момента вызова
  • 9.2 Допуск назван величиной, один предикат на все сравнения, поведение около нуля объявлено
  • 9.3 Вырожденные свидетельства исключены явно, кворум назван числом, ниже кворума исход — unknown, а не умолчание
  • 9.4 unknown — исход первого класса, и его причины различимы клиентом без второго запроса
  • 9.5 Измерение ничего не пишет и не кешируется скрытно
  • 9.6 Стоимость ответа ограничена сверху и по числу запросов, и по числу прочитанных страниц; не растёт вместе с историей
  • 9.7 HTTP-контракт полон: только GET, пустая витрина — 200 с пустым списком, авторизация до работы, токены и значения здоровья не в логах выше DEBUG
  • 9.8 Ответ самоописателен: словарь слоёв тот же, что везде; границы объявляют, что метят; расхождение единиц показано, а не выбрано молча
  • 9.9 Каталог отдаёт наблюдаемое, а не досчитанное; деталь хранения наружу не протекает
  • 9.10 Нижний слой в сверке не участвует; source в измерение не входит
  • 9.11 Смена вердикта наблюдаема чекпоинтом
  • 9.12 Правило часа и правило метрики тестируются без БД; на живом архиве проверяются свойства, а не числа