Мост YouGile → агент
Упомяните агента в чате задачи, и он начнёт работать в вашем репозитории
Приёмник вебхуков, превращающий обсуждение в трекере в сессию разработки. Упоминание агента в задаче YouGile поднимает Claude Code в заданном рабочем каталоге, причём чат каждой задачи отображается один в один на постоянную сессию, поэтому агент помнит нить, когда его упоминают снова.
YouGile ──POST──▶ https://your.domain/yougile/webhook │ ▼ FRP / cloudflared / ngrok / nginx — your choice 127.0.0.1:9100 (this service) │ ▼ first matching rule in rules.toml claude --dangerously-skip-permissions cwd = rule.workdir --session-id <chatId> (first time on this chat) --resume <chatId> (subsequent)Полный путь от упоминания в чате до работающего агента, как он описан в README проекта.
Идея
Расстояние между «надо бы это починить» в трекере задач и сессией разработки — это в основном копирование: текст задачи, репозиторий, контекст уже сказанного. Мост его закрывает. Упомяните агента в чате задачи YouGile — и он поднимет Claude Code в том рабочем каталоге, на который указывает правило этой задачи.
Пригодным к работе, а не демонстрацией, это делает отображение сессий. Чат каждой задачи получает идентификатор сессии Claude, выведенный из идентификатора чата, поэтому первое упоминание сессию начинает, а каждое следующее её продолжает. Разговор в трекере и разговор с агентом — это один и тот же разговор.
Маршрутизация — это конфигурация, а не код
Какой репозиторий ведёт задача, решает первое подошедшее правило в rules.toml,
рядом с шаблоном промпта для этого триггера. Добавить в схему новый проект —
изменение конфига; никакой логики диспетчеризации править не нужно.
Большая часть работы — обработка отказов
Приёмник живёт на небольшом хосте за тем туннелем, который оказался под рукой, а значит, сеть действительно ненадёжна — и вебхук, о смерти которого вы не узнали, хуже, чем его отсутствие.
Поведение выше — результат. Отдельно стоит принудительный IPv4: сломанный или
полуоткрытый маршрут IPv6 выдавал SSL handshake timed out на хосте, который в
остальном был совершенно здоров, тогда как curl подключался мгновенно. У
urllib нет Happy Eyeballs, поэтому он сидел на мёртвом AAAA-адресе до таймаута.
Отбрасывание AAAA-ответов — с запасным вариантом для хостов, публикующих только
их, — заставило целый класс отказов исчезнуть.
Второе — /healthz. Он намеренно никогда не ходит в API YouGile, потому что
проверка здоровья, зависящая от того, о чём она должна докладывать, перестаёт
отвечать ровно тогда, когда ответ нужен. Он возвращает разобранный набор правил,
последний известный снимок подписки от фонового наблюдателя и то, как давно
тикал цикл событий.
- Forced IPv4
- Полуоткрытый маршрут IPv6 подвешивает urllib до таймаута — Happy Eyeballs там нет. Отбрасывание AAAA-записей убирает целый класс отказов.
- Capped exponential backoff
- Каждый вызов API и каждая загрузка вложения повторяются при сетевых ошибках, таймаутах и 429/5xx.
- Permanent vs transient
- 4xx не повторяется никогда — повтор отказа просто прячет его.
- Self-healing subscription
- Наблюдатель оживляет отключённую на той стороне подписку и повторяет неудавшийся цикл через 60 секунд, а не ждёт следующего прохода.
- Event-loop watchdog
- Если цикл asyncio заклинило, процесс перезапускается, а не остаётся молча «живым».
- Non-blocking /healthz
- Возвращает разобранный набор правил и возраст удара сердца цикла, не обращаясь к YouGile, — поэтому остаётся правдивым, когда сети нет.
Каждый из этих пунктов появился из конкретного сбоя, увиденного на практике.