Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Typ5sXWNMFirr2va2uL8o
74 lines
13 KiB
Markdown
74 lines
13 KiB
Markdown
## 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 | опечатка плана; спека о месте теста молчит | нулевая |
|
||
| 2026-10-11 | R-2: T3.9 (транзакции на общем соединении между потоками) — не дефект; в `set_note` правится только вводящий в заблуждение комментарий; в Task 6 проверить, что API открывает соединение на запрос | Architecture плана: каждый запрос открывает своё соединение SQLite (WAL, busy_timeout) | если соединение будут делить потоки — ошибки вложенного BEGIN; чинится локом в db.transaction |
|
||
| 2026-10-11 | R-3: `commands.check_confirmed(conn, cmd, link_types, reporter=None)` — необязательный фильтр по `reporter` для `ensure_issue`; Task 5 берёт значение из настройки `reporter`, если она задана | спека требует «задача с той же темой от нас», а хаб не знает логин агента | без настройки возможно ложное `confirmed_by_state` чужой задачей с той же темой за 7 дней (только при потерянном отчёте) |
|
||
| 2026-10-11 | R-4: временный ключ `new:<n>` должен быть объявлен `ensure_issue` раньше по списку пакета (строже спеки «вне пакета») | иначе зависимая команда с меньшим `seq` блокирует очередь | при реальной потребности в обратном порядке — снять проверку и сортировать по зависимостям |
|
||
| 2026-10-11 | R-5: `db.transaction` становится вложенной (если `conn.in_transaction` — блок входит во внешнюю транзакцию без своих BEGIN/COMMIT) | после исправления T3.1/T3.2 `mirror.rename_note`/`set_note` сами открывают транзакцию, а `queue.report_result` по плану вызывает их внутри своей — вложенный `BEGIN IMMEDIATE` падал бы | вложенный блок не откатывается отдельно от внешнего (savepoint не нужен: ошибка откатывает всю внешнюю транзакцию) |
|
||
| 2026-10-11 | R-6: сбой резервной копии в `queue.claim` не блокирует выдачу команды: исключение ловится, пишется предупреждение в лог | копия защищает базу хаба и не является условием записи в Jira; иначе команда повисает в `claimed` на 10 минут | при сбое диска команда выполнится без свежей копии; строгий вариант — откат claim |
|
||
| 2026-10-11 | R-7 (уточняет R-5): вложенный `db.transaction` использует `SAVEPOINT`; соединение не делится между потоками | внутренний блок, чья ошибка перехвачена вызывающим, не должен фиксировать частичные записи | лишние SAVEPOINT на каждый вложенный вызов — дёшево |
|