Andrey Epifancev ff0f2d1077 docs: рулинг R-1
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Typ5sXWNMFirr2va2uL8o
2026-10-11 21:40:07 +04:00

9.9 KiB
Raw Blame History

Context

Две машины в одной локальной сети: Mac mini (192.168.0.106, Python 3.13, uv, Docker не всегда запущен) и виртуалка с macOS и системным Python 3.9.6 (192.168.0.112, входящих портов нет, pip3 есть, доступ к Jira банка https://sdlc.go.rshbank.ru/jira по логину/паролю из config.json). Общая папка гипервизора — запасной канал. Исходный код для переноса: admin/jira-sync/jira_sync.py (клиент REST v2 + Agile, темп Pacer, карта переходов, исправленная семантика связей: в POST /issueLink inwardIssue — источник, в GET outwardIssue у X — цель X), config.example.json (id полей, типы, карта переходов, типы связей, доска 4216).

Goals / Non-Goals

Goals: убрать ручной перенос файлов; один источник ключей; журнал и повторяемость изменений; безопасный повтор любой команды; темп, при котором Jira не отвечает 429; MCP, через который сессия ставит пакеты и читает состояние. Non-goals — см. proposal.md.

Decisions

  1. Два репозитория, один change. Спека и план лежат в jira-hub; jira-agent ссылается на них из CLAUDE.md. Агент меняется редко и живёт на другой машине, поэтому отдельный репозиторий без зависимостей от хаба.
  2. Хаб — один процесс: SQLite + HTTP + MCP. Один писатель в базу, нет зависимости от Docker, база — один файл. Агент и интерфейсы ходят только через HTTP; виртуалке ничего не ставить, у неё нет входящих портов.
  3. Команды — желаемый результат (ensure_*), каждая на одной задаче, выполняется как «прочитать из Jira → если результат уже есть, done без записи → иначе записать». Повтор безопасен по построению.
  4. Хаб не повторяет команды. Взятая команда без ответа видна как «нет ответа от агента с …»; повторяет её только сам агент после перезапуска по своему журналу, и только через проверку. Отменить взятую команду может только человек.
  5. Истина в Jira. После каждого пакета хаб добавляет refresh по затронутым ключам; команда без ответа, результат которой виден в зеркале, помечается confirmed_by_state.
  6. Политика темпа в хабе, пауза на команде. При выдаче команды хаб ставит «не раньше, чем через N с после предыдущей операции в Jira» по типу команды и типу предыдущей. Агент помнит только время последнего вызова Jira. Нет следующей команды — нет паузы. 429/503 — Retry-After в агенте (инструкция сервера, не политика).
  7. Пакет = одно действие пользователя. Команды пакета — по порядку, пакеты — по порядку постановки, одна команда в полёте. Упавшая команда не останавливает пакет; команды, ссылающиеся на её результат, — skipped.
  8. Временные ключи new:<n> живут в хабе: ensure_issue с временным ключом, после done хаб подменяет new:<n> в параметрах следующих команд пакета и в issue_note. У агента соответствия нет.
  9. Зеркало — JQL из настроек (project = AURORA AND labels = AWPCA); задачи, на которые ведут связи, но вне JQL, дочитываются по ключу и помечаются neighbor.
  10. Служебные заметки отдельно от зеркала (issue_note), чтобы обновление из Jira их не затирало.
  11. Настройки агента приходят от хаба (id полей, карта переходов, типы связей, политика темпа, версия настроек); локально у агента — адрес хаба, токен, Jira и учётные данные.

Risks / Trade-offs

  • Чтение перед каждой записью — лишний вызов на команду. Цена мала (пауза перед чтением 2 с), а гарантия идемпотентности стоит дороже.
  • Поиск дубля при ensure_issue по точной теме и автору за 7 дней может не найти задачу, если тему успели поправить в Jira руками. Остаточный риск принят: окно между ответом Jira и записью журнала — миллисекунды.
  • Один агент и одна команда в полёте — медленно для сотни операций (как и сейчас: ~4,5 ч на полную актуализацию). Это цена темпа, не архитектуры.
  • Хаб должен быть запущен для MCP; если нет — инструменты возвращают понятную ошибку, не падают.
  • SQLite по сети не используется; при переезде хаба на другую машину — переносится файл.

Decisions made without discussion

Дата Решение Обоснование Цена ошибки
2026-10-11 Хаб: SQLite в одном процессе с HTTP, без PostgreSQL один писатель, Docker здесь не всегда поднят, виртуалке ничего не ставить; владелец отдал плумбинг исполнителю смена хранилища — замена одного модуля хаба, API не меняется
2026-10-11 HTTP-библиотека хаба — stdlib http.server + ThreadingHTTPServer, без FastAPI API маленький, зависимостей ноль, тесты через urllib; MCP — пакет mcp (FastMCP) через uv при росте API — переезд на FastAPI внутри того же репозитория
2026-10-11 Порт хаба 8790, привязка к 0.0.0.0 свободный порт; виртуалка в LAN с другим адресом, 127.0.0.1 ей недоступен пересечение портов — поправить настройку
2026-10-11 claim — долгий запрос до 30 с, затем сразу следующий минимальная задержка выдачи без постоянного трафика —
2026-10-11 Поиск дубля в ensure_issue: project = AURORA AND reporter = currentUser() AND created >= -7d + точное сравнение темы на клиенте JQL summary ~ нечёткий; точное сравнение в агенте надёжнее дубль при смене темы руками в первые 7 дней; хаб показывает такие команды отдельно
2026-10-11 Исполнитель входит в ensure_fields 11.10 исполнители ставились руками, в просмотрщике это нужная операция; владелец согласился —
2026-10-11 Паузы по умолчанию: изменение задачи 120 с, комментарий и переход 30 с, чтение 2 с, создание связи 30 с значения pacing из config.example.json, проверенные на Jira банка 10–11.10 настройки, правятся без кода
2026-10-11 Резервная копия файла базы перед каждым пакетом в backups/, хранить последние 30 заменяет историю версий зеркала место на диске; число в настройках
2026-10-11 Хранение базы и настроек хаба — ~/.local/share/jira-hub/ вне git, переживает пересоздание worktree —
2026-10-11 Регистрация MCP — в ~/.claude.json для проекта awp-ca (stdio, uv run jira-hub mcp) как остальные MCP этого проекта —
2026-10-11 Полная синхронизация по расписанию: хаб ставит refresh по базовой JQL раз в 60 мин, если агент на связи и очередь пуста свежее зеркало без участия человека лишние чтения; интервал в настройках, 0 отключает
2026-10-11 R-1: Review Focus #5 плана ссылается на Task 7, тест test_settings_put_rejects_protected лежит в Task 6 — тест остаётся в Task 6 опечатка плана; спека о месте теста молчит нулевая