From 4eaad42d15745f1904cf080edc74228535179880 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Mon, 12 Oct 2026 00:15:04 +0400 Subject: [PATCH] =?UTF-8?q?docs(agent):=20fix=20round=201=20=E2=80=94=20Op?= =?UTF-8?q?enSpec-=D1=80=D0=B0=D0=B7=D0=B4=D0=B5=D0=BB=20=D0=B2=20CLAUDE.m?= =?UTF-8?q?d,=20README=20(=D0=BF=D1=80=D0=B5=D1=84=D0=B8=D0=BA=D1=81=20?= =?UTF-8?q?=D0=BB=D0=BE=D0=B3=D0=B0,=20timeout/poll,=20429),=20=D0=BE?= =?UTF-8?q?=D0=B3=D0=BE=D0=B2=D0=BE=D1=80=D0=BA=D0=B0=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=B5=D1=80=D0=BA=D0=B8=203.9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CLAUDE.md: восстановлен раздел «OpenSpec и superpowers» из шаблона; убрана фраза про задачу 7; тесты описаны по областям; оговорка про проверку 3.9 (ast не ловит API, только прогон на 3.9.6). - README: пример вывода check с префиксом времени; timeout и poll_seconds в доставке; 429/503 — не более 4 ожиданий, Retry-After в HTTP-date не разбирается. - ARCHITECTURE.md: формулировка повторов 429/503. - tests/test_py39.py: комментарий с ограничением проверки. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_014Typ5sXWNMFirr2va2uL8o --- ARCHITECTURE.md | 2 +- CLAUDE.md | 18 +++++++++++++----- README.md | 22 ++++++++++++++++------ tests/test_py39.py | 4 ++++ 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index f2b4352..a93867a 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -8,7 +8,7 @@ Jira и хаба. Спека: `../jira-hub/openspec/changes/bridge-core/`, пл | Раздел | Классы и функции | Ответственность | Что использует | |---|---|---|---| | Настройки | `load_config` | читает `agent.json`, проверяет обязательные поля | — | -| HTTP | `Http`, `HttpError`, `JiraClient`, `HubClient` | запросы; `JiraClient` считает вызовы, повторяет 429/503 по `Retry-After` (до 5 попыток), хранит время последнего вызова | `urllib` | +| HTTP | `Http`, `HttpError`, `JiraClient`, `HubClient` | запросы; `JiraClient` считает вызовы, повторяет 429/503 по `Retry-After` (до 5 запросов, не более 4 ожиданий), хранит время последнего вызова | `urllib` | | Журнал | `Journal`, `_parse_at` | дозапись `journal.jsonl`: `started` → `finished` → `acked`; восстановление; `prune` | файловая система | | Исполнитель | `Executor` и `do_<тип>` | по одному методу на тип команды: читает Jira, пишет только разницу; `refresh` читает и отправляет состояние в хаб | `JiraClient`, `HubClient` (только `refresh`), настройки хаба | | Цикл | `Runner` | claim, пауза до команды, журнал, выполнение, отчёт до подтверждения, восстановление при старте | `HubClient`, `JiraClient`, `Journal`, `Executor` | diff --git a/CLAUDE.md b/CLAUDE.md index c2fcd0e..cb306a8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,8 +9,8 @@ ## Стек и ограничения - Python 3.9.6 (как на виртуалке), только стандартная библиотека, без venv и зависимостей. -- Запрещено: синтаксис 3.10+ (`match`, `X | Y` в аннотациях, `dict | dict`), сторонние импорты. - Проверяет `tests/test_py39.py` (разбор с `feature_version=(3, 9)` и разрешённый список импортов). +- Запрещено: синтаксис 3.10+ и сторонние импорты. + `tests/test_py39.py` ловит синтаксис 3.10+ (match и т. п.) и сторонние импорты; `X | Y` в аннотациях и API новее 3.9 подтверждает только прогон на /usr/bin/python3 3.9.6. - `agent.json` содержит учётные данные Jira — не коммитить, не отправлять в хаб, не выносить с виртуалки. Шаблон без секретов — `src/agent.example.json`. @@ -34,11 +34,19 @@ - Тесты в `tests/`, запуск из корня worktree. Внешних сервисов нет: Jira и хаб заменяют фейки `tests/fake_jira.py` и `tests/fake_hub.py`. - `tests/_common.py` — общая настройка: путь к `src`, `SETTINGS` (как их отдаёт `/agent/settings`), `jira_cfg()`, `no_sleep()`. Паузы в тестах отключаются через `ZERO_PACING` и `no_sleep`. -- Для каждого типа команды — свой набор: `test_executor_fields.py`, `test_executor_links.py`, `test_refresh.py`; - цикл и восстановление — `test_runner.py`; журнал — `test_journal.py`; HTTP и 429 — `test_clients.py`. +- Тесты разбиты по областям, а не по типам команд: `test_executor_fields.py` (`ensure_issue`, `ensure_fields`, `ensure_comment`), + `test_executor_links.py` (`ensure_status`, `ensure_link`, `ensure_sprint`), `test_refresh.py` (`refresh`), + `test_runner.py` (цикл и восстановление), `test_journal.py` (журнал), `test_clients.py` (HTTP и 429). - Golden-фикстур пока нет: каждый тест строит сценарий в коде. ## Первая подсистема Исполнитель команд и цикл агента (спека `agent/spec.md`, `commands/spec.md`) — реализованы целиком: все типы команд, -журнал, восстановление после перезапуска, CLI `run|check|prune`. Задача 7 плана — документация и проверка совместимости с 3.9. +журнал, восстановление после перезапуска, CLI `run|check|prune`. План и спека живут в `jira-hub`: `openspec/changes/bridge-core/` и `docs/superpowers/plans/2026-10-11-bridge-core-hub.md`. + +## OpenSpec и superpowers +- Вход в любую задачу — superpowers:brainstorming. Skills openspec-propose и openspec-explore сами по себе не запускать. +- Когда brainstorming классифицировал задачу как architectural, структуру change создать через `openspec new change ` (или `/opsx:propose `), а содержимое proposal.md, specs/ и design.md написать по итогам brainstorming. tasks.md из OpenSpec не заполнять — задачи живут в плане superpowers. +- `/opsx:apply` не использовать: исполнение идёт через writing-plans и SDD/executing-plans. +- `openspec archive ` — после merge PR, как часть чеклиста закрытия. +- Хук require-plan защищает любой каталог `src/` на любой глубине. Если код лежит иначе — перечислить каталоги (префиксы от корня, по одному в строке) в `.claude/code-paths`. diff --git a/README.md b/README.md index 24f64dc..fc6b291 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,9 @@ - `hub_token` — агентский токен из `jira-hub tokens` на Mac mini; - `jira.base_url` — адрес Jira банка; - `jira.auth` — `{"type": "basic", "user": ..., "password": ...}` или `{"type": "bearer", "token": ...}`; - - `verify_ssl`, `ca_bundle` — при корпоративном сертификате (см. ограничение 3 ниже). + - `verify_ssl`, `ca_bundle` — при корпоративном сертификате (см. ограничение 3 ниже); + - `timeout` — таймаут запроса в секундах (по умолчанию 60); + - `poll_seconds` — пауза между опросами хаба, когда команд нет (по умолчанию 5). 3. `agent.json` содержит учётные данные Jira. Никуда не копировать и никуда не отправлять, кроме этой виртуалки. Хаб получает от агента только результаты команд и состояние задач, без паролей и токенов Jira. @@ -28,7 +30,14 @@ python3 agent.py check ``` -В норме печатает две строки: `хаб: доступен, настройки v, проект ` и `Jira: доступен, пользователь `. +В норме печатает две строки с префиксом времени `[ЧЧ:ММ:СС]`, например: + +``` +[10:42:17] хаб: доступен, настройки v3, проект AURORA +[10:42:18] Jira: доступен, пользователь ivanov +``` + +(`хаб: доступен, настройки v, проект ` и `Jira: доступен, пользователь `). Код выхода 0. Если хаб или Jira недоступны, строка содержит `НЕДОСТУПЕН` и текст ошибки, код выхода 1. Команда ничего не пишет в Jira. @@ -121,8 +130,8 @@ python3 agent.py prune - **Одна команда в полёте.** Агент берёт следующую команду, только когда предыдущая выполнена и отчёт подтверждён. - **Отчёт до подтверждения.** Отчёт повторяется каждые 10 с, пока хаб не подтвердит. Ответы 404/409/422 хаб выдаёт, когда команда уже отменена или удалена: агент считает её закрытой и не зацикливается. -- **429 и 503.** Агент ждёт `Retry-After` из ответа Jira и повторяет запрос, до 5 попыток. Если заголовка нет, - ждёт 60 с, умноженные на номер попытки. Число ожиданий идёт в `rate_limited` отчёта. +- **429 и 503.** Агент ждёт `Retry-After` из ответа Jira и повторяет запрос: до 5 запросов, то есть не более 4 ожиданий; + на пятой неудаче — ошибка команды. Если заголовка нет или он в формате HTTP-date (не разбирается), ждёт 60 с, умноженные на номер попытки. Число ожиданий идёт в `rate_limited` отчёта. - **Версия.** Константа `AGENT_VERSION` в `agent.py` (сейчас 1) отправляется в каждом `claim`. Если хаб назначил `min_agent_version` выше, он пишет в `agent_status.warning`: «обновите агента»; команды при этом выдаются по-прежнему. @@ -160,5 +169,6 @@ python3 agent.py prune Отсутствующие ключи при `refresh` по `keys` попадают в `result.missing` (не больше 50). 6. **Сравнение описания в `ensure_fields` точное.** Если Jira нормализует переводы строк (CRLF), будут лишние записи. Наблюдать на реальной Jira. -7. **429/503 без потолка.** Агент ждёт `Retry-After` сколько бы он ни был (без верхней границы), до 5 попыток. - Длинный `Retry-After` держит очередь. +7. **429/503 без потолка.** Агент ждёт `Retry-After` в секундах сколько бы он ни был (без верхней границы): не более 4 ожиданий, + на пятой неудаче — ошибка. Длинный `Retry-After` держит очередь. Формат HTTP-date в `Retry-After` не разбирается: + тогда ждётся 60 с × номер попытки. diff --git a/tests/test_py39.py b/tests/test_py39.py index 1763e5f..8235b6c 100644 --- a/tests/test_py39.py +++ b/tests/test_py39.py @@ -4,6 +4,10 @@ import unittest from _common import SRC +# Ограничение проверки: ast.parse(feature_version=(3, 9)) ловит синтаксис 3.10+ (match и т. п.) и сторонние +# импорты; API и конструкции, новые в 3.9 (например, `X | Y` в аннотациях), не проверяет. Их подтверждает только +# прогон на /usr/bin/python3 (3.9.6). + class Py39Test(unittest.TestCase): def test_agent_parses_as_python_39(self):