- chat.Router: ограниченный пул chatWorkers=4 воркеров + FIFO-очереди per-user (userState/workerLoop/runUser). Порядок сообщений одного UserID сохраняется; разные пользователи обрабатываются параллельно (до 4 одновременных LLM-вызовов), long-poll Telegram не блокируется чужим аналитиком. Backpressure по jobs — только на перегруженного пользователя. - app.FreeChat: sessions под sync.Mutex (защита от data race при параллельных воркерах роутера). - update: ResolveLatest проверяет наличие бинаря HEAD-пробой без скачивания тела (fallback GET Range 0-0 при 405/501), сортировка версий по id убыв.; один общий http.Client (keep-alive) вместо нового на каждый запрос. - тесты: порядок/параллелизм per-user в router, HEAD-без-тела и фоллбэк на версию без бинаря в update. - память Serena: инварианты Router/update, примечания по форматированию на Windows.
54 lines
4.3 KiB
Markdown
54 lines
4.3 KiB
Markdown
# conventions
|
||
|
||
## Стиль / кодстайл
|
||
- Стандартный Go-стиль; `gofmt`/`go fmt ./...`. Документация-комментарии и
|
||
package-doc на русском языке.
|
||
- Типизация: строгие типы, интерфейсы для абстракций (Decider, ui.Store, events.Event, chat.Channel).
|
||
- Свой тип `config.Duration` для времени (YAML-строки "5s"/"10m"), метод `.Duration()`.
|
||
- UI: uni-directional data flow — UI читает снапшоты (копии, `ui.Store`), не мутирует storage/core.
|
||
Ядро публикует события в `events.Bus`, UI подписывается `events.Hub` → колбэки в UI-поток через `fyne.Do`.
|
||
|
||
## Обработка ошибок
|
||
- Ошибки классифицируются по идентификаторам классов в исходниках (см. ниже).
|
||
- Ошибка-обёртка: `fmt.Errorf("%w: %v", ErrXxx, err)`.
|
||
- `errors.Join` для склейки нескольких ошибок валидации (config.Validate).
|
||
|
||
## Классы ошибок (маркируются в коде, документированы в README)
|
||
| Блок | Коды | Где |
|
||
|---|---|---|
|
||
| C | C1–C4 | internal/config |
|
||
| A | A1–A4 | internal/analyst; A (app): A1–A4 в internal/app |
|
||
| M | M1–M5 | internal/chat |
|
||
| D | D1–D5 | internal/core (ErrDecideFailed, ErrMaxTurns, ErrCommandUnknown…) |
|
||
| O | O1–O5 | internal/opencode |
|
||
| S | S1–S5 | internal/storage |
|
||
| W | W1–W5 | internal/worker |
|
||
| E | E1–E4 | internal/worker (репозитории) |
|
||
| R | R1–R6 | internal/worker/review_errors.go |
|
||
| U | U1–U6 | internal/update |
|
||
|
||
## Архитектурные конвенции
|
||
- **Composition root** — internal/app; подсистемы собираются там. UI-окно создаётся через
|
||
`newUIWindow(store)` (build-tag: cgo → desktop.New, !cgo → nil/headless). Флаг `--noui` выключает окно.
|
||
- **Слои:** model (домен) ← storage/events/UI зависят только от model; core не знает про UI.
|
||
- **Агенты** (analyst.md/dev.md/reviewer.md) — markdown-промпты, go:embed, распаковка в config_dir.
|
||
Вердикт — строгий JSON.
|
||
- Репо-клонирование/воркеры — через gitops; ветки задач `feat/<taskTag>` (см. mem:core).
|
||
- Обратная совместимость: поле `Repo` (одиночный) и `Repos` (список); EffectiveRepos/
|
||
SetReposFromDB/ReposJoined в storage/models.go.
|
||
|
||
## Версии
|
||
- `app.Version` — семантическая major.minor.patch (ручной инкремент: patch=фиксы,
|
||
minor=новая обратно-совместимая функциональность, major=несовместимые изменения). Сейчас 0.2.2.
|
||
- `main.version` (ldflag) — build-идентификатор `commit-<sha7>`, отдельно от app.Version.
|
||
|
||
## Форматирование (важно на Windows)
|
||
- Репо на Windows-чекауте: `core.autocrlf=true` → файлы в рабочей копии с CRLF; `gofmt -l`
|
||
на CRLF-копии шумит (глобально ~60 файлов). **Проверять формат только на LF-версии** (напр.
|
||
`git -c core.autocrlf=false clone` во временный каталог) — так дифы видны корректно.
|
||
- gofmt 1.26 форматирует doc-comments (`//` перед `go build ...` в `// go build` → пустая строка
|
||
`//`) и выравнивание структур; не все файлы отформатированы по новой версии (предсуществующе).
|
||
- CI (`go vet` + `go test`) формат не проверяет → gofmt-дифы не ломают сборку.
|
||
- **Known race в тест-харнессе app:** `e2eChannel.Send` (`internal/app/e2e_test.go:213`) пишет
|
||
`c.sent` без лока, тест читает с главной горутины → `-race` ловит в `TestE2ENotificationsOnTransitions`.
|
||
Путь `worker→Notify→Send`; предсуществует, ещё не чинили (fix — мьютекс в `e2eChannel`). |