# Tasks ## 1. Граф в store - [x] 1.1 Объявить `allowedTransitions map[State][]State` (from → to) в `internal/store/download.go` рядом с `terminalStates`, с комментарием об источнике истины и правиле самоперехода. Заполнить по таблице из `design.md`. - [x] 1.2 Построить обратное отображение `to → {легальные from}` (для SQL-предиката) как package-level `var` через хелпер-инвертор; включать сам `to` (самопереход). ## 2. Гейт в setState - [x] 2.1 В `setState` добавить предикат `AND state IN (<легальные источники для to>)` до/рядом с существующим гардом терминальности; аргументы через `placeholders`. - [x] 2.2 На `n == 0` — диагностическое чтение текущего состояния (через `sqlx.QueryerContext`, если `e` его поддерживает) для точного сообщения: «not found» / «illegal transition » / терминал без revive. - [x] 2.3 Сверить `PromoteCatched`: ребро `catched → downloading` присутствует в графе; оставить его собственный гард `state='catched'`, добавить комментарий-ссылку на граф. ## 3. Тест согласованности - [x] 3.1 `TestTransitionGraphWellFormed`: все состояния в ключах и значениях графа — известные (из полного списка `State`); ни один список не содержит сам ключ (петли не перечисляются явно). - [x] 3.2 `TestSetStateAllowsDeclaredEdges`: для набора объявленных не-revive рёбер (`downloading→completed`, `review→linking`, `recognizing→review`, …) `SetDownloadState` проходит. - [x] 3.3 `TestSetStateRejectsUndeclaredEdges`: репрезентативные необъявленные (`review→done`, `downloading→done`, `completed→linking`) отклоняются, состояние не меняется. - [x] 3.4 `TestSelfTransitionAllowed`: `deferred→deferred` проходит. - [x] 3.5 `TestTerminalReviveOnlyViaActivate`: `failed→downloading` через `SetDownloadState` отклоняется, а через `ActivateIfNoOtherActive` (при свободном infohash) проходит. - [x] 3.6 `TestCancelDeferReachableFromEveryNonTerminal`: инвариант generic-команд — для каждого не-терминального состояния `cancelled` — легальная цель, и `deferred` — легальная цель (кроме самого `deferred`, где это самопереход). Ловит класс дыры «забыли состояние» (напр. `linking` после краха). - [x] 3.7 `TestPreflightDesyncEdges`: рёбра `reconcileToReality` из ревью/терминальных состояний при пропавшем источнике — `review/deferred/reverted/cancelled → orphaned` и `→ deleted` — проходят. ## 4. Проверка отсутствия регрессий - [x] 4.1 `task test` — весь набор зелёный (существующие тесты воркера/store — сеть безопасности против слишком тесного графа). - [x] 4.2 `task lint` — 0 issues. - [x] 4.3 `openspec validate state-transition-graph --strict`.