Andrey Epifancev ef48e62953 Схема v9: ранг, оценка, доски и спринты (#108)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 18:34:04 +04:00

28 KiB
Raw Blame History

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

  1. Веб — только просмотр (решение владельца 24.10.2026: «веб только смотреть… вэб это только вью»). Ни перетаскивания, ни кнопок, меняющих Jira, на новых экранах нет; все изменения — пакетами команд из MCP.

  2. Экраны по типу доски проекта (решение владельца): scrum — «Бэклог» и «Спринт»; kanban — «Kanban»; без доски — пунктов нет. Тип доски неизвестен (NULL) — пунктов нет, как без доски.

  3. Kanban-бэклог — раздел страницы «Kanban», а не отдельный пункт меню (решение агента): признак kanban-бэклога известен только из данных доски (GET /agile), а меню строится по GET /projects; отдельный пункт мигал бы при загрузке. Если бэклог у доски включён — над колонками свёрнутый список «Бэклог (N)» по рангу.

  4. «Доска» переименовывается в «Связи» (решение владельца): подпись в меню и заголовке вкладки — «Связи», адрес — /app/jira/links; /app/jira/board и прежний /app/board перенаправляют на него с сохранением строки запроса.

  5. Спринты — сущности зеркала. Таблица модуля jira jira_sprint (id Jira, доска, имя, состояние active / future / closed, даты начала, конца и завершения, цель) и issue_sprint (задача ↔ все её спринты, любого состояния). issue.sprint (имя текущего спринта) остаётся как есть — фильтры списка задач и доска связей его используют. История закрытых спринтов (решение владельца: «лучше иметь исторические данные») — из issue_sprint: Jira хранит у задачи все её спринты, агент передаёт их id.

  6. Колонки доски — из настроек доски Jira (GET /rest/agile/1.0/board/{id}/configuration): jira_board хранит колонки [{name, statuses: [имя статуса…]}] и признак kanban-бэклога. Статус задачи без колонки — колонка «Без колонки» в конце (видна, только если в ней есть задачи).

  7. «Выполнено» — статус в последней колонке доски (правило Jira для scrum-доски). Для закрытого спринта — по текущему статусу задачи (решение агента): статуса на момент закрытия хаб не знает (журнал изменений покрывает только открытые спринты и с 22.10.2026). Задача, выполненная после закрытия спринта, в прошлом спринте покажется выполненной — принятое ограничение.

  8. Свои задачи проекта. Экраны и MCP показывают только свои задачи проекта (метка проекта), соседей нет. Спринты — все спринты доски проекта, в том числе без своих задач.

  9. Ранг — строка Jira (LexoRank) как есть, порядок — сравнение строк; задачи без ранга — в конце, по ключу.

  10. Оценка — story points, число REAL (issue.story_points, NULL — нет оценки). Поле Jira: настройка field_ids.story_points, если задана; иначе агент находит поле сам (решение агента в agent-v15-sprints). Суммы по спринту: всего и выполнено; задача без оценки в сумму не входит, а число таких задач показывается.

  11. Новые типы команд, все «проверь, потом сделай», темп по умолчанию 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.
  12. Оценка ставится через ensure_fields (новое поле story_points: число ≥ 0 или null — очистить), отдельной команды нет.

  13. Данные досок — в каждом 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.

  14. Совместимость с агентом v14. Без boards и новых полей хаб ничего не теряет: прежние данные досок и задач остаются; экраны без данных доски показывают «Нужен агент v15 и обновление из Jira». Новые типы команд агенту v14 — failed «неизвестный тип» на его стороне; min_agent_version хаб не поднимает (поднимет владелец после выкатки v15).

  15. API — один маршрут GET /agile?project=<метка> (данные доски, спринты со своими задачами и суммами, бэклог). Веб и MCP читают его; отдельных маршрутов на спринт и бэклог нет.

  16. Схема v9: issue.rank, issue.story_points, таблицы jira_board, jira_sprint, issue_sprint.

  17. Kanban без бэклога (kanban_backlog false или null): первая колонка — обычная колонка доски, её задачи — в board_issues; backlog = null.

  18. 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 — нет): повторная выдача без отчёта выполняется агентом заново, это безопасно по «проверь, потом сделай».

  19. Проектный пакет: ensure_rank принадлежит проекту по key (своя задача или временный ключ пакета); before / after — любая задача зеркала. ensure_sprint_meta/ensure_sprint_state в пакете проекта проверяются по доске проекта (решение 11), не по задаче. command_retry снимает board_id у всех трёх типов со спринтом, как у ensure_sprint.

  20. Форма новых полей состояния: story_points: null и rank: null — «очистить»; sprint_ids: [] — у задачи нет спринтов; sprint_ids: null или не-список целых — 422 bad_state.

  21. Меню модуля зависит от проекта: пункт раздела модуля веба может нести условие по проекту (тип доски из GET /projects); пока проекты не загружены, условные пункты скрыты. Смена проекта на странице, которой у нового проекта нет, оставляет адрес и показывает «У проекта нет …-доски» (как прямой переход).

  22. Тип доски — одна правда, jira_project.board_type: тип из boards пачки состояния хаб записывает и в jira_board.type, и в jira_project.board_type проектов этой доски (как результат refresh, только scrum/ kanban). Меню и 422 постановки читают jira_project, экраны — GET /agile, тип в нём — из jira_project.

  23. Три состояния данных доски на экранах: данных нет (fetched_at = null) — «Нужен агент v15 и обновление из Jira»; данные есть, колонок нет (агент не смог прочитать настройки) — «Не удалось прочитать настройки доски из Jira» и обновление; колонки есть — экран. Без колонок done у всех задач — false.

  24. Даты: в командах — ГГГГ-ММ-ДД (агент переводит в формат Jira); в jira_sprint — строки Jira как есть (ISO-8601 со смещением); веб показывает их датой «ДД.ММ» в часовом поясе браузера; «осталось N дн.» — от сегодня до end_date.

  25. История закрытых спринтов — из базовой JQL: базовая JQL проектов не отсекает закрытые задачи (labels in (…)), полный refresh возвращает и их — членство всех своих задач в закрытых спринтах приходит первым же полным refresh агента v15.

  26. «Доска связей» → «Связи» (сейчас пункт меню называется «Доска связей», адрес /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}/configuration Jira 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 и e2e geometry.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 (инструкция сессии точнее перечня плана). Цена ошибки — переписать трейлеры, ничего не пушилось.