Files
transcriber/openspec/specs/pipeline/spec.md
T
av 2559d09fc8 доменные ошибки сравниваются через errors.As, отказ Close не теряется
- признаки «работы нет» и «задача не найдена» узнаются по смыслу, а не
  приведением типа: обёртка `%w` на пути больше не превращает пустой прогон
  воркера в отказ раз в секунду
- отказ закрытия соединения с распознавателем доходит до вызывающего
  (`errors.Join`) либо до журнала; у `errcheck` включён `check-blank`, иначе
  критерий принимал реализацию, выбрасывающую отказ в пустоту
- заведены первые тесты пакета worker и capability `pipeline`; долг из четырёх
  замечаний линтера закрыт, гейт зелёный целиком
2026-08-11 18:04:25 +03:00

6.2 KiB

pipeline Specification

Purpose

Конвейер расшифровки: как задача движется по состояниям, что делает воркер, когда работы нет, и что считается отказом шага.

Описан пока только пустой прогон воркера — тот, что нормируют проверки пакета internal/controller/worker и перевод признака в internal/service. Сознательно не описаны переходы состояний и цепочка created → converted → transcribe → done | failed, захват задачи и срок его протухания, отмена контекста посреди шага, освобождение ресурсов внешних клиентов. Это не значит, что такого поведения нет: оно живёт в коде, а требования на него не написаны, потому что требование без проверки — предположение, а не норма. Первая задача, которая трогает любое из перечисленного, дописывает его сюда.

Requirements

Requirement: Пустой прогон воркера — не отказ

Воркер SHALL отличать «работы в этом состоянии сейчас нет» от отказа шага. На пустом прогоне он MUST не считать прогон отказом: не увеличивать счётчик работы и не писать о нём на уровне владельца сервиса. Признак пустого прогона MUST узнаваться по смыслу значения, а не по его точной форме, и MUST переживать пояснения, добавленные к этому значению на любом промежуточном шаге пути.

Требование стоит на инварианте проекта «NoopJobError — не ошибка»: три воркера опрашивают базу раз в секунду, и пустой прогон, принятый за отказ, даёт три записи отказа в секунду и столько же засчитанных сбоев, которых не было.

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

Отказ шага, наоборот, MUST быть виден владельцу сервиса записью в журнале и MUST быть засчитан в счётчик работы с пометкой отказа.

Сколько раз он записывается и каким уровнем — это требование не нормирует, и умолчанием тут считать нечего. Сегодня один отказ даёт две записи: пишет шаг конвейера и следом воркер, — а уровень стоит ERROR там, где конвенция просит WARN для повторяющегося сбоя фонового цикла. И то и другое записано долгом в docs/conventions/logging.md, раздел «Ошибки», строкой «Расхождение, и оно системное». Долгом оно и остаётся: требование, объявившее одиночную запись нормой, сделало бы недостижимое обязательным, а требование, объявившее нормой двойную, — закрыло бы долг контрактом. Задача, которая возьмётся за этот долг, дописывает норму сюда.

Scenario: Работы в состоянии нет

  • GIVEN ни одной задачи в опрашиваемом состоянии нет
  • WHEN воркер делает свой прогон
  • THEN на уровне владельца сервиса об этом прогоне не пишется ничего
  • AND счётчик работы воркера не растёт

Scenario: Признак пустого прогона дошёл с пояснением

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

Scenario: Шаг отказал

  • GIVEN шаг конвейера вернул отказ
  • WHEN воркер завершает прогон
  • THEN отказ виден владельцу сервиса записью в журнале
  • AND счётчик работы воркера растёт с пометкой отказа

Scenario: Шаг сделал работу

  • GIVEN шаг конвейера отработал задачу без отказа
  • WHEN воркер завершает прогон
  • THEN счётчик работы воркера растёт с пометкой успеха
  • AND записи об отказе в журнале нет