Andrey Epifancev 9eddc13b62 OpenSpec: design-only change agent-v15-sprints (#49)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 18:09:20 +04:00

8.0 KiB
Raw Blame History

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=<n> в строке; не разобралось — элемент пропускается с записью в лог. Поля нет — 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 — правка спринта руками