17 KiB
MODIFIED Requirements
Requirement: Команда — желаемый результат на одной задаче
Каждая команда SHALL описывать желаемое состояние одной задачи Jira и выполняться агентом как «прочитать → сравнить →
записать только разницу». Повторное выполнение любой команды с тем же результатом в Jira SHALL завершаться done
без записи. fetch_attachment в Jira не пишет: она читает файл вложения и передаёт его хабу; повтор безопасен. Типы команд и их контракт:
| Тип | Параметры | Что читает | Что пишет |
|---|---|---|---|
ensure_issue |
temp_key (new:<n>), issue_type (task/epic/bug), summary, description, priority, duedate, planned_start, planned_end, labels, epic_key, assignee |
поиск project = <proj> AND reporter = currentUser() AND created >= -7d, точное сравнение summary |
при отсутствии — POST /issue с типом из issue_types.<issue_type> (для эпика — с epic_name_field_id); результат key |
ensure_fields |
key, любое подмножество: summary, description, priority, duedate, planned_start, planned_end, labels_add, labels_remove, epic_key, assignee, story_points (число ≥ 0 или null — очистить) |
GET /issue/{key} |
PUT /issue/{key} только с отличающимися полями; эпик — по epic_link.mode; оценка — в поле story points (см. agent, «Спринты, ранг и оценки»); поля нет — failed «поле оценки не найдено в Jira: задайте field_ids.story_points» |
ensure_comment |
key, marker, body |
GET /issue/{key}/comment |
комментарий с marker в начале: PUT если текст отличается, POST если нет |
ensure_status |
key, target (имя статуса или список-путь) |
GET /issue/{key} (статус), GET /issue/{key}/transitions на каждом шаге |
POST /transitions по шагам пути; при отсутствии перехода — failed с перечнем доступных |
ensure_link |
source, target, link_type (логическое имя из link_types), replace (bool) |
GET /issue/{source} (issuelinks) |
нет нужной и нет конфликта — POST /issueLink; конфликт (другой тип из link_types или обратное направление) и replace — DELETE лишней, затем POST; конфликт без replace — failed с описанием |
ensure_sprint |
key, sprint (имя или backlog); board_id хаб дописывает при постановке (от клиента — 422) |
GET /issue/{key} (поле спринта), список спринтов доски board_id (активные и будущие) |
POST /rest/agile/1.0/sprint/{id}/issue или POST /rest/agile/1.0/backlog/issue |
ensure_sprint_meta |
sprint (имя), необязательные rename_to, start_date, end_date (ГГГГ-ММ-ДД), goal; board_id хаб дописывает при постановке (от клиента — 422) |
спринты доски board_id (все состояния) |
спринта с именем sprint нет — POST /rest/agile/1.0/sprint (originBoardId, имя rename_to или sprint, даты, цель); есть — POST /rest/agile/1.0/sprint/{id} только с отличающимися полями; спринт закрыт — failed «спринт закрыт»; результат {sprint_id, created} |
ensure_sprint_state |
sprint, state (active или closed), move_open_to (имя активного или будущего спринта той же доски или backlog; при closed обязателен, при active — 422); board_id — как у ensure_sprint_meta |
спринты доски (все состояния), задачи спринта и колонки доски | active: спринт будущий — POST /sprint/{id} state = active (нет дат у спринта — failed «у спринта нет дат: задайте ensure_sprint_meta»); closed: задачи спринта не в последней колонке доски — в move_open_to, затем state = closed; уже в нужном состоянии — ничего; результат {sprint_id, moved: [ключ…]} |
ensure_rank |
key и ровно одно из before, after (ключ другой задачи) |
ранг обеих задач | key уже выше before (ниже after) — ничего; иначе PUT /rest/agile/1.0/issue/rank (rankBeforeIssue / rankAfterIssue) |
ensure_attachment |
key, file (sha256 файла в хранилище хаба, 64 hex-символа), filename (имя вложения в Jira, без /, \, " и управляющих символов); size хаб дописывает при выдаче |
GET /issue/{key}?fields=attachment |
вложения с тем же filename и размером нет — файл у хаба (GET /agent/files/{file}), затем POST /issue/{key}/attachments (multipart); есть — ничего; результат attachment_id |
refresh |
keys (список) или jql; boards ([{id, type}] — доски проектов, type — scrum, kanban или null) хаб дописывает при выдаче |
search с полями зеркала, по 100 на страницу; плюс дочитывание соседей по issuelinks; доски с неизвестным типом — GET /rest/agile/1.0/board/{id} |
ничего в Jira; состояние уходит в хаб; результат — `boards: {"": "scrum" |
fetch_attachment |
key, attachment_id (id вложения Jira, строка из цифр) |
GET /issue/{key}?fields=attachment (имя и размер вложения) |
ничего в Jira; содержимое — GET <base_url>/secure/attachment/{id}/{имя}, затем хабу POST /agent/files; результат {attachment_id, file, size} |
Доску для ensure_sprint хаб SHALL определять при постановке: проект задачи — проекты, чьи метки есть у задачи в
зеркале (для временного ключа — в labels её ensure_issue в пакете, после дописывания метки проекта); board_id —
доска этого проекта. 422 при постановке: задача ни в одном проекте; в нескольких проектах с разными досками; у проекта
нет доски; тип доски kanban («у проекта P канбан-доска, спринтов нет»); тип доски ещё не известен («тип доски проекта P
ещё не известен — дождитесь обновления из Jira»). boards у refresh хаб SHALL дописывать при каждой выдаче (в базе
не хранится): все доски проектов без повторов. По результату refresh хаб SHALL записать присланные типы scrum и
kanban в проекты с этой доской; null тип не меняет.
Доску для ensure_sprint_meta и ensure_sprint_state хаб SHALL определять при постановке по проекту пакета
(project пакета): пакет без проекта — 422 «команды спринта — только в пакете проекта»; у проекта нет доски, доска
kanban или тип ещё не известен — 422 с теми же текстами, что у ensure_sprint. ensure_rank в пакете проекта
принадлежит проекту по key; before/after — любая задача. Повтор команды (command_retry) SHALL снимать board_id
у ensure_sprint_meta и ensure_sprint_state, как у ensure_sprint. Хвостовой refresh пишущего пакета с
ensure_sprint_meta или ensure_sprint_state SHALL быть refresh {jql: <JQL проекта пакета>} (project = <project_key> AND labels in ("<метка>")); иначе — как раньше, по ключам команд. Подтверждения по состоянию зеркала у
ensure_sprint_meta, ensure_sprint_state, ensure_rank нет.
ensure_sprint_meta SHALL искать спринт по имени среди всех спринтов доски: закрытый — failed «спринт закрыт»;
несколько с этим именем — failed «несколько спринтов с именем …: <id…>»; rename_to на имя другого спринта доски —
failed «имя занято». ensure_sprint_state: closed для будущего спринта — failed «спринт не начат»; active для
закрытого — failed «спринт закрыт»; результат ensure_rank — {changed}.
Семантика ensure_link: source блокирует/ссылается на target; агент в POST /issueLink SHALL ставить source в
inwardIssue, target в outwardIssue (проверено на Jira банка 11.10.2026).
Текст описания (ensure_fields) и тело комментария (ensure_comment) агент SHALL сравнивать с текущим значением в Jira
без различия переводов строк \r\n и \n; записывается текст команды как есть. Jira хранит присланный текст, а правленный
в веб-интерфейсе — с \r\n (проверено на Jira банка 13.10.2026).
Scenario: Повтор уже выполненной команды
- WHEN
ensure_fields {key: AURORA-1533, duedate: 2026-10-23}выполняется, а в Jira срок уже 23.10 - THEN агент не делает
PUT, отчётdone,jira_calls = 1
Scenario: Описание отличается только переводами строк
- WHEN
ensure_fields {key: X, description: "а\nб"}, а в Jira описание X —"а\r\nб" - THEN агент не делает
PUT, отчётdoneсchanged = []
Scenario: Замена связи
- WHEN
ensure_link {source: AURORA-1537, target: AURORA-1538, link_type: "связанные", replace: true}и в Jira между ними «блокирует начало» 1537→1538 - THEN
DELETEстарой связи, пауза по политике,POSTновой; отчётdoneс обеими операциями
Scenario: Связь-конфликт без replace
- WHEN та же команда с
replace: false - THEN
failed, вerrorнайденная связь, Jira не изменена
Scenario: Переход по пути
- WHEN
ensure_status {key: X, target: ["Разработка", "Готово"]}и X в «Ожидание разработки» - THEN два
POST /transitionsс паузой между ними; если X уже «Готово» —doneбез записи
Scenario: Создание ошибки
- WHEN
ensure_issue {temp_key: new:1, issue_type: bug, summary: "ОСВ 60 не распознана", labels: [AWPCA]}, в настройкахissue_types.bug = {name: "Ошибка"}безid - THEN агент создаёт задачу с
issuetype = {name: "Ошибка"}; отчётdoneс ключом
Scenario: Вложение уже есть
- WHEN
ensure_attachment {key: X, file: <sha>, filename: "ОСВ 60.xlsx"}, у X в Jira есть вложение «ОСВ 60.xlsx» того же размера - THEN агент не скачивает файл у хаба и не загружает его в Jira; отчёт
doneсattachment_idнайденного,jira_calls = 1
Scenario: Вложение с тем же именем, другого размера
- WHEN та же команда, у X есть «ОСВ 60.xlsx» другого размера
- THEN агент загружает файл вторым вложением с тем же именем (Jira это допускает); отчёт
doneс новымattachment_id
Scenario: Создание с проверкой дубля
- WHEN
ensure_issueповторяется после падения агента, а задача с той же темой от нас уже создана час назад - THEN агент не создаёт вторую, отчёт
doneс ключом найденной
Scenario: Спринт задачи проекта
- WHEN у AURORA-1538 метка
AWPCA, проектAWPCAс доской 4216 типаscrum;ensure_sprint {key: AURORA-1538, sprint: "Спринт 2"} - THEN пакет создан, в параметрах команды
board_id = 4216
Scenario: Спринт на канбане
- WHEN у AURORA-1 метка
STOCKS_DRRK, доска проекта —kanban;ensure_sprint {key: AURORA-1, sprint: "Спринт 2"} - THEN 422 «у проекта STOCKS_DRRK канбан-доска, спринтов нет»
Scenario: Тип доски определён
- WHEN проект
STOCKS_DRRKс доской 5120 иboard_type = null; выдаётсяrefresh, агент вернулboards: {"5120": "kanban"} - THEN при выдаче в параметрах
boardsесть{id: 5120, type: null}; после отчёта у проектаboard_type = kanban
Requirement: Политика темпа и пауза на команде
Хаб SHALL хранить pacing как минимальные зазоры в секундах по типу команды (по умолчанию: ensure_issue и
ensure_fields 120, ensure_comment, ensure_status, ensure_link, ensure_sprint, ensure_sprint_meta, ensure_sprint_state, ensure_rank, ensure_attachment 30, refresh и fetch_attachment 2; тип, которого нет в
сохранённой настройке pacing, получает зазор по умолчанию). При выдаче команды
хаб SHALL сообщить pause_seconds = зазор по типу выдаваемой команды; агент SHALL выдержать его от времени своего
последнего вызова Jira. Внутри одной команды с несколькими записями (ensure_link с replace, путь переходов) агент
SHALL выдерживать зазор типа команды между записями.
Scenario: Нет следующей команды — нет паузы
- WHEN очередь пуста
- THEN
claimвозвращает пустой ответ, агент не спит сверх долгого запроса
Scenario: Создание спринта повтором не дублируется
- WHEN пакет проекта
AWPCA(доска 4216, scrum)ensure_sprint_meta {sprint: "AWPCA_Sprint_4", start_date: "2026-10-28", end_date: "2026-11-11", goal: "свод ВГО"}выполнен, затем поставлен повторно - THEN первый раз спринт создан (
created: true), второй —doneбез записи (created: false)
Scenario: Завершение спринта с переносом
- WHEN
ensure_sprint_state {sprint: "AWPCA_Sprint_3_Corrections", state: "closed", move_open_to: "AWPCA_Sprint_4"}, в спринте 37 задач, 12 не в последней колонке доски - THEN 12 задач перенесены в
AWPCA_Sprint_4, спринт закрыт; результатmoved— их ключи; повтор —doneбез записи
Scenario: Спринт без проекта пакета
- WHEN
POST /batchesбезprojectсensure_sprint_meta {sprint: "S"} - THEN 422 «команда 1 (ensure_sprint_meta): команды спринта — только в пакете проекта»
Scenario: Ранг уже такой
- WHEN
ensure_rank {key: AURORA-1612, before: AURORA-1601}, ранг 1612 меньше ранга 1601 - THEN
doneбез записи в Jira