- client.go: эндпоинты /api/* (create+model, prompt-admit, message, active, interrupt) - runner.go: неблокирующий prompt + поллинг новых assistant-сообщений; завершение = сессия ушла из активных дренажей + стабильное финальное сообщение - config.go: чтение top-level model из opencode.jsonc (JSONC-стрип) + хардпин в сессию - server.go: healthcheck /api/health, MinVersion=1.18.18, понятная ошибка для старого бинаря - класс O5 WARN: устойчивость к v1-конфигу провайдера (npm/options игнорируются v2) - README: раздел интеграции, минимальная версия opencode, предупреждения - .serena: актуализация памяти (core, tech_stack)
70 lines
5.9 KiB
Markdown
70 lines
5.9 KiB
Markdown
# core
|
||
|
||
Ratatoskr-go — оркестратор конвейера Ratatoskr (порт с Python на Go) в единый
|
||
статический бинарь (CGO_ENABLED=0). Субагенты запускаются через внешний процесс
|
||
[opencode](https://opencode.ai). Взаимодействие — Telegram-бот.
|
||
|
||
## Структура (модули internal/)
|
||
|
||
```
|
||
cmd/ratatoskr/ точка входа, сборка бинаря; main.version и main.updateToken вшиваются ldflag'ом
|
||
internal/
|
||
app/ composition root/DI: App.New -> config.Load+Validate, ResolveExePaths, storage, Runner, Analyst, Core, Worker, Updater. packageOwner="kamelion", Version="0.1.0"
|
||
config/ YAML+env загрузка (${VAR:-default}), defaults, validate C1-C4
|
||
chat/ мультиканальный Router; telegram — long-poll канал. Коды M1-M5
|
||
core/ state-machine задач + Decider/analyst интерфейс. Коды D3/D4
|
||
analyst/ аналитик: промпт + разбор JSON-вердикта (opencode agent). Коды A1-A4
|
||
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
|
||
```
|
||
|
||
## Ключевые инварианты
|
||
|
||
- **Команды/ядро:** Core.ProcessTurn — state-machine поверх storage. Команды:
|
||
/start /cancel /skip /retry N /status N /continue N. Активная задача — одна на чат.
|
||
- **Фазы аналитика (Decision.Phase):** `ask` (уточняющие вопросы), `propose` (правки
|
||
черновика), `ready` (черновик полон как есть), `abort` (тема вне проекта). propose и ready
|
||
в core обрабатываются одинаково.
|
||
- **Статусы задач:** draft→collecting→ready→approved→running→success/failed/timeout,
|
||
плюс cancelled/aborted/closed. approved — финальное одобрение («создавай»), после чего
|
||
воркер берёт задачу. IsValidTransition/IsTerminal в storage/models.go.
|
||
- **Decider/Worker/Analyst/Reviewer:** Decider=analyst интерфейс (analyst пакет реализует);
|
||
Worker — polling-планировщик dev-агента; Reviewer проверяет diff dev-ветки (R1-R6),
|
||
вердикт JSON {passed, critical_issues, solid_violations, comments}.
|
||
- **gitops (worker):** worker работает с worktrees; feature-ветка = `feat/<taskTag>` от
|
||
origin/main; ensureBranch (reset --hard + clean -fd + checkout -B), branchDiff
|
||
(`origin/main...<branch>`), push через http.extraHeader, токен как Bearer.
|
||
- **Пути «всё рядом с .exe»:** относительные db/worktree резолвятся от каталога бинаря
|
||
(ExeDir), не от cwd. config.yaml ищутся рядом с бинарём, фоллбэк cwd.
|
||
- **Автообновление:** авто = только Check+уведомление; замена — по /update. Версии в
|
||
Gitea Packages `commit-<sha7>/` (не `latest/`). Вердикты агентов — строгий JSON.
|
||
|
||
## opencode (v2 HTTP API, >= 1.18.18)
|
||
|
||
- Интеграция с субагентами — через headless `opencode serve`, **v2 API** (префикс `/api/*`).
|
||
Минимальная версия opencode **>= 1.18.18** (старый бинарь — только `/global/health`, не годится;
|
||
healthcheck падает с понятной ошибкой, класс O1).
|
||
- **Хардпин модели:** при создании сессии читается top-level `"model"` из конфига opencode
|
||
(`internal/opencode/config.go`, JSONC-стрип `//`/`/* */`/trailing-запятых) и передаётся в
|
||
`POST /api/session` как `{"model":{providerID,id}}` (разбор `provider/id` по первому `/`).
|
||
- **Класс O5 WARN (устойчивость к v1-конфигу):** конфиг по старой v1-схеме
|
||
(`provider.X.npm`/`options`) молча игнорируется v2 → провайдер без api → модель unsupported →
|
||
fallback. Раtatoskr не чинит это сам, но логирует warning: конфиг не читается/нет `model`,
|
||
и/или фактическая модель ответа (из assistant-сообщения) ≠ ожидаемой. Правильный v2-вид:
|
||
`api:{type:"aisdk",package,url}` и `request.headers` вместо `options.headers`.
|
||
- **Поллинг вердикта:** `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.
|
||
- `ModelRef{ProviderID,ID,Variant}` — аналог v2 Model.Ref; `MinVersion="1.18.18"` в server.go.
|
||
|
||
## Контракты (не ломать)
|
||
|
||
- `App.New(configPath, version, updateToken string)` — сигнатура.
|
||
- `packageOwner` — константа "kamelion" (не плодить vars/ldflag/конфиг).
|
||
- `update.Updater` — создаётся структурой `&update.Updater{...}`, конструктора нет.
|
||
|
||
См. также `mem:tech_stack`, `mem:conventions`, `mem:task_completion`, `mem:suggested_commands`. |