## ADDED Requirements

### Requirement: Перечисление непокрытых секций доставки

Разбор SHALL перечислять верхнеуровневые ключи объекта `data` и возвращать
вызывающему те из них, которые он не покрывает. Содержимое непокрытой секции
MUST NOT удерживаться после того, как разбор прошёл мимо неё: тела доходят до
42 МиБ, и удержание кучи здесь — часть контракта, а не деталь реализации.

Покрытым сегодня является ровно один ключ — `metrics`. Разбор и перечисление
MUST ходить по одному объявленному множеству покрытых имён: состояние «секция
разбирается, но числится непокрытой» невыразимо по построению.

Непокрытым ключ считается независимо от того, что лежит внутри: содержимое не
интерпретируется, поэтому и о пустоте секции разбор честно ничего не знает.
Измерено на живом архиве — пустых секций HAE не присылает ни разу (99 доставок).

Список SHALL быть каноничен: имена отсортированы, повторов нет. Порядок ключей в
JSON от HAE нестабилен, а значение уезжает в базу и сравнивается между
доставками.

Отсутствие непокрытых ключей и отсутствие секции `metrics` — разные события, и
оба нормальны: половина потока состоит из доставок без метрик вовсе (48 из 99).

#### Scenario: Незнакомая секция попадает в список непокрытых

- **WHEN** тело содержит `data.workouts` наряду с `data.metrics`
- **THEN** разбор возвращает `workouts` в списке непокрытых ключей
- **AND** точки секции `metrics` разбираются как обычно

#### Scenario: Доставка без метрик разбирается и не теряется

- **WHEN** тело содержит только `data.stateOfMind`
- **THEN** разбор завершается без ошибки, точек нет
- **AND** `stateOfMind` возвращается в списке непокрытых ключей

#### Scenario: Доставка из одних метрик непокрытых ключей не даёт

- **WHEN** единственный ключ `data` — `metrics`
- **THEN** список непокрытых ключей пуст

#### Scenario: Один и тот же набор секций даёт один и тот же список

- **WHEN** два тела несут те же секции в разном порядке, а одно из них
  повторяет непокрытый ключ дважды
- **THEN** списки непокрытых ключей у них совпадают

#### Scenario: Содержимое непокрытой секции не удерживается в памяти

- **WHEN** тело в десятки мегабайт состоит преимущественно из непокрытой секции
- **THEN** после разбора удержано не больше четырёх размеров тела — та же
  граница, что и для тела из метрик
- **AND** содержимое непокрытой секции в результат разбора не попадает

### Requirement: Границы списка непокрытых секций

Список непокрытых ключей MUST быть ограничен — не больше 32 имён и не больше
64 байт на имя: имена приходят из тела, которым отправитель управляет целиком.
Срабатывание любой из границ MUST быть видно вызывающему — молчаливое усечение
превратило бы список в уверенный, но неполный ответ на вопрос «что останется
потерянным, если тело удалить».

Число имён сверх предела отдаётся счётчиком. Имя длиннее предела обрезается по
границе рун, к обрезанному приписывается маркер `…` — сверх предела, а не внутри
него. Обрезка не инъективна, поэтому обрезанное имя сравнению со словарём
известных секций не подлежит.

Предел длины считается по байтам **декодированного** имени: escape-
последовательности JSON к этому моменту уже разобраны.

#### Scenario: Ключей больше предела

- **WHEN** объект `data` содержит 40 непокрытых ключей
- **THEN** список содержит 32 имени
- **AND** число отброшенных имён отдано отдельным счётчиком

#### Scenario: Имя ключа длиннее предела

- **WHEN** непокрытый ключ длиннее 64 байт
- **THEN** в списке лежит имя, обрезанное по границе рун, с маркером `…`

### Requirement: Отказ разбора остаётся всё или ничего

Разбор SHALL оставаться операцией «всё или ничего»: ошибка, встреченная
**после** того, как секция `metrics` уже разобрана (обрезанное тело, мусор в
следующем члене), MUST NOT оставлять точки в результате — доставка считается
неразобранной целиком.

Иначе часть точек оказалась бы в витрине под статусом, по которому доставку
никто не подберёт, и свёртка перестала бы быть детерминированной по журналу.

Повтор ключа `metrics` в одном объекте `data` SHALL давать объединение секций, а
не победу последней: молча терять точки нельзя.

#### Scenario: Тело оборвано после секции метрик

- **WHEN** тело содержит целую секцию `metrics`, а следующий член `data`
  оборван
- **THEN** разбор завершается ошибкой и точек не отдаёт

#### Scenario: Секция метрик встречается дважды

- **WHEN** объект `data` содержит два ключа `metrics`
- **THEN** точки обеих секций попадают в результат
