# ✨ Показывать метрикой задачу, застрявшую в состоянии - **Тип:** feature - **Категория:** Очередь — Признак вставшего конвейера — вторая половина той же работы. - **Зачем:** Вставший конвейер неотличим от простоя: возраст задачи в состоянии не считается, и очередь без движения выглядит как отсутствие работы. - **Теги:** review-2026-08-23 По метрикам видно, что конвейер встал, и это отличимо от «работы нет». Сегодня метрики считают события — принято, распознано, отказано, — а состояние очереди не считает ничто. Пустая очередь и очередь, где десять задач висят третий час, дают одинаковый нулевой прирост счётчиков. **Вторая половина той же слепоты — сам воркер.** Ревью change `2026-08-23-storage-without-pocketbase` (проход `review-ops`, O1) показало: живой воркер, которому не досталось работы, и воркер, чей шаг завис, дают одну и ту же картину — ни строки в журнале, ни движения счётчика. Признак живости здесь свой, отдельный от возраста задачи: очередь бывает пуста законно, а вот прогон цикла случается всегда. Считать для этого `NoopJobError` **нельзя** — инвариант CLAUDE.md: значение «задач в этом состоянии нет» не логируется, не считается в метрику и не поднимает уровень. Нужен свой признак: отметка времени последнего прогона цикла у каждого воркера, а не счётчик пустых заходов. ## Затрагивает - `internal/metrics` — метрика числа задач по состояниям, возраста самой старой из них и времени последнего прогона цикла у воркера; - цикл воркера `internal/controller/worker` — откуда снимается признак живости; - таблица задач: колонка состояния, по которой считаются числа, и отметка времени перехода, по которой считается возраст самой старой; - `docs/architecture.md`, раздел эксплуатации — что означает каждое число; - `docs/database.md` — частота съёма чисел. ## Критерии приёмки - Число задач в каждом состоянии видно на `/metrics`. Оракул — тест: завести три задачи в разных состояниях и снять метрики. - Возраст самой старой задачи в состоянии виден числом и растёт, пока задача не сменила состояние. Оракул — тест на задаче с заведомо старой отметкой времени. - Съём чисел не мешает работе воркеров. Оракул — чтение кода: запрос идёт по индексу состояния и не берёт захват. - Живой воркер отличим от вставшего. Оракул — тест: у воркера, крутящего цикл на пустой очереди, отметка последнего прогона растёт; у воркера, чей шаг завис дольше периода опроса, стоит на месте. - `NoopJobError` не считается и не пишется. Оракул — тест с перехваченным журналом на пустой очереди: строк о задаче нет, счётчик отказов не вырос, а признак живости при этом двигается. ## Рамки Правило оповещения на этой метрике заводит задача `owner-alerting` — здесь только число. Порог «сколько считается застреванием» здесь не решается: он зависит от замеров `speechkit-limits` и `intake-limits-measure`.