Перейти к основному содержимому

Жизненный цикл задачи

Что это

У каждой задачи есть ровно одно состояние. Переходы описаны детерминированным конечным автоматом в src/raytsystem/tasking.py — незаявленные переходы отклоняются. Так история задачи остаётся предсказуемой и проверяемой.

Состояния

  • inbox — «Входящие»: только что созданная задача.
  • planned — «Запланировано».
  • ready — «Готово» (готова к работе).
  • running — «В работе».
  • review — «На проверке».
  • blocked — «Заблокировано».
  • done — «Завершено» (финальное состояние).
  • cancelled — «Отменено» (финальное состояние).

Новая задача всегда создаётся в состоянии inbox.

Допустимые переходы

Таблица переходов из _TRANSITIONS в src/raytsystem/tasking.py:

  • inbox → planned, cancelled
  • planned → ready, blocked, cancelled
  • ready → running, blocked, cancelled
  • running → review, blocked, cancelled
  • review → done, running, blocked, cancelled
  • blocked → planned, ready, running, cancelled
  • done → (нет переходов, финальное)
  • cancelled → (нет переходов, финальное)

Обратите внимание: из review можно вернуть задачу в running (доработка), а из blocked — снять блокировку в planned/ready/running. done и cancelled — тупиковые состояния; их нельзя переоткрыть, историю можно только читать.

Зависимости

При создании задачи все её dependency_ids должны уже существовать на доске, иначе команда отклоняется. При переходе в ready или running все зависимости обязаны быть в состоянии done — иначе raytsystem отклоняет переход как «зависимости не завершены». Граф зависимостей проверяется на отсутствие циклов.

Блокировки

Переход в blocked требует явную причину (blocked_reason). Причина сохраняется в задаче; при выходе из blocked она очищается. История блокировки остаётся неизменяемой.

Правила времени и ревизий

Каждый переход увеличивает revision задачи на единицу и обновляет updated_at. Метка времени не может «идти назад»: переход с временем раньше текущего updated_at отклоняется. Переход меняет только контролируемые поля (состояние, причину блокировки, ревизию, время) — остальные поля задачи неизменяемы.

Конфликт поколений

Каждый переход требует expected_generation_id. Если доска изменилась между чтением и записью (другой переход, другая сессия), команда падает с конфликтом поколений (TaskConflict). Это защита от гонок: перечитайте доску командой task list и повторите переход с актуальным поколением. Идемпотентный повтор той же команды с тем же ключом безопасен — он вернёт прежний результат.

Пример

Перевести задачу в работу (сначала прочитайте актуальное поколение через task list):

uv run raytsystem task transition task_XXXX running \
--idempotency-key move-running-01 \
--expected-generation tgen_YYYY

Частые ошибки

  • «Illegal task transition» — целевое состояние не разрешено из текущего. Сверьтесь с таблицей выше.
  • «Task dependencies are not complete» — вы переводите в ready/running, но не все зависимости done.
  • Конфликт поколений — доска изменилась; перечитайте её и повторите. См. конфликт поколений.
  • Задача «застряла» — см. задача не переходит.

Связанные страницы

Источники истины

  • src/raytsystem/tasking.py
  • ops/decisions/ADR-015-local-web-control-plane-and-task-ledger.md
  • web/src/features/Tasks.tsx