# ADR-2026-08-11. Границу распознавания доменного признака держит норма, а не код

- **Дата:** 2026-08-11
- **Источник:** [openspec/changes/archive/2026-08-11-errors-as-instead-of-typecast/design.md](../../openspec/changes/archive/2026-08-11-errors-as-instead-of-typecast/design.md), раздел `Decisions`, Решение 3

## Решение

Признак «работы нет» узнаётся через `errors.As`, то есть на любой глубине цепочки
ошибки. Встречный риск — отказ, к которому признак примешался по дороге, — закрыт
**требованием спеки**, а не проверкой в коде воркера.

Дословно из источника:

> Граница ставится **нормой, а не кодом**: спека требует, чтобы признак рождался
> только ответом хранилища на опрос этим же шагом, и запрещает слою сохранять
> чужой признак в цепочке своей ошибки.

## Почему

Приведение типа видело только вершину цепочки — потому и ломалось от первой же
обёртки. `errors.As` эту проблему устраняет, но устраняет симметрично: признак
теперь виден и там, где его никто не клал осознанно. Отказ, к которому признак
примешался обёрткой или `errors.Join`, воркер зачёл бы пустым прогоном — задача
осталась бы в своём состоянии и переопрашивалась раз в секунду без единой записи.
Это тот же класс, от которого защищает инвариант «Принятая запись не теряется
молча», только с обратным знаком относительно чинимого дефекта.

Кодовый вариант рассмотрен и отвергнут по цене:

> **Рассмотрено и отвергнуто — научить воркер различать «признак на вершине» от
> «признака в глубине».** Отвергнуто по цене: `errors.As` такого различения не
> даёт вовсе, пришлось бы либо проверять вершину вручную (то есть вернуть
> приведение типа, которое чинится), либо заводить свой обход цепочки. Код
> усложняется ради случая, которого сегодня нет ни одного, а защита от него нужна
> на входе — при написании нового слоя, — где норма работает, а проверка в
> рантайме опоздала бы.

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

- `+` код остался коротким: одна проверка вместо разбора цепочки вручную.
- `+` защита стоит там, где ошибку совершают, — за письменным столом автора
  нового слоя, а не в рантайме, где она уже случилась.
- `−` норма не механизирована: её нарушение поймает только ревью или чтение.
  Единственный `MUST` требования `pipeline` без машинного оракула — этот.
- `−` правило живёт в двух документах: требованием в
  [openspec/specs/pipeline/spec.md](../../openspec/specs/pipeline/spec.md) и
  прозой в [conventions/errors.md](../conventions/errors.md), где оно нужно
  автору в момент письма. Второй адрес ссылается на первый и нормой не является —
  разойтись они могут только правкой, сделанной мимо спеки.
