## ADDED Requirements ### Requirement: Приём досок и спринтов `POST /agent/state` (агент v15) SHALL принимать необязательные поля: - у задачи — `rank` (строка или `null`), `story_points` (число или `null`), `sprint_ids` (список целых); присланные `rank` и `story_points` записываются в `issue`; присланный `sprint_ids` заменяет все строки `issue_sprint` задачи; задача без поля свои значения сохраняет; - в пачке — `boards`: список `{id, type, columns, kanban_backlog, sprints}`; `columns` — список `{name, statuses}` или `null`, `kanban_backlog` — `true`/`false`/`null`, `sprints` — список `{id, name, state, start_date, end_date, complete_date, goal}`. Доска из `boards` SHALL заменять свою строку `jira_board` и все строки `jira_sprint` этой доски; доски, которых нет в `boards`, не меняются. Форму хаб SHALL проверять до записи, как `comments`: нарушение — 422 `bad_state` с ключом задачи или номером доски и именем поля, пачка не применяется. `state` спринта вне `active`/`future`/`closed`, `sprint_ids` не список целых (в том числе `null`) — нарушение; `rank: null` и `story_points: null` — очистить поле. Тип доски из `boards` (`scrum` / `kanban`) хаб SHALL записывать и в `jira_project.board_type` проектов этой доски; `null` тип не меняет. Пачка может быть пустой (`issues: []`) и нести только `boards`. #### Scenario: Пачка с досками - **WHEN** пачка `refresh` несёт `boards: [{id: 4216, type: "scrum", columns: […], kanban_backlog: null, sprints: [101 closed, 102 active, 103 future]}]`, раньше у доски были спринты 101 и 102 - **THEN** у доски 4216 строки `jira_sprint` 101, 102, 103 с присланными полями; колонки заменены #### Scenario: Агент v14 - **WHEN** пачка без `boards`, задачи без `rank`, `story_points`, `sprint_ids` - **THEN** данные досок и эти поля задач не меняются ### Requirement: Маршрут планирования `GET /agile?project=<метка>` (клиентский токен) SHALL отдавать для проекта (неизвестный — 404; без `project` — 422): `{board, sprints, backlog, board_issues}`. - `board` — `{id, type, columns, kanban_backlog, fetched_at}` доски проекта; доски нет — `null` (остальные поля — `[]`/`null`); доска есть, а её данных от агента нет — `{id, type, columns: null, kanban_backlog: null, fetched_at: null}`. - `sprints` — все спринты доски: `{id, name, state, start_date, end_date, complete_date, goal, count, count_done, unestimated, points, points_done, issues}`; `issues` — свои задачи проекта в этом спринте (по `issue_sprint`); `count`/`count_done` — их число и число выполненных, `points`/`points_done` — сумма `story_points` всех и выполненных, `unestimated` — число без оценки. Порядок: `active` по `start_date`, `future` по id, `closed` по `complete_date`, новые сверху. У kanban-доски — `[]`. - `backlog` — scrum: свои задачи не в активном и не в будущем спринте и не выполненные; kanban: свои задачи в статусах первой колонки, если `kanban_backlog = true`, иначе `null`. - `board_issues` — только kanban: свои задачи в статусах остальных колонок (при `kanban_backlog` не `true` — и первой) и без колонки; выполненные — только с `jira_updated` не старше 14 дней; scrum — `null`. Строка задачи — `{key, summary, issue_type, status, priority, assignee, epic_key, story_points, rank, done}`; списки — по `rank` (строки сравниваются как есть; `null` — в конце), затем по `key`. #### Scenario: Спринт и бэклог - **WHEN** у `AWPCA` активный спринт 102 с задачами A (3 SP, в последней колонке), B (без оценки), в бэклоге C и выполненная D без спринта - **THEN** спринт 102: `count = 2`, `count_done = 1`, `points = 3`, `points_done = 3`, `unestimated = 1`; `backlog` — только C #### Scenario: Проект без доски - **WHEN** `GET /agile?project=STOCKS_DRRK`, у проекта нет доски - **THEN** 200, `board = null`, `sprints = []`, `backlog = null`, `board_issues = null`