# Ответ точек несёт измеренный род и его применимость к отданному ряду

- **Дата:** 2026-08-04
- **Источник:** openspec/changes/archive/2026-08-04-tochki-metriki-za-period/design.md

## Решение

Конверт ответа маршрута точек несёт `aggregation` **объектом**
`{style, applicable, last_hour}`, а не строкой с применённой свёрткой:

- `style` — измеренный род метрики, тот же словарь и то же имя, что у каталога;
- `applicable` — применим ли объявленный род к **отданному ряду**;
- `last_hour` — ярлык самого свежего часа окна измерения.

`docs/architecture.md` до этого изменения обещал `"aggregation": "sum"` —
строку. Решение её **пересматривает**: строка называет применённое и молчит об
основании.

Отвергнуто и названо поимённо: поле `applied` с именем применённой свёртки
(выводится из `style` и `bucket` тем же инвариантом; как строка неверно
описывает свёртку мгновенной метрики, у которой по архитектуре «среднее с
`min`/`max` рядом»); полное основание каталога (`hours`, `compared`, `agreeing`,
`conflicting`, `first_hour`) в конверте точек — второй экземпляр факта, обязанный
сходиться с первым.

## Почему

**Род есть свойство метрики, а слой — свойство ряда, и их сочетание бывает
опасным.** Конверт `{"layer": "raw", "style": "cumulative"}` законен и штатен:
правило выбора слоя при равном охвате предпочитает самый мелкий. Инвариант
«нижний слой HAE не суммируется никогда» система соблюдает, ничего не складывая,
— но потребитель об инварианте не знает, а сумма по нижнему слою завышает втрое
(находка 34 разведки). Разрыв построен проходом `review-rubric` на предложении,
до кода:

> Конверт `{"layer": "raw", "aggregation": {"style": "cumulative"}}` законен,
> штатен — и он прямо приглашает главного потребителя (агента с ограниченным
> контекстом) сложить ряд самому. Система при этом свёртки не делает, инвариант
> формально цел; результат у потребителя завышен, а решение по нему уже принято.

`applicable: false` — та самая оговорка, которая едет вместе с данными.

**`last_hour` — единственный след замершего окна.** Род считается по 48 самым
свежим **общим** часам, а не по последним 48 часам календаря: выключенная
минутная автоматизация HAE останавливает пополнение общих часов, окно замирает и
продолжает объявлять род.

**Литература расколота, и обе стороны названы.** Род **вместе с данными**:
Google Cloud Monitoring объявляет `metricKind` и `valueType` в каждом объекте
`TimeSeries` ответа, а не только в дескрипторе метрики; CloudWatch
`GetMetricData` кладёт `StatusCode` (`Complete` / `PartialData`) рядом с рядом —
оговорка едет с данными, а не оставляется клиенту на вывод; Home Assistant
`statistics_during_period` держит `start` и `end` в ответе **всегда**,
независимо от запрошенных `types`. Род **отдельно от данных**: Prometheus отдаёт
`{resultType, result}` без единого слова о типе, а тип живёт в
`/api/v1/metadata`; Graphite render не объявляет ничего. Второе отвергнуто по
измеримой причине: клиент обязан сделать второй запрос, а до тех пор не
отличает «род известен» от «род не измерен», — и согласованности между двумя
ответами всё равно нет, потому что род есть функция **окна**, а окно едет с
часами. Принцип HealthKit `HKStatistics` («род не тот — свёртки нет») взят,
механизм неприменим: у нас стиль источником не объявлен.

## Последствия

- `+` Потребитель видит не только число, но и на каком основании его можно
  сворачивать, без второго запроса и без знания инвариантов проекта.
- `+` Форма объявлена **до** того, как её скопируют свёртка по сетке, порог
  неполного ведра, тренировки, записи и MCP. После копирования это была бы не
  развилка, а археология.
- `−` Поле `applicable` избыточно по построению: клиент, знающий правило «нижний
  слой HAE не суммируется», вывел бы его из `style` и `layer`. Взято сознательно
  — правило принадлежит нам, и молчаливо перекладывать его на потребителя
  дороже, чем поле.
- `−` Чтобы разобрать, **почему** род `unknown`, придётся спросить каталог:
  полное основание живёт там в одном экземпляре.
- `−` Род в конверте точек и род в каталоге считаются в разные моменты и у
  клиента, сравнивающего два ответа, могут разойтись. Это свойство измерения, а
  не дефект; ровно поэтому `last_hour` едет вместе с родом.

## Открыто, решает владелец

**Машинно-различимый код причины отказа.** Тело отказа несёт только
человекочитаемую строку, и агент не отличит «зона не указана» от «слой
незнаком» иначе, чем разбором русского текста. Правило общее для всех маршрутов
и меняет `errorWire`, то есть и контракт приёма, — сюда не взято.
