From 9eddc13b62a519d248acb52af0a58565de91bc7d Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Sat, 24 Oct 2026 18:09:20 +0400 Subject: [PATCH] OpenSpec: design-only change agent-v15-sprints (#49) Co-Authored-By: Claude Opus 5.5 --- .../changes/agent-v15-sprints/.openspec.yaml | 2 + openspec/changes/agent-v15-sprints/design.md | 61 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 openspec/changes/agent-v15-sprints/.openspec.yaml create mode 100644 openspec/changes/agent-v15-sprints/design.md diff --git a/openspec/changes/agent-v15-sprints/.openspec.yaml b/openspec/changes/agent-v15-sprints/.openspec.yaml new file mode 100644 index 0000000..1f5d280 --- /dev/null +++ b/openspec/changes/agent-v15-sprints/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-10-24 diff --git a/openspec/changes/agent-v15-sprints/design.md b/openspec/changes/agent-v15-sprints/design.md new file mode 100644 index 0000000..31b4c1c --- /dev/null +++ b/openspec/changes/agent-v15-sprints/design.md @@ -0,0 +1,61 @@ +# agent-v15-sprints — решения агента + +Спека и дизайн изменения — в `sdlc-hub/openspec/changes/sprints-backlog/` (issue aep/sdlc-hub#108, aep/jira-agent#49). +Агентская часть: требование `agent` «Спринты, ранг и оценки» (ADDED); контракт команд — `commands` «Команда — +желаемый результат на одной задаче» (MODIFIED: `story_points` в `ensure_fields`, `ensure_sprint_meta`, +`ensure_sprint_state`, `ensure_rank`) и абзацы о командах спринтов; формат состояния — `hub-api` «Приём досок и +спринтов» (ADDED). Версия агента — 15. + +## Решения агента + +1. **Поля Jira — по типу поля, один раз на процесс.** `GET /rest/api/2/field` при первой нужде, кэш на процесс: + - ранг — поле с `schema.custom = "com.pyxis.greenhopper.jira:gh-lexo-rank"`; + - спринт — поле с `schema.custom = "com.pyxis.greenhopper.jira:gh-sprint"`; + - story points — поле с именем «Story Points» (без учёта регистра); несколько — первое по id. + Перекрытие из настроек хаба: `field_ids.rank`, `field_ids.sprint`, `field_ids.story_points` (если заданы — поиск не + нужен). Ошибка `GET /field` — поля считаются ненайденными, причина в лог; повтор — в следующем `refresh`. +2. **`sprint_ids` — из поля спринта в том же `search`**, а не обходом спринтов: id поля спринта добавляется в `fields` + поиска. Значение — список; элемент — объект с `id` (Jira 8+) или строка + `com.atlassian.greenhopper.service.sprint.Sprint@…[id=72,…]` (Jira Server): id — из `id` или из `id=` в строке; + не разобралось — элемент пропускается с записью в лог. Поля нет — `sprint_ids` не передаётся (хаб сохранит + прежнее). Имя текущего спринта (`sprint`) — как в v12 (обход активных и будущих спринтов досок), без изменений. +3. **`rank`, `story_points` — тоже из `search`**: id полей добавляются в `fields`. `story_points` — число (`int` или + `float`), иное — `null`; поле не найдено — `story_points` не передаётся у задач и запись в лог один раз. +4. **Данные досок.** Для каждой доски `scrum`/`kanban` из `refresh.boards` (тип известный или только что определённый): + - колонки — `GET /rest/agile/1.0/board/{id}/configuration` → `columnConfig.columns[]` `{name, statuses: [{id}]}`; + id статусов переводятся в имена по `GET /rest/api/2/status` (кэш на процесс; неизвестный id — пропускается); + - kanban-бэклог (только `kanban`): `GET /rest/agile/1.0/board/{id}/backlog?maxResults=0` — 200 → `true`, 400 → `false`, + иное → `null`; проверить на Jira банка при выкатке; + - спринты (только `scrum`): `GET /rest/agile/1.0/board/{id}/sprint` без фильтра состояния, постранично (50); + элемент → `{id, name, state, start_date: startDate, end_date: endDate, complete_date: completeDate, goal}`; + - ошибка чтения настроек доски — `columns: null`, `kanban_backlog: null`, спринты — если прочитались; причина в лог. + `boards` прикладывается к первой пачке `POST /agent/state`; при пустой выборке — одна пачка `{issues: [], links: [], + boards}`. Доски без данных (тип `null`) в `boards` не входят. +5. **Даты команд спринта.** `start_date`/`end_date` (`ГГГГ-ММ-ДД`) → `ГГГГ-ММ-ДДT09:00:00.000<смещение>` и + `ГГГГ-ММ-ДДT18:00:00.000<смещение>`, смещение — локального времени агента (`time.strftime("%z")`, вид `+0300`). + Сравнение с Jira — по дате (первые 10 символов `startDate`/`endDate`). +6. **`ensure_sprint_meta`.** Спринты доски `board_id` — все состояния (тот же обход, что в п. 4, кэш на команду); + поиск по имени `sprint` (точное совпадение). Нет — `POST /rest/agile/1.0/sprint` `{name: rename_to or sprint, + originBoardId, startDate?, endDate?, goal?}`; есть — `POST /rest/agile/1.0/sprint/{id}` только с отличающимися + полями (`name` при `rename_to`, даты по п. 5, `goal`); нет отличий — `done` без записи. Ошибки — по контракту + `commands`. +7. **`ensure_sprint_state`.** `active`: спринт `future` — `POST /sprint/{id}` `{state: "active", startDate, endDate}` + (из спринта; нет дат — `failed`). `closed`: колонки доски (п. 4) → набор статусов последней колонки; задачи спринта + (`GET /sprint/{id}/issue?fields=status`, постранично) не в этих статусах — в `move_open_to` (`POST /sprint/{to}/issue` + или `POST /backlog/issue`, по 50 ключей), затем `POST /sprint/{id}` `{state: "closed"}`. Колонки не прочитались — + `failed` «не удалось прочитать колонки доски». Между записями — зазор типа команды, как в `ensure_link`. +8. **`ensure_rank`.** Ранг обеих задач — `search` по `key in (key, other)` с полем ранга; нет поля ранга — `failed` + «поле ранга не найдено»; уже выше/ниже (сравнение строк) — `done {changed: false}`; иначе + `PUT /rest/agile/1.0/issue/rank` `{issues: [key], rankBeforeIssue | rankAfterIssue: other, rankCustomFieldId}` → + `{changed: true}`. +9. **`story_points` в `ensure_fields`.** Поле по п. 1; не найдено — `failed` «поле оценки не найдено в Jira: задайте + field_ids.story_points». Сравнение чисел: равны, если `abs(a - b) < 1e-9`; `null` — очистка (`None` в `PUT`). + +## Decisions made without discussion + +| Маркер | Дата | Решение | Обоснование | Цена ошибки | +|---|---|---|---|---| +| R-60 | 2026-10-24 | Поля ранга, спринта, оценки — по типу/имени через `GET /field`, перекрытие `field_ids` (п. 1) | Владелец полей не знает, оценок в AURORA ещё нет | Не то поле «Story Points» — задать `field_ids.story_points` | +| R-61 | 2026-10-24 | `sprint_ids` из поля спринта в `search`, разбор строкового вида Jira Server (п. 2) | Обход закрытых спринтов — N запросов на спринт; поле есть у каждой задачи | Формат поля банка иной — `sprint_ids` пропадут, видно на живой проверке | +| R-62 | 2026-10-24 | Kanban-бэклог — по ответу `GET /board/{id}/backlog` (п. 4) | В `configuration` Jira Server явного признака нет | `null` — у kanban нет раздела «Бэклог» | +| R-63 | 2026-10-24 | Время дат спринта — 09:00 и 18:00 по локальному времени агента (п. 5) | Команда несёт только дату | Другое время в Jira — правка спринта руками |