# bridge-core (агент) — решения Спека изменения лежит в репозитории jira-hub: `jira-hub/openspec/changes/bridge-core/` (specs/agent, specs/commands, specs/hub-api, design.md). Здесь — только решения, принятые при реализации агента без обсуждения с человеком. ## Decisions made without discussion | # | Дата | Решение | Обоснование | Цена ошибки | |---|---|---|---|---| | 1 | 2026-10-11 | `tests/__init__.py` добавляет каталог `tests/` в `sys.path` (R-1) | Тесты из плана импортируют `_common`/`fake_jira` по простому имени, при `discover -s tests -t .` иначе ImportError | Мелкая: переписать импорты в тестах на относительные | | 2 | 2026-10-11 | Потолок на `Retry-After` не вводим (R-2) | Спека: «ждать `Retry-After`»; потолок тихо нарушает требование сервера | Агент может уснуть надолго при аномальном заголовке; лечится потолком позже | | 3 | 2026-10-11 | Журнал: не лечим права существующего файла и не блокируем prune/append flock'ом; запрет `prune` при работающем агенте — в README (R-3) | Журнал создаётся этим же кодом (0600); `prune` — ручная команда раз в месяц | Редкая потеря записи журнала при гонке prune/run; лечится flock | | 4 | 2026-10-11 | Minor-замечания ревью Task 3 (T3.1, T3.3–T3.6, T3.8: пагинация поиска дубля, флаги existed/created, режим epic_link, префикс маркера, общий except в run, тесты) чиним одной финальной волной, а не по месту (R-4) | Не блокируют Task 4–5; одна волна дешевле | Task 4–5 унаследуют общий `run`; ошибки будут чиниться после | | 5 | 2026-10-11 | Неизвестный `link_type` берётся как сырое имя типа Jira (fallback), без `failed` (R-5) | Так же делает хаб в `check_confirmed`; допускает ручной ввод имён Jira | Опечатка в типе даст HTTP-ошибку Jira вместо понятной ошибки агента | | 6 | 2026-10-11 | Спринт в `refresh` берётся из Agile API по активным/будущим спринтам доски, а не из поля `search`; закрытые спринты дают `sprint = null` (R-6) | В настройках хаба нет id поля спринта (`field_ids` его не содержит); план Task 5 выбрал Agile | Исторические имена спринтов затрутся при полном refresh; лечится полем спринта в настройках | | 7 | 2026-10-11 | Бисекция чанка `key in (...)` при 400 принята без лимита вызовов (до ~2·m·log2(n)); если не найден ни один ключ — ошибка, а не `missing` (R-7) | Иначе refresh с удалённой задачей падает целиком; лимит вызовов усложняет без выгоды при редких удалениях | При массовом удалении задач — всплеск запросов и риск 429 (агент их переживает) | | 8 | 2026-10-11 | Общий `except Exception` стоит в `Runner.execute` (не в `Executor.run`); minor-находки T3.x/T5.x/T6.x чиним одной финальной волной, включая ложный `failed` для `ensure_issue` при ConnectionError из Jira (R-8) | Исключение вне HttpError/ConnectionError не должно ронять цикл и вызывать зацикленное восстановление; одна волна дешевле | Ложный `failed` у `ensure_issue` оставит временный ключ неразрешённым, а задача в Jira уже создана | | 9 | 2026-10-12 | В журнал пишется запись `claimed` сразу после `claim`, до паузы; `unfinished_started`, `recover`, `prune` считают её открывающей; `started` по-прежнему после паузы (R-9) | Иначе остановка агента в паузе/claim оставляет команду `claimed` в хабе навсегда (хаб не переиздаёт), а лечение cancel+retry не восстанавливает зависимые команды пакета | Формат журнала шире, чем в спеке (лишний `kind`); старые журналы совместимы | | 10 | 2026-10-12 | `Runner.execute` повторяет команду до 3 раз через проверку при ConnectionError/HTTP 5xx, не записывая `finished`; затем `failed` «результат неизвестен, повтор безопасен» (R-10) | Неоднозначный исход записи в Jira (обрыв после POST) нельзя отдавать как `failed`: для `ensure_issue` хаб пропустит зависимые команды; все команды идемпотентны | Лишние задержки при недоступной Jira | | 11 | 2026-10-12 | Запрошенное, но не настроенное поле (epic_key, planned_start/end) даёт `failed`, а не молчаливый `done` (R-11) | Ложный успех хаб запишет в зеркало как выполненный | Команды падают, пока хаб не настроен | | 12 | 2026-10-12 | `agent_version` добавляется в отчёт и в `/agent/state` (R-12) | Спека: версия «в каждом отчёте»; хаб лишние поля игнорирует | Нет | | 13 | 2026-10-12 | F-1 (повтор по R-10 не ждёт 30 с, если Jira недоступна с первого вызова и `last_call_at` старый) сначала отложен контроллером (вторая волна фиксов не предусмотрена), затем исправлен по команде владельца: ожидание перед повтором — фиксированное `max(pause, 30)` от момента отказа (`Runner.wait_seconds`), не от последнего успешного вызова Jira | Обрыв соединения не обновляет `last_call_at`, поэтому пауза «от последнего вызова» при старом значении была нулевой и три повтора шли подряд | Повтор ждёт полные 30 с даже если с последнего вызова Jira прошло больше; лишнее ожидание не дольше 30 с на попытку |