- TelegramCfg.Enabled (дефолт true); при false канал не создаётся и не крепится в Router, long-poll не стартует — нет сетевых вызовов к api.telegram.org - Validate требует token/chat_id только при enabled - Load пресетит enabled=true до unmarshal (applyDefaults для bool не различает явный false) - тесты, README и config.yaml.example обновлены
8.5 KiB
8.5 KiB
core
Ratatoskr-go — оркестратор конвейера Ratatoskr (порт с Python на Go) в единый бинарь. Субагенты (analyst/dev/reviewer) запускаются через внешний процесс opencode (v2 HTTP API). Интерфейсы: Telegram-бот + Fyne UI. Linux — статический headless-бинарь (CGO_ENABLED=0); Windows — с Fyne-окном (cgo).
Структура (модули internal/)
cmd/ratatoskr/ точка входа; флаги: -config, -version, -noui (headless). main.version/updateToken вшиваются ldflag'ом
internal/
app/ composition root/DI: App.New -> config.Load+Validate, ResolveExePaths, storage, Runner, Analyst, Core, Worker, Updater, UI. packageOwner="kamelion", Version="0.2.2"
config/ YAML+env загрузка (${VAR:-default}), defaults, validate C1-C4
model/ доменные типы/контракты: Status + validTransitions (IsValidTransition/IsTerminal), TraceStatus. Нижний слой — не зависит от storage/events
chat/ мультиканальный Router; telegram — long-poll канал. Коды M1-M5
core/ state-machine задач + Decider/analyst интерфейс. Коды D3/D4
analyst/ аналитик: промпт + разбор JSON-вердикта (opencode agent). Коды A1-A4
events/ шина событий UI: Bus (buffered Sub/Pub), типизированный Hub, события TaskCreated/TaskStatusChanged/HistoryAppended/TraceAppended/AgentActivity, LogBus (панель «Логи»)
worker/ polling-планировщик + dev/reviewer-конвейер + gitops. Коды W*, E*, R*
agents/ встроенные opencode-агенты (analyst.md, dev.md, reviewer.md) через go:embed
opencode/ HTTP-клиент v2 API opencode serve, поллинг вердикта, LiveRegistry. Коды O1-O5
storage/ SQLite (modernc.org/sqlite, без CGO): tasks, traces, task_history. Коды S1-S5
update/ автообновление из Gitea Packages. Коды U1-U6
ui/ desktop-оболочка: Store-абстракция (копии), Commands (UI->Core), Controller (шины->View), View/Window интерфейсы
ui/desktop/ Fyne-реализация окна (build-tag cgo, --noui / нет cgo → headless)
scripts/ build-publish-ui.ps1 — локальная Windows-сборка Fyne-бинаря + публикация в Gitea
docs/ ui-spec.md — спека Fyne UI (слои, event-bus, fyne.Do/снапшоты)
Ключевые инварианты
- App.New-сигнатура:
App.New(configPath, version, updateToken string, noUI bool)(4-й параметр — headless; cgo-вариант собирается только при наличии C-компилятора). - chat.Router: асинхронная обработка входящих — ограниченный пул
chatWorkers=4воркеров + FIFO-очереди per-user (userState,workerLoop/runUser). Порядок сообщений одного UserID сохраняется (флагscheduled→ один активный воркер на пользователя); разные пользователи обрабатываются параллельно (до 4 одновременных LLM-вызовов). Backpressure поjobsблокирует только перегруженного пользователя, не весь long-poll.Processed()/WaitProcessed()— синхронизация тестов. - FreeChat (app):
sessions map[uid]sessionIDзащищёнsync.Mutex(пишется из разных воркеров роутера). - UI: окно — ещё одна реализация
chat.Channel(присоединяется в Router). Core не трогает UI; обмен — событийная шина (events). Кнопка «Завершить» = полный выход (SetOnQuit→cancel→UI.Run возвращается); закрытие крестиком = сворачивание, Core живёт. UI собирается с--noui/без cgo. - Фазы аналитика (Decision.Phase):
ask,propose,ready(два последних обрабатываются одинаково в core),abort. Требования валидатора: ask — chat_reply/questions; propose — хотя бы одно изменённое поле; ready — без изменённых полей. - Статусы задач (internal/model): draft→collecting→ready→approved→running→success/failed/timeout + cancelled/aborted/closed (терминальные). UserID — chat.ID (одна активная задача на чат).
- Decider/Worker/Analyst/Reviewer: Decider=analyst интерфейс; Worker — polling-планировщик; Reviewer проверяет diff dev-ветки (R1-R6), вердикт JSON {passed, critical_issues, solid_violations, comments}.
- gitops (worker): worktree-режим; feature-ветка
feat/<taskTag>от origin/main; push через http.extraHeader, токен Bearer. - Пути «всё рядом с .exe»: db/worktree резолвятся от ExeDir; config.yaml — рядом с бинарём, фоллбэк cwd.
- Автообновление: авто = только Check+уведомление; замена — по /update; версии в
commit-<sha7>/(неlatest/); Verify сверяет контрольную сумму бинаря против.sha256той же версии. Perf:ResolveLatestпроверяет наличие бинаря версии HEAD-пробой без скачивания тела (405/501 → fallbackGET Range: bytes=0-0), сортировка версий по id убыв.; один общийhttp.Client(keep-alive). Ошибка U4 — только при несовпадении суммы (пустой/отсутствующий.sha256пропускает проверку — M1-известное замечание). - Telegram toggle:
telegram.enabled(дефолт true, preset вLoadдо unmarshal, т.к.applyDefaultsдля bool не различает явный false). Приfalseканал не создаётся/не крепится в Router и long-poll не стартует — сетевых вызовов к api.telegram.org нет; token/chat_id не валидируются.
opencode (v2 HTTP API, >= 1.18.18)
- Интеграция с субагентами — через headless
opencode serve, v2 API (/api/*). Версия opencode >= 1.18.18. - Модель — только глобальный конфиг opencode. Ratatoskr модель не выбирает и про неё не знает: opencode сам берёт модель по умолчанию из своего глобального конфига. Код opencode-конфиг не читает (config.go удалён).
- Свой агент: при создании сессии в
POST /api/sessionпередаётсяagent(analyst/dev/reviewer/chat/postmortem) из встроенных определений (internal/agents, распаковка вOPENCODE_CONFIG_DIR). - Поллинг вердикта:
POST /api/session/:id/prompt(durable admit) →GET /api/session/:id/message?order=desc&limit=200(новые assistant-сообщения, текст вcontent[].type=="text") → завершение =GET /api/session/activeбез сессии + финальное assistant-сообщение, стабильноеsettlePolls=2опроса.POST .../interruptвместо abort.
Контракты (не ломать)
App.New(configPath, version, updateToken string, noUI bool)— сигнатура.packageOwner— константа "kamelion" (не плодить vars/ldflag/конфиг).update.Updater— создаётся структурой&update.Updater{...}, конструктора нет.ui.Store— чтение-модель для UI: ListTasks/GetTask/GetHistory/GetTraces (копии, без ссылок на storage).events.Bus/events.LogBus— шина UI (см. internal/events). publisher-интерфейсы подключаются к Core/Worker.
см. также mem:tech_stack, mem:conventions, mem:task_completion, mem:suggested_commands.