Andrey Epifancev ca231ba2ae docs(openspec): архив agent-self-update (#22)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 22:58:46 +04:00

90 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 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 — обновится только вручную»