90 lines
8.3 KiB
Markdown
90 lines
8.3 KiB
Markdown
## MODIFIED Requirements
|
||
|
||
### Requirement: Маршруты агента
|
||
Хаб SHALL предоставлять агенту следующие маршруты (все под агентским токеном):
|
||
- `POST /agent/claim` `{agent_version, settings_version, last_jira_call_at, update_state, launcher_version}` — долгий
|
||
запрос до 30 с; ответ `{command: {id, type, params, pause_seconds}, settings_version, agent_release}` или `{command:
|
||
null, settings_version, agent_release}`, где `agent_release` — `{version, sha256}` опубликованной версии агента или
|
||
`null`; обновляет `agent_status` (`last_jira_call_at = null` не затирает прежнее значение). `update_state` (объект или
|
||
`null`, `error` хранится до 500 символов) и `launcher_version` (целое или `null`) необязательны; хаб SHALL хранить
|
||
последние значения в памяти процесса, значения неверного типа игнорировать, не отвергая `claim`. Если есть команда в
|
||
`claimed`, `claim` SHALL выдать её снова (повторная выдача, см. `commands`), а не следующую из очереди.
|
||
- `GET /agent/settings` — настройки для агента (без токенов).
|
||
- `GET /agent/commands/{id}` — команда по id (для восстановления из журнала), включая `pause_seconds`.
|
||
- `POST /agent/commands/{id}/result` `{status, result, error, jira_calls, rate_limited}` — идемпотентно: повторный
|
||
отчёт по команде в терминальном статусе отвечает 200 без изменений, кроме позднего отчёта `done` с `result.key` по
|
||
`ensure_issue` в `cancelled` или `failed` без результата (результат записывается, статус не меняется, заметка `new:N`
|
||
переносится на ключ). Отчёт по команде в `queued` SHALL отвергаться 409 `not_claimed`. `error` хаб SHALL обрезать до
|
||
1500 символов.
|
||
- `POST /agent/state` `{command_id, issues: [...], links: [...], neighbor_keys: [...], page, last_page}` — загрузка состояния
|
||
пачками; `neighbor_keys` — ключи вне базовой JQL, дочитанные по связям; хаб обновляет зеркало и `sync_run`. Каждая
|
||
пачка применяется независимо; `page` и `last_page` необязательны и хабом не используются.
|
||
- `GET /agent/known-keys` — ключи зеркала (для определения соседей).
|
||
- `GET /agent/release` — опубликованная версия агента `{version, sha256, content}` (`content` — текст `agent.py`);
|
||
нет публикации или файл не сходится с манифестом по sha256 — 404 `no_release`.
|
||
|
||
#### Scenario: Выдача с подстановкой ключа
|
||
- **WHEN** следующая команда ссылается на `new:60`, а `ensure_issue` для `new:60` завершилась с `AURORA-1590`
|
||
- **THEN** `claim` отдаёт команду с `AURORA-1590` в параметрах
|
||
|
||
#### Scenario: Повторный отчёт
|
||
- **WHEN** агент дважды прислал `result` для команды 15
|
||
- **THEN** второй ответ 200, статус и результат не изменились
|
||
|
||
#### Scenario: Отчёт по невыданной команде
|
||
- **WHEN** `POST /agent/commands/16/result` с `status = done`, команда 16 в `queued`
|
||
- **THEN** 409 `not_claimed`, команда осталась `queued`
|
||
|
||
#### Scenario: Длинная ошибка
|
||
- **WHEN** агент прислал `failed` с `error` длиной 5000 символов
|
||
- **THEN** в `command.error` первые 1500 символов
|
||
|
||
#### Scenario: Поздний отчёт после предела выдач
|
||
- **WHEN** `ensure_issue {temp_key: new:8, …}` переведена хабом в `failed` «агент не отчитался после 3 выдач», затем агент
|
||
прислал `done` с `result.key = AURORA-1630`
|
||
- **THEN** команда `failed`, в `result` — `AURORA-1630`; заметка `new:8` перенесена на `AURORA-1630`
|
||
|
||
#### Scenario: Публикация в ответе claim
|
||
- **WHEN** опубликована v6, агент v5 отправил `claim`, команд нет
|
||
- **THEN** ответ `{command: null, settings_version, agent_release: {version: 6, sha256: …}}`
|
||
|
||
#### Scenario: Состояние обновления от агента
|
||
- **WHEN** агент прислал в `claim` `update_state = {state: rolled_back, from_version: 6, to_version: 7, error: "код 1 через 3 с"}`
|
||
и `launcher_version = 1`
|
||
- **THEN** `GET /agent` отдаёт это `update_state`, `launcher_version = 1` и `update_hint` «откат с v7 на v6: код 1 через 3 с»
|
||
|
||
#### Scenario: Скачивание версии
|
||
- **WHEN** агентским токеном `GET /agent/release`, опубликована v6
|
||
- **THEN** 200 `{version: 6, sha256, content}`, sha256 от `content` в UTF-8 равен `sha256`; клиентским токеном — 401;
|
||
без публикации — 404 `no_release`
|
||
|
||
## ADDED Requirements
|
||
|
||
### Requirement: Публикация версии агента
|
||
`jira-hub agent publish PATH` SHALL публиковать файл агента: текст в UTF-8 не больше 1 МБ, ровно одна строка
|
||
`AGENT_VERSION = <целое>`, версия больше опубликованной. Файл SHALL записываться в `<JIRA_HUB_HOME>/release/agent.py`
|
||
(каталог 0700), затем `manifest.json` `{version, sha256, size, published_at}`; оба — через временный файл и
|
||
переименование, манифест последним. Команда SHALL работать без запущенного `serve` и печатать «опубликована v<N>,
|
||
sha256 <первые 12 символов>…, <size> байт»; при ошибке — сообщение и код 1, опубликованное не меняется. Хаб SHALL
|
||
читать манифест при каждом `claim`, `GET /agent/release` и `GET /agent`; битый манифест означает «публикации нет»
|
||
(предупреждение в лог). `GET /agent` SHALL дополняться полями `release` (`{version, sha256, published_at}` или `null`),
|
||
`update_state`, `launcher_version` и `update_hint`: «агент запущен без run.py — обновится только вручную» (опубликована
|
||
версия выше версии агента, агент онлайн, `launcher_version = null`), «откат с v<to> на v<from>: <error>»
|
||
(`rolled_back`), «v<to> отвергнута: <error>» (`rejected`), иначе `null`.
|
||
|
||
#### Scenario: Публикация новой версии
|
||
- **WHEN** опубликована v5, `jira-hub agent publish jira-agent/src/agent.py` с `AGENT_VERSION = 6`
|
||
- **THEN** в `release/` — новый `agent.py` и манифест с `version = 6`; вывод «опубликована v6, sha256 …»; код 0
|
||
|
||
#### Scenario: Версия не выше опубликованной
|
||
- **WHEN** опубликована v6, публикуется файл с `AGENT_VERSION = 6`
|
||
- **THEN** «опубликована v6, новая должна быть больше», код 1; `release/` не изменён
|
||
|
||
#### Scenario: Нет строки версии
|
||
- **WHEN** публикуется файл без строки `AGENT_VERSION = <целое>` или с двумя такими строками
|
||
- **THEN** сообщение об ошибке, код 1; `release/` не изменён
|
||
|
||
#### Scenario: Агент без запускателя
|
||
- **WHEN** опубликована v6, агент v5 онлайн, в последнем `claim` `launcher_version = null`
|
||
- **THEN** `GET /agent` → `update_hint` = «агент запущен без run.py — обновится только вручную»
|