6.8 KiB
Архитектура
Агент — один файл src/agent.py и запускатель src/run.py (Python 3.9, только стандартная библиотека). Тесты в tests/ работают на фейках
Jira и хаба. Спека: ../jira-hub/openspec/changes/bridge-core/, план агента: docs/superpowers/plans/2026-10-11-bridge-core-agent.md (план хаба: ../jira-hub/docs/superpowers/plans/2026-10-11-bridge-core-hub.md).
Разделы файла src/agent.py
| Раздел | Классы и функции | Ответственность | Что использует |
|---|---|---|---|
| Настройки | load_config |
читает agent.json, проверяет обязательные поля |
— |
| HTTP | Http, HttpError, JiraClient, HubClient, multipart_file, wire_filename |
запросы; Http.send — запрос с готовым телом (JSON-вызов call идёт через него); JiraClient считает вызовы, повторяет 429/503 по Retry-After (до 5 запросов, не более 4 ожиданий), хранит время последнего вызова; JiraClient.upload — вложение multipart/form-data (таймаут не меньше 120 с); HubClient.file — файл вложения у хаба (base64 в JSON) |
urllib |
| Папка | FolderTransport |
транспорт folder: к хабу через общую папку: req/<id>.json → реле хаба → resp/<id>.json; ошибки как у Http; HubClient делегирует ему call (спека jira-hub/openspec/changes/folder-transport/) |
os, uuid |
| Журнал | Journal, _parse_at |
дозапись journal.jsonl: started → finished → acked; восстановление; prune (из CLI — под блокировкой журнала) |
файловая система |
| Исполнитель | Executor и do_<тип> |
по одному методу на тип команды: читает Jira, пишет только разницу; refresh читает и отправляет состояние в хаб; ensure_attachment берёт файл вложения у хаба (hub_file: sha256 и размер, повреждение — HubError с повтором); доски — из параметров команд (ensure_sprint.board_id, refresh.boards, тип доски — board_type), board_id настроек — запасной |
JiraClient, HubClient (refresh и файл в ensure_attachment), настройки хаба |
| Самообновление | read_update_state, write_update_state, Updater, launcher_version |
update.json; после claim без команды — проверка agent_release, скачивание, sha256, компиляция, selftest, staged; подтверждение ok новой версией; без запускателя — только подсказка в лог |
HubClient, subprocess |
| Цикл | Runner |
claim, пауза до команды, журнал, выполнение, отчёт до подтверждения, восстановление при старте; после claim без команды спрашивает Updater, готова версия — выход с EXIT_UPDATE (75) |
HubClient, JiraClient, Journal, Executor |
| CLI | cmd_run, cmd_check, cmd_selftest, cmd_prune, main |
команды run, check, selftest, prune (run и prune берут acquire_lock на <журнал>.lock); обработка сигналов |
Runner, Journal |
Запускатель src/run.py
Не импортирует agent.py (тот меняется). Launcher запускает agent.py run дочерним процессом с
JIRA_AGENT_LAUNCHER; коды 0/130 — выход; 75 — apply_update (копия в agent.py.prev, os.replace, trial);
падение в trial или 10 минут без ok — rollback; иначе перезапуск с паузой 5…300 с. Блокировка run.py.lock;
SIGINT при живом агенте игнорирует (Ctrl-C получает агент), SIGTERM пересылает. Спека — «Запускатель run.py».
reconcile при старте и in_trial перед каждым запуском сверяют update.json с sha256 agent.py (таблица — design 1 change'а
agent-v9-hardening); коды агента: 75 — подмена, 73 — журнал занят (не сбой версии); OSError при подмене — rejected,
запускатель не падает.
Границы
- Код новой версии приходит только через API хаба (
GET /agent/release) и пишется в каталог агента; общая папка для кода не используется. - Пароль и токен Jira — только в
agent.json. В хаб, журнал и логи не попадают. - Политика темпа не хранится в агенте: зазоры (
pacing) иpause_secondsприходят от хаба. Агент досыпает паузу до зазора от последнего вызова Jira (JiraClient.last_call_at). - Одна команда в полёте: цикл берёт следующую только после выполнения и подтверждения отчёта.
- Отчёт до подтверждения:
send_resultповторяет каждые 10 с; 404/409/422 от хаба считаются закрытием. - Журнал пишется трижды на команду вокруг выполнения:
started— после паузы, перед первым обращением к Jira;finished— после результата;acked— после подтверждения хаба. Восстановление опирается только на этот порядок. - Каждый обработчик
Executorвозвращаетstatus/result/errorи счётчики. Исключение, которое не обработано в обработчике,Runner.executeпревращает вfailedи не роняет цикл. - Тесты не ходят во внешние сервисы; фейки —
tests/fake_jira.pyиtests/fake_hub.py.
Открытые вопросы
- HTTPS хаба с корпоративным CA:
HubClientне принимаетca_bundle/verify_ssl(ограничение README, пункт 2). - Спринт задач закрытых спринтов:
sprint = null(рулинг R-6).