- признаки «работы нет» и «задача не найдена» узнаются по смыслу, а не приведением типа: обёртка `%w` на пути больше не превращает пустой прогон воркера в отказ раз в секунду - отказ закрытия соединения с распознавателем доходит до вызывающего (`errors.Join`) либо до журнала; у `errcheck` включён `check-blank`, иначе критерий принимал реализацию, выбрасывающую отказ в пустоту - заведены первые тесты пакета worker и capability `pipeline`; долг из четырёх замечаний линтера закрыт, гейт зелёный целиком
4.2 KiB
ADR-2026-08-11. Границу распознавания доменного признака держит норма, а не код
- Дата: 2026-08-11
- Источник: 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 и прозой в conventions/errors.md, где оно нужно автору в момент письма. Второй адрес ссылается на первый и нормой не является — разойтись они могут только правкой, сделанной мимо спеки.