# Tasks ## 1. Данные попытки и вывод итога - [x] 1.1 [backend] `settleHourly` выводит итог попытки из durable-строк шагов в одной транзакции; `cause: 'INTERRUPTED'` капит итог до `partial`; вызывающие не передают результат и список ошибок - **Covers:** `hourly-investment-sync` — «Последний результат обхода доступен только для наблюдения» - **Source:** Design — Decisions 1, 6; System Analysis — Data and State Semantics - **Verification:** изолированный SQLite-тест: сбой после частичного обновления даёт `partial` с сохранённым успехом; сбой до первого шага даёт `failed`; ложный `succeeded` невозможен ни на одном error-пути - **Depends on:** none - [x] 1.2 [backend] id строк шагов персистятся в `progress` hourly-попытки (`children` + `attempts`) в момент dispatch, до ожидания шага - **Covers:** `hourly-investment-sync` — «Восстановление после сбоя сохранения не блокирует последующие обходы», «Устаревшая публикация не влияет на актуальную попытку» - **Source:** Design — Decisions 2, 5 - **Verification:** SQLite-тест: после потери выполнения попытки множество шагов восстанавливается из `progress`, включая шаг, упавший в dispatch - **Depends on:** 1.1 ## 2. Ограниченное ожидание и восстановление - [x] 2.1 [backend] ограничение ожидания на каждый шаг в `settledWorks` (константа ~15 минут, per-id deadline); истёкший шаг учитывается проблемным с причиной `DEADLINE`; начатые обращения к T-Bank не отменяются; часы в тестах — jest fake timers без инъекции clock в production-код - **Covers:** `hourly-investment-sync` — «Каждая попытка обхода достигает итога в ограниченное время» - **Source:** Design — Decision 3; System Analysis — Error and Recovery Semantics - **Verification:** SQLite-тест с зависшим шагом на fake timers: попытка терминальна в ограниченное время, наблюдение не ждёт бесконечно - **Depends on:** 1.1 - [x] 2.2 [backend] in-memory `active`-реестр обходов + `recoverAbandoned` перед reservation: running-попытка своего `processRunId` без активного walk закрывается derived settle с `INTERRUPTED`; застрявшие legacy-строки закрываются первым проходом - **Covers:** `hourly-investment-sync` — «Восстановление после сбоя сохранения не блокирует последующие обходы» (Потерянное закрытие попытки) - **Source:** Design — Decisions 4, 6; System Analysis — Error and Recovery Semantics - **Verification:** SQLite-тест: подавленная ошибка закрытия при живом процессе — следующий проход закрывает попытку, следующая допустимая попытка стартует - **Depends on:** 1.1, 1.2 - [x] 2.3 [backend] restart-ветка `reserveHourly` закрывает чужую running-попытку derived settle с `INTERRUPTED` вместо голого `cancelled`; `due`-гейт и защита от overlap прежние - **Covers:** `hourly-investment-sync` — «Восстановление после сбоя сохранения не блокирует последующие обходы» (Перезапуск процесса) - **Source:** Design — Decision 5 - **Verification:** SQLite-тест перезапуска процесса: брошенная попытка закрыта `partial`/`failed` с сохранёнными результатами шагов, новый обход не ранее следующего часового срока, перекрытия нет - **Depends on:** 1.1, 1.2 ## 3. Финализаторы шагов - [x] 3.1 [backend] внешний catch финализаторов шагов (enrichment, operations drain/step) записывает терминальный неуспешный результат своей строки шага существующим путём записи ошибки перед возвратом или пробросом - **Covers:** `hourly-investment-sync` — «Каждая попытка обхода достигает итога в ограниченное время» (шаг покидает running) - **Source:** Design — Decision 7 - **Verification:** SQLite-тест с инъекцией ошибки финализатора: строка шага терминальна, попытка проходит terminal barrier - **Depends on:** none ## 4. Поздняя публикация - [x] 4.1 [backend] подтверждено fence-поведение публикации шага: результат до фиксации итога попытки сохраняется и учитывается в итоге; после появления преемника — отклоняется без изменения данных и итога актуальной попытки - **Covers:** `hourly-investment-sync` — «Устаревшая публикация не влияет на актуальную попытку» - **Source:** Design — Decisions 1–3; System Analysis — Error and Recovery Semantics - **Verification:** SQLite-тесты поздней публикации: до фиксации итога — учтён; после successor — данные и итог актуальной попытки не изменились - **Depends on:** 1.1, 1.2, 2.1 ## 5. Системная верификация backend - [x] 5.1 [backend] полный набор изолированных SQLite-тестов coordinator: ветки `first`, `not-due`, `running`, `restart`, `partial`, итог после terminal barrier; инъекции ошибок отдельно в дочерние шаги и родительский coordinator; controlled stop/restart без бесконечного drain и без новых reservations после остановки; scheduler stub `upcoming-events` не является единственным доказательством - **Covers:** `hourly-investment-sync` — все Requirements дельты - **Source:** Design — Developer Verification Strategy; QA Analysis — Coverage Matrix, Risk-based Priorities - **Verification:** полный `backend verify` зелёный на финальном diff; наборы auth/money/publication сохранены - **Depends on:** 1.1, 1.2, 2.1, 2.2, 2.3, 3.1, 4.1 - [x] 5.2 [backend] backend-generated OpenAPI семантически совместим с текущим контрактом: форма `meta` и словарь состояний не изменились, новые значения только внутри открытого строкового поля диагностики - **Covers:** `N/A` — защита контракта (изменений контракта нет) - **Source:** Design — Contract Implementation; System Analysis — Contracts - **Verification:** diff сгенерированного OpenAPI до/после изменения не содержит shape-изменений - **Depends on:** 1.1, 1.2, 2.1, 2.2, 2.3, 3.1, 4.1 ## 6. Canonical documentation - [x] 6.1 [docs] обновить `data-freshness.md`: вывод итога из фактических результатов шагов, ограниченное ожидание шага, закрытие брошенной попытки без блокировки расписания — рядом со словарём `lastAttempt` - **Covers:** `N/A` — docs promotion - **Source:** docs-impact — Documentation Changes, строка 1 - **Verification:** страница не противоречит canonical spec `hourly-investment-sync` и фактическому поведению `meta` - **Depends on:** 5.1 - [x] 6.2 [docs] обновить раздел «Состояние и ошибки» `tbank.md`: причины жизненного цикла `INTERRUPTED`/`DEADLINE` — внутренняя диагностика, не ошибки T-Bank; начатые обращения не отменяются - **Covers:** `N/A` — docs promotion - **Source:** docs-impact — Documentation Changes, строка 2 - **Verification:** раздел различает ошибки подключения/external-коды и внутренние причины жизненного цикла - **Depends on:** 5.1