30 KiB
Context
Основа — main a2bf3fc (схема v8 после fact-status-removal #104). Issue #108, ветка feat/108-sprints-backlog.
Агент — jira-agent v14 (../jira-agent), его решения — в design-only change agent-v15-sprints репозитория jira-agent.
Что есть сейчас (разведка 24.10.2026):
- у задачи в зеркале
issue.sprint— имя одного спринта строкой; агент собирает имена со спринтов досокscrum(активные и будущие,GET /rest/agile/1.0/board/{id}/sprint?state=active,futureи/sprint/{id}/issue); у задач закрытых спринтовsprint = NULL— принятое ограничение; - спринтов как сущностей (id, состояние, даты, цель), ранга задач, оценок и колонок доски хаб не знает;
ensure_sprint {key, sprint}переносит задачу в спринт или бэклог,board_idхаб дописывает по проекту задачи;- доска проекта —
jira_project.board_id,board_type(scrum/kanban/NULL), тип пишет только результатrefresh; - живые данные: доска только у
AWPCA(4216, scrum), спринтыAWPCA_Sprint_2_VGO(73 задачи) иAWPCA_Sprint_3_Corrections(37), 188 своих задач вне спринтов; 11 статусов; уSTOCKS_DRRKиsfm_kedrдосок нет; - оценок в AURORA пока нет (
field_idsбез story points); веб —/app/jira/board(«Доска» — доска связей),/app/jira/issues,/app/jira/feed.
Goals / Non-Goals
Goals: в вебе — бэклог, доска спринта и kanban по образцу Jira, только просмотр; история закрытых спринтов; оценки (story points) в зеркале, на экранах и в MCP; из чата — создание и правка спринта, запуск и завершение, ранжирование, оценка.
Non-Goals и Anti-Goals: см. proposal.md.
Decisions
-
Веб — только просмотр (решение владельца 24.10.2026: «веб только смотреть… вэб это только вью»). Ни перетаскивания, ни кнопок, меняющих Jira, на новых экранах нет; все изменения — пакетами команд из MCP.
-
Экраны по типу доски проекта (решение владельца): scrum — «Бэклог» и «Спринт»; kanban — «Kanban»; без доски — пунктов нет. Тип доски неизвестен (
NULL) — пунктов нет, как без доски. -
Kanban-бэклог — раздел страницы «Kanban», а не отдельный пункт меню (решение агента): признак kanban-бэклога известен только из данных доски (
GET /agile), а меню строится поGET /projects; отдельный пункт мигал бы при загрузке. Если бэклог у доски включён — над колонками свёрнутый список «Бэклог (N)» по рангу. -
«Доска» переименовывается в «Связи» (решение владельца): подпись в меню и заголовке вкладки — «Связи», адрес —
/app/jira/links;/app/jira/boardи прежний/app/boardперенаправляют на него с сохранением строки запроса. -
Спринты — сущности зеркала. Таблица модуля jira
jira_sprint(id Jira, доска, имя, состояниеactive/future/closed, даты начала, конца и завершения, цель) иissue_sprint(задача ↔ все её спринты, любого состояния).issue.sprint(имя текущего спринта) остаётся как есть — фильтры списка задач и доска связей его используют. История закрытых спринтов (решение владельца: «лучше иметь исторические данные») — изissue_sprint: Jira хранит у задачи все её спринты, агент передаёт их id. -
Колонки доски — из настроек доски Jira (
GET /rest/agile/1.0/board/{id}/configuration):jira_boardхранит колонки[{name, statuses: [имя статуса…]}]и признак kanban-бэклога. Статус задачи без колонки — колонка «Без колонки» в конце (видна, только если в ней есть задачи). -
«Выполнено» — статус в последней колонке доски (правило Jira для scrum-доски). Для закрытого спринта — по текущему статусу задачи (решение агента): статуса на момент закрытия хаб не знает (журнал изменений покрывает только открытые спринты и с 22.10.2026). Задача, выполненная после закрытия спринта, в прошлом спринте покажется выполненной — принятое ограничение.
-
Свои задачи проекта. Экраны и MCP показывают только свои задачи проекта (метка проекта), соседей нет. Спринты — все спринты доски проекта, в том числе без своих задач.
-
Ранг — строка Jira (LexoRank) как есть, порядок — сравнение строк; задачи без ранга — в конце, по ключу.
-
Оценка — story points, число
REAL(issue.story_points,NULL— нет оценки). Поле Jira: настройкаfield_ids.story_points, если задана; иначе агент находит поле сам (решение агента вagent-v15-sprints). Суммы по спринту: всего и выполнено; задача без оценки в сумму не входит, а число таких задач показывается. -
Новые типы команд, все «проверь, потом сделай», темп по умолчанию 30 с:
ensure_sprint_meta {sprint, rename_to?, start_date?, end_date?, goal?}— спринт доски проекта с этим именем (среди активных и будущих) существует и имеет эти поля; нет — создаётся;ensure_sprint_state {sprint, state: "active" | "closed", move_open_to?}— спринт в этом состоянии; приclosedневыполненные задачи (не в последней колонке) сначала переносятся вmove_open_to(имя активного или будущего спринта илиbacklog; дляclosedобязателен);ensure_rank {key, before | after}— задача выше (before) или ниже (after) другой задачи; уже так — без записи.board_idуensure_sprint_metaиensure_sprint_stateхаб дописывает при постановке из проекта пакета (команды не про задачу — проекта задачи нет). Пакет без проекта, проект без доски, kanban-доска, неизвестный тип доски — 422 с тем же текстом, что уensure_sprint.
-
Оценка ставится через
ensure_fields(новое полеstory_points: число ≥ 0 илиnull— очистить), отдельной команды нет. -
Данные досок — в каждом
refresh. Агент v15 к первой пачке состоянияrefreshприкладываетboards— по каждой доске из параметров команды: тип, колонки, kanban-бэклог, все спринты (любого состояния); хаб заменяет данные доски целиком. У задач —rank,story_points,sprint_ids. Результат команд видно после хвостовогоrefresh, который хаб уже дописывает к пишущему пакету (hooks.tail, по ключам задач команд); пакет сensure_sprint_metaилиensure_sprint_stateполучает хвостrefresh {jql: <JQL проекта пакета>}— у этих команд нет ключей, а перенесённые при закрытии задачи заранее неизвестны.refreshбез задач (все ключи пропали) агент v15 всё равно отправляет одной пустой пачкой сboards. -
Совместимость с агентом v14. Без
boardsи новых полей хаб ничего не теряет: прежние данные досок и задач остаются; экраны без данных доски показывают «Нужен агент v15 и обновление из Jira». Новые типы команд агенту v14 —failed«неизвестный тип» на его стороне;min_agent_versionхаб не поднимает (поднимет владелец после выкатки v15). -
API — один маршрут
GET /agile?project=<метка>(данные доски, спринты со своими задачами и суммами, бэклог). Веб и MCP читают его; отдельных маршрутов на спринт и бэклог нет. -
Схема v9:
issue.rank,issue.story_points, таблицыjira_board,jira_sprint,issue_sprint. -
Kanban без бэклога (
kanban_backlogfalseилиnull): первая колонка — обычная колонка доски, её задачи — вboard_issues;backlog = null. -
ensure_sprint_metaищет спринт по имени среди всех спринтов доски: закрытый —failed«спринт закрыт»; несколько с этим именем —failedс их id;rename_toна имя другого спринта доски —failed«имя занято».ensure_sprint_state {state: closed}для будущего спринта —failed«спринт не начат»;activeдля закрытого —failed«спринт закрыт». Результаты:ensure_sprint_meta—{sprint_id, created},ensure_sprint_state—{sprint_id, moved},ensure_rank—{changed}. Подтверждения по зеркалу у новых типов нет (check_confirmed— нет): повторная выдача без отчёта выполняется агентом заново, это безопасно по «проверь, потом сделай». -
Проектный пакет:
ensure_rankпринадлежит проекту поkey(своя задача или временный ключ пакета);before/after— любая задача зеркала.ensure_sprint_meta/ensure_sprint_stateв пакете проекта проверяются по доске проекта (решение 11), не по задаче.command_retryснимаетboard_idу всех трёх типов со спринтом, как уensure_sprint. -
Форма новых полей состояния:
story_points: nullиrank: null— «очистить»;sprint_ids: []— у задачи нет спринтов;sprint_ids: nullили не-список целых — 422bad_state. -
Меню модуля зависит от проекта: пункт раздела модуля веба может нести условие по проекту (тип доски из
GET /projects); пока проекты не загружены, условные пункты скрыты. Смена проекта на странице, которой у нового проекта нет, оставляет адрес и показывает «У проекта нет …-доски» (как прямой переход). -
Тип доски — одна правда,
jira_project.board_type: тип изboardsпачки состояния хаб записывает и вjira_board.type, и вjira_project.board_typeпроектов этой доски (как результатrefresh, толькоscrum/kanban). Меню и 422 постановки читаютjira_project, экраны —GET /agile, тип в нём — изjira_project. -
Три состояния данных доски на экранах: данных нет (
fetched_at = null) — «Нужен агент v15 и обновление из Jira»; данные есть, колонок нет (агент не смог прочитать настройки) — «Не удалось прочитать настройки доски из Jira» и обновление; колонки есть — экран. Без колонокdoneу всех задач —false. -
Даты: в командах —
ГГГГ-ММ-ДД(агент переводит в формат Jira); вjira_sprint— строки Jira как есть (ISO-8601 со смещением); веб показывает их датой «ДД.ММ» в часовом поясе браузера; «осталось N дн.» — от сегодня доend_date. -
История закрытых спринтов — из базовой JQL: базовая JQL проектов не отсекает закрытые задачи (
labels in (…)), полныйrefreshвозвращает и их — членство всех своих задач в закрытых спринтах приходит первым же полнымrefreshагента v15. -
«Доска связей» → «Связи» (сейчас пункт меню называется «Доска связей», адрес
/jira/board);/app/boardперенаправляет сразу на/app/jira/links. Документы, где адрес упомянут (README, CLAUDE.md репозитория, e2e), — в том же change.
Контракт данных
POST /agent/state (агент v15), необязательные поля:
- у задачи:
rank(строка илиnull),story_points(число илиnull),sprint_ids(список целых — все спринты задачи); присланноеsprint_idsзаменяет все строкиissue_sprintзадачи, задача без поля свои строки сохраняет; - в пачке:
boards: [{id, type, columns: [{name, statuses: [строка]}] | null, kanban_backlog: bool | null, sprints: [{id, name, state, start_date, end_date, complete_date, goal}]}]; доска изboardsзаменяет свою строкуjira_boardи все свои строкиjira_sprint. Даты спринтов — строки Jira как есть илиnull. Пустойrefresh— одна пачка{issues: [], links: [], boards}.
GET /agile?project=P →
{board: {id, type, columns, kanban_backlog, fetched_at} | null,
sprints: [{id, name, state, start_date, end_date, complete_date, goal,
count, count_done, unestimated, points, points_done, issues: [строка]}],
backlog: [строка] | null,
board_issues: [строка] | null}
Строка задачи: key, summary, issue_type, status, priority, assignee, epic_key, story_points, rank, done (done —
статус в последней колонке). Порядок спринтов: активные (по дате начала), будущие (по id), закрытые (по дате
завершения, новые сверху). backlog: scrum — свои задачи не в активном и не в будущем спринте и не выполненные;
kanban — свои задачи в статусах первой колонки, если kanban-бэклог включён, иначе null. board_issues (только
kanban) — свои задачи в статусах остальных колонок (при выключенном или неизвестном бэклоге — и первой) и без колонки;
выполненные — только с jira_updated за последние 14 дней. Всё
по рангу.
Risks / Trade-offs
- Поля Jira банка не проверены: имя поля story points, тип поля ранга, признак kanban-бэклога в
board/{id}/configurationJira Server. Проверка — на живой Jira при выкатке агента; не найдено — агент передаётnull, экраны показывают «—», признак kanban-бэклогаnull— раздела «Бэклог» нет. - Объём закрытых спринтов: агент читает список всех спринтов доски каждым
refresh(постранично, без задач) — на доске с десятками спринтов это несколько запросов; задачи закрытых спринтов не дочитываются, членство — из поля спринта задач. - Story points на экране редактирования Jira: если поле не на экране AURORA,
ensure_fields story_pointsполучит ошибку Jira —failedс её текстом; нужна настройка Jira (у владельца/администратора).
Decisions made without discussion
- 24.10.2026 — R-1 (design #3): kanban-бэклог — раздел страницы «Kanban», не пункт меню. Цена ошибки — перенести раздел в отдельную страницу.
- 24.10.2026 — R-2 (design #7): «выполнено» у закрытого спринта — по текущему статусу. Цена ошибки — считать по журналу изменений, когда он появится по закрытым спринтам.
- 24.10.2026 — R-3 (design #8): только свои задачи проекта на экранах и в MCP. Цена ошибки — добавить соседей флагом.
- 24.10.2026 — R-4 (design #10): поле оценки —
field_ids.story_pointsили поиск агентом. Цена ошибки — настройка руками. - 24.10.2026 — R-5 (design #11): имена и параметры команд
ensure_sprint_meta,ensure_sprint_state,ensure_rank; «выше/ниже» — отношение, а не «вплотную». Цена ошибки — переименование типов до выкатки. - 24.10.2026 — R-6 (design #13): данные досок — в каждом
refresh, в том числе по ключам. Цена ошибки — лишние запросы к Jira (несколько на доску). - 24.10.2026 — R-7 (design #15): один маршрут
GET /agile. Цена ошибки — разделить маршрут. - 24.10.2026 — R-8 (контракт): kanban — выполненные задачи за 14 дней. Цена ошибки — число в настройке.
- 24.10.2026 — R-9 (design #6): колонка «Без колонки» для статусов вне настроек доски. Цена ошибки — нет.
- 24.10.2026 — R-10 (design #13): хвост пакета со спринтами —
refreshпо JQL проекта; пустойrefreshшлёт пачку сboards. Цена ошибки — лишнее чтение проекта (~3 запросаsearch). - 24.10.2026 — R-11 (design #17–#20): правила kanban без бэклога, поиска спринта по имени, ошибок состояний, проектной
принадлежности
ensure_rank, формы полей. Цена ошибки — правка проверок. - 24.10.2026 — R-12 (design #21–#26): условные пункты меню, тип доски в
jira_project, три состояния данных доски, даты в поясе браузера, история из базовой JQL, переименование «Доска связей». Цена ошибки — правка веба. - 24.10.2026 — R-28 (план агента R-64, R-65): поле Jira не найдено — ключ у задач не передаётся (хаб сохраняет прежнее),
а не
null; ошибка чтения списка спринтов — доска не входит вboards(иначе стёрлась бы история спринтов). Цена ошибки — нет. - 24.10.2026 — R-13 (контракт ядра):
CommandHooks.tailполучает(conn, cmds, project)вместо(cmds): хвост пакета со спринтовыми командами —refreshпо JQL проекта пакета (design #13), а без соединения и проекта хук JQL не построит. Правится ядро (core/modules.py,core/queue.py), wiki (tail=lambda conn, cmds, project: None) и две заглушки в тестах. Цена ошибки — вернуть сигнатуру и строить хвост вcheck_batch(дописываяrefreshв конец пакета). - 24.10.2026 — R-14 (форма
boards): у доски вboardsполеsprintsотсутствует илиnull— спринты доски в зеркале не меняются (агент не смог их прочитать, agent spec «прежние правила для спринтов»);[]— у доски спринтов нет, строки удаляются.columnsотсутствует илиnull— пишетсяNULL.boards: nullв пачке — как отсутствие. Цена ошибки — устаревшие спринты на экране до следующегоrefreshсsprints: []. - 24.10.2026 — R-15 (проверки постановки): хаб проверяет
start_date/end_date(ГГГГ-ММ-ДД, настоящая дата, начало не позже конца), непустыеsprint/rename_to,state∈ {active,closed},move_open_to(обязателен приclosed, 422 приactive, не равенsprint), уensure_rankровно одно изbefore/afterи не сама задача,story_points— конечное число ≥ 0 илиnull. Цена ошибки — правка проверок. - 24.10.2026 — R-16 (подтверждение по зеркалу):
ensure_fieldsсоstory_pointsподтверждается, только еслиissue.story_points == params.story_pointsточно (null=null,0≠null); прочие поля — как раньше. Цена ошибки — лишняя довыдача. - 24.10.2026 — R-17 (
GET /agile):board_type = null→sprints: [],backlog: null,board_issues: null; scrum-бэклог исключает задачи в активном/будущем спринте любой доски изjira_sprint(спринт, которого нет вjira_sprint, не учитывается); kanban: выполненная задача безjira_updatedне показывается;kanban_backlogв ответе —true/false/null; суммыpoints/points_done— сумма чисел (без оценок —0). Цена ошибки — правка выборки. - 24.10.2026 — R-18 (Kanban с бэклогом): при
kanban_backlog = trueпервая колонка доски не рисуется — её задачи в «Бэклог (N)» (как kanban-бэклог Jira). Цена ошибки — вернуть колонку. - 24.10.2026 — R-19 (три состояния): «Нужен агент v15 и обновление из Jira» —
board = nullилиboard.fetched_at = null; «Не удалось прочитать настройки доски из Jira» —fetched_atесть,columns = null(design #23 вместо противоречивой фразы web-app «board.columns = nullили ответ без доски»). Цена ошибки — правка условия. - 24.10.2026 — R-20 (кнопка «Обновить»): на экранах — существующая
RefreshButton(«Обновить из Jira», в шапке и в сообщениях трёх состояний). Правило «кнопок нет, кроме раскрытия секций и карточки» к ней не относится: она читает Jira, а не пишет. Цена ошибки — убрать кнопку из шапки. - 24.10.2026 — R-21 (переименование): «Связи» — подпись пункта меню, заголовок вкладки и ссылка в карточке jira на
«Обзоре»; заголовок
h1страницы остаётся «Доска связей» (спека говорит о меню и вкладке). Цена ошибки — правкаBoardHeaderи e2egeometry.spec.ts. - 24.10.2026 — R-22 (смена проекта): фраза «Проект в приложении» («смена проекта на странице, которой нет у нового
проекта, открывает «Обзор»») не меняется в коде: других условных страниц нет, а для экранов доски действует design
#21 (адрес остаётся, сообщение «У проекта нет …-доски») —
Layout.selectуже сохраняет раздел. Цена ошибки — переход на «Обзор» для новых страниц. - 24.10.2026 — R-23 (NotFound): «не найдено» перечисляет только безусловные пункты (без
when). Цена ошибки — нет. - 24.10.2026 — R-24 (склонение): «N задач» — с русским склонением (1 задача, 2 задачи, 5 задач). Цена ошибки — нет.
- 24.10.2026 — R-25 (тексты MCP): шапки таблиц и отказы
sprints/sprint/backlog— по задаче 7. Цена ошибки — правка текста. - 24.10.2026 — R-26 (даты): хаб сортирует спринты по
history.jira_utc(дата); веб передnew Dateприводит±HHMMк±HH:MM. Цена ошибки — нет. - 24.10.2026 — R-27 (e2e): тестовый хаб засевает второй проект
KANBAN_E2E(доска 5120, kanban, задачи AURORA-9101…9103); доска AWPCA — scrum со спринтами 101 (закрыт), 102 (активный), 103 (будущий). Цена ошибки — правка сида. - 24.10.2026 — R-29: трейлер коммитов — Co-Authored-By: Claude Sonnet 5.5 (инструкция сессии точнее перечня плана). Цена ошибки — переписать трейлеры, ничего не пушилось.
- 24.10.2026 — R-30 (задача 3,
GET /agile, по R-17): доска с заданнымboard_id, но неизвестным типом (board_typeNULL) —boardсtype: null,sprints: [],backlog: null,board_issues: null; kanban безcolumnsприkanban_backlog = 1—backlog: [], все задачи вboard_issues. Выбрано исполнителем задачи 3, подтверждено ревью как следствие R-17. Цена ошибки — правка выборки вagile.view. - 24.10.2026 — R-31 (задача 4, по R-15): пустая строка в
move_open_toприstate = activeи вbefore/afterуensure_rankтрактуется как «не задано»;ensure_sprint_metaбез параметров, кромеsprint, допустима. Выбрано исполнителем задачи 4. Цена ошибки — ужесточить проверку (422 вместоfailedу агента). - 24.10.2026 — R-32 (задача 9, по web-app): блок «Прошлые спринты (N)» на «Бэклоге» не рисуется при N = 0 (спека не требует пустой блок). Выбрано исполнителем задачи 9. Цена ошибки — рисовать «Прошлые спринты (0)» свёрнутым.