feat: реле общей папки для агента за VPN (#5) #6

Merged
aep merged 7 commits from feat/5-folder-transport into main 2026-10-13 09:07:34 +03:00
Owner

Closes #5

Что сделано

Реле общей папки в хабе (OpenSpec change folder-transport). При включённом VPN «Континент ZTN» на виртуалке сеть до Mac mini закрыта, поэтому агент обменивается с хабом файлами через общую папку UTM (VirtioFS).

  • Настройка relay_dir. Принимает абсолютный путь или null, иначе отвечает 422. Агенту не передаётся.
  • src/jira_hub/relay.py. Поток внутри jira-hub serve:
    • забирает req/<id>.json переименованием в work/;
    • пересылает запрос в свой HTTP (на тот же адрес, на котором запущен serve; при 0.0.0.0 — на 127.0.0.1);
    • атомарно пишет ответ в resp/<id>.json;
    • просроченные запросы выбрасывает; claim пересылает, только если до срока осталось не меньше 35 с;
    • при сбое соединения со своим HTTP отвечает 502;
    • раз в минуту убирает старые файлы;
    • на каждый запрос пишет в лог строку relay <METHOD> <path> <status> <с>;
    • сбои папки переживает сам: при отказе доступа или удалённых подкаталогах пишет одно предупреждение, а не поток трассировок.
  • api.start(). Поднимает HTTP, тикер и реле; serve() вызывает его.
  • Документация. README (раздел «Транспорт через общую папку», включая доступ launchd к внешнему диску), ARCHITECTURE.md, CLAUDE.md.

HTTP API, обработчики и токены не менялись. Агент: aep/jira-agent PR из ветки feat/5-folder-transport.

Проверка

  • uv run pytest -q — 159 passed. Из них 20 тестов реле, включая сквозной через api.start() и сбои папки.
  • Ревьюер (opus) отдельно прогнал настоящий хаб с реле (Python 3.13) против настоящего HubClient агента с folder: (Python 3.9.6). Результат: settings 200, claim 200, result(999) → 404, чужой токен → 401.
  • Приёмка, шаг 1: мок Jira через временную папку с пробелом в пути, хаб на конкретном адресе 127.0.0.1:8791.
    • check через папку проходит.
    • refresh full — 138 задач.
    • import совпадает с прогоном по HTTP: 139 заметок, 25 связей, missing 1 и 4.
    • Пакет из 12 команд — 13 записей в Jira, повтор — 0.
    • Реле выключено → агент пишет «хаб не ответил»; реле включено → команда выполнена один раз.
    • SIGINT в паузе → после перезапуска команда выполнена один раз.

Rulings I made

  • R-1 (design.md). В ARCHITECTURE.md граница «HTTP живёт только в api и client» дополнена relay: реле само ходит в HTTP. Цена ошибки: нет.
  • R-2 (design.md). В tests/test_py39.py агента в белый список stdlib добавлен uuid, его требует спека (uuid4().hex). Цена ошибки: нет.
  • R-3. Пункты ревьюера из «Declined to judge» оставлены как есть: они вне спеки или приняты в design.md. Это несколько агентов на одной папке, права файлов с токеном, общий случай M-2 из bridge-core, лавина потоков, симлинки, IPv6, частота опроса, ожидание полного таймаута без реле, hub_url не строкой, двойные строки лога. Цена ошибки: нет.

Находки

Маркер Статус Что
I-1 fixed 4a82be0 поток трассировок раз в 0,1 с при отказе доступа или удалённом req/; неверная подсказка в README
I-2 fixed 4a82be0 удалён work/ — реле молча перестаёт работать; удалён resp/ — ответ теряется
M-1 deferred тест не закрепляет --host <конкретный IP> (код верен, живой прогон на 127.0.0.1:8791 прошёл)
M-3 deferred http.client.HTTPException от своего HTTP не превращается в 502
M-4 deferred work/ чистится при каждой повторной подготовке папки, а не только при старте; лог «перезапуск реле» вводит в заблуждение
O-2 deferred после перезапуска агент выполняет выданную до остановки команду, не выдерживая паузу темпа (поведение восстановления из bridge-core)

Дальше (приёмка, шаги 2–3, после merge)

  1. Перезапустить launchd-хаб и задать relay_dir = /Volumes/externalSSD/utm/rshb/jira-bridge. Проверить доступ launchd к SSD.
  2. На виртуалке при включённом Континенте:
    • check;
    • запуск агента, refresh full;
    • jira-hub import, сверка с CSV.

🤖 Generated with Claude Code

Closes #5 ## Что сделано Реле общей папки в хабе (OpenSpec change `folder-transport`). При включённом VPN «Континент ZTN» на виртуалке сеть до Mac mini закрыта, поэтому агент обменивается с хабом файлами через общую папку UTM (VirtioFS). - **Настройка `relay_dir`.** Принимает абсолютный путь или `null`, иначе отвечает 422. Агенту не передаётся. - **`src/jira_hub/relay.py`.** Поток внутри `jira-hub serve`: - забирает `req/<id>.json` переименованием в `work/`; - пересылает запрос в свой HTTP (на тот же адрес, на котором запущен `serve`; при `0.0.0.0` — на `127.0.0.1`); - атомарно пишет ответ в `resp/<id>.json`; - просроченные запросы выбрасывает; `claim` пересылает, только если до срока осталось не меньше 35 с; - при сбое соединения со своим HTTP отвечает 502; - раз в минуту убирает старые файлы; - на каждый запрос пишет в лог строку `relay <METHOD> <path> <status> <с>`; - сбои папки переживает сам: при отказе доступа или удалённых подкаталогах пишет одно предупреждение, а не поток трассировок. - **`api.start()`.** Поднимает HTTP, тикер и реле; `serve()` вызывает его. - **Документация.** README (раздел «Транспорт через общую папку», включая доступ launchd к внешнему диску), ARCHITECTURE.md, CLAUDE.md. HTTP API, обработчики и токены не менялись. Агент: aep/jira-agent PR из ветки `feat/5-folder-transport`. ## Проверка - `uv run pytest -q` — 159 passed. Из них 20 тестов реле, включая сквозной через `api.start()` и сбои папки. - Ревьюер (opus) отдельно прогнал настоящий хаб с реле (Python 3.13) против настоящего `HubClient` агента с `folder:` (Python 3.9.6). Результат: `settings` 200, `claim` 200, `result(999)` → 404, чужой токен → 401. - **Приёмка, шаг 1:** мок Jira через временную папку с пробелом в пути, хаб на конкретном адресе `127.0.0.1:8791`. - `check` через папку проходит. - `refresh full` — 138 задач. - `import` совпадает с прогоном по HTTP: 139 заметок, 25 связей, missing 1 и 4. - Пакет из 12 команд — 13 записей в Jira, повтор — 0. - Реле выключено → агент пишет «хаб не ответил»; реле включено → команда выполнена один раз. - SIGINT в паузе → после перезапуска команда выполнена один раз. ## Rulings I made - **R-1 (design.md).** В ARCHITECTURE.md граница «HTTP живёт только в `api` и `client`» дополнена `relay`: реле само ходит в HTTP. Цена ошибки: нет. - **R-2 (design.md).** В `tests/test_py39.py` агента в белый список stdlib добавлен `uuid`, его требует спека (`uuid4().hex`). Цена ошибки: нет. - **R-3.** Пункты ревьюера из «Declined to judge» оставлены как есть: они вне спеки или приняты в design.md. Это несколько агентов на одной папке, права файлов с токеном, общий случай M-2 из bridge-core, лавина потоков, симлинки, IPv6, частота опроса, ожидание полного таймаута без реле, `hub_url` не строкой, двойные строки лога. Цена ошибки: нет. ## Находки | Маркер | Статус | Что | |---|---|---| | I-1 | fixed 4a82be0 | поток трассировок раз в 0,1 с при отказе доступа или удалённом `req/`; неверная подсказка в README | | I-2 | fixed 4a82be0 | удалён `work/` — реле молча перестаёт работать; удалён `resp/` — ответ теряется | | M-1 | deferred | тест не закрепляет `--host <конкретный IP>` (код верен, живой прогон на `127.0.0.1:8791` прошёл) | | M-3 | deferred | `http.client.HTTPException` от своего HTTP не превращается в 502 | | M-4 | deferred | `work/` чистится при каждой повторной подготовке папки, а не только при старте; лог «перезапуск реле» вводит в заблуждение | | O-2 | deferred | после перезапуска агент выполняет выданную до остановки команду, не выдерживая паузу темпа (поведение восстановления из bridge-core) | ## Дальше (приёмка, шаги 2–3, после merge) 1. Перезапустить launchd-хаб и задать `relay_dir = /Volumes/externalSSD/utm/rshb/jira-bridge`. Проверить доступ launchd к SSD. 2. На виртуалке при включённом Континенте: - `check`; - запуск агента, `refresh full`; - `jira-hub import`, сверка с CSV. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
aep added 7 commits 2026-10-13 00:09:15 +03:00
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Отказ доступа (TCC), удалённые req/ или work/ больше не сыплют трассировки каждые 0,1 с и не останавливают
реле молча: одно предупреждение на причину, подкаталоги создаются заново; resp/ создаётся до пересылки,
чтобы выданный claim не уходил в никуда.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
aep merged commit b7acd08c69 into main 2026-10-13 09:07:34 +03:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: aep/sdlc-hub#6
No description provided.