[2026-09-29] eagle: personal/plans/balda-callee-prism-plan.md

This commit is contained in:
Alexey Martemyanov committed 2026-09-29 01:43:26 +06:00
1 parent 1c2d734ace
commit 0f67b3791b
1 file changed
+226 -42
+226 -42
View File
@@ -116,6 +116,167 @@ Kraken разгружается: уходят Балда, MCP-сервер, obsi
---
## 1.1a Формула прав
**`права = права процесса × смонтированное × разрешённое в графе`**
| Уровень | Кто задаёт | Пример |
|---|---|---|
| Чем запущен процесс | вы, при старте | контейнер / хост-юзер / отдельный системный юзер |
| Что смонтировано | compose | `~/agents:/agents` — и ничего больше |
| Что разрешено в графе | автор воркфлоу | какие роли и скрипты вообще есть в каталоге |
🔴 **`permissions.mode: allow_all` у роли — это про права модели ВНУТРИ ACP-сессии**
(править файлы, запускать команды). Оно **не** расширяет, что процесс видит в файловой
системе. Это два разных ограничения, не путать.
### Где callee — там и движки, и Script'ы
🔴 **ACP-провайдер — дочерний процесс callee.** Движок на хосте + callee в контейнере
технически невозможны: это один процесс-тред.
| Исполнитель | Что исполняет | Права |
|---|---|---|
| callee | сам граф: циклы, роли, state | права процесса, которыми запущен |
| движок (codex/claude/dsh) | модель: думает, правит файлы | наследует + своя песочница |
| Script | shell: сборки, тесты, ssh | наследует права процесса |
**Следствие:** «callee на хосте» = **всё исполнение на хосте целиком**. Промежуточного
варианта нет.
### Динамических маунтов не существует
На macOS Docker маунты задаются **при создании контейнера**; live `mount --bind` контейнер
не видит; симлинк на внешний путь битый. **Вопрос «как модель запросит доступ к папке»
не имеет решения через маунты.**
| Подход | Как | Оценка |
|---|---|---|
| A. Смонтировать всё заранее | `~/Developer:ro` | запись нельзя |
| B. Пересоздание контейнера | правка compose + `up -d` (~3 с) | нужен механизм вне контейнера |
| C. Хост-агент ставит права сам | callee на хосте | полный доступ, ноль изоляции |
| **D. Проект = рабочая копия** ✅ | модель работает в `~/agents/<проект>`, результат уходит в `~/Developer` через git | доступ задаётся наличием репозитория, а не маунтом |
**Решение: D.** Модель всегда работает в `~/agents/<проект>` (уже смонтировано), результат
попадает в проект через `git`. Право выдаётся точечно.
### Доступ не запрашивается — он выбирается маршрутом
Модель **не просит** прав: она не может эскалировать. Если у роли нет доступа, задача
**падает**, а не спрашивает. Доступ задаёт автор графа.
```
Роутер в графе
├─ задача требует ~/agents → роль в контейнере
├─ задача требует Xcode/lume → роль на хост-исполнителе
└─ задача требует хоста по ssh → роль со ssh-ключом (command=)
```
**Разграничение = статический роутинг по типам задач**, а не динамические права.
### Гибрид «контейнер + узкий хост-исполнитель»
| Вариант | Права | Динамика | Оценка |
|---|---|---|---|
| callee в контейнере | узкие, контролируемые | только `~/agents` | безопасно; Xcode/lume/docker недоступны |
| callee на хосте | **полный доступ пользователя** | всё работает | ноль изоляции |
| **гибрид** ✅ | контейнер для основного + узкий хост-исполнитель для Xcode/lume/docker | маунты не нужны | рекомендован |
🟡 Хост-исполнитель — дыра в изоляции **по определению**, но **узкая и явная**: три операции
(xcodebuild, lume, docker build), а не «что угодно».
---
## 1.1b Роли: жизненный цикл и контроль
### Что такое роль
**Роль — YAML/MD-файл в каталоге.** Не «право», не «токен», а **описание листа графа**:
какой провайдер, какой промпт, какие параметры, какой таймаут.
```yaml
apiVersion: callee.metalagman.dev/v1alpha1
kind: Role
spec:
description: Собирает Xcode-проект
provider:
type: generic_acp
cmd: dsh-acp
model: deepseek-chat
timeout: 20m
---
Собери проект {{ .Params.project }} и верни отчёт об ошибках.
```
### Кто их раздаёт и откуда они берутся
| Источник | Что даёт |
|---|---|
| Каталог по умолчанию | 6 стартовых ресурсов (`callee setup`) |
| Импорт из git | `callee agent import baldaworks/prism-callee --prefix prism` |
| **Наши собственные** | файлы в рабочем каталоге проекта / `~/.callee/` |
**Роли не выдаются динамически — они ЛЕЖАТ в каталоге.** Кто положил файл, тот и «выдал»
роль. Никакого рантайм-механизма выдачи прав нет.
### Что раздаётся, а что нет
| Сущность | Динамически выдаётся? | Чем регулируется |
|---|---|---|
| Роль (файл графа) | ❌ статично | наличие файла в каталоге |
| Провайдер (тип, `cmd`) | ❌ статично, или из `State` через DynamicRole | автор графа |
| **Файлы** | ❌ | монтирование + рабочая копия (§1.1a) |
| **SSH-ключ** | ❌ | наличие файла у процесса |
| **Разрешение модели внутри сессии** | ⚠️ **да** — `allow_all`/`ask`/`deny_all` | конфиг Балды |
🔴 **Единственное, что реально запрашивается в рантайме — разрешение модели внутри ACP-сессии**
(`ask` → вопрос в чат). Всё остальное статично.
### Ваша гипотеза: проверяю по пунктам
> «я ставлю задачу → приходит запрос "дать такую-то роль для такой-то задачи" → апрув → доступ появился»
| Часть гипотезы | Вердикт |
|---|---|
| Задача приходит в систему | ✅ да |
| **Приходит запрос «выдать роль»** | ❌ **нет такого механизма.** Роли не выдаются, они лежат в каталоге |
| **Апрув → у агента появляется доступ** | ⚠️ **частично — не к инструментарию, а к действию** |
| **Без апрува доступа нет** | ✅ **верно, но иначе** |
**Что происходит на самом деле:** доступ у роли **есть всегда** — он определяется тем, чем
запущен callee и что смонтировано. Апрув не выдаёт доступ, апрув **разрешает выполнить
операцию**, которая уже технически возможна.
### Значит правильная формулировка
```
Роль лежит в каталоге (доступ есть по определению)
│
▼
Граф доходит до узла → Human («собрать Xcode в VM macos-27?») → пауза
│
├─ апрув → Script исполняется, потому что права у процесса УЖЕ были
└─ отказ → граф останавливается, ничего не исполняется
```
🔴 **Разница принципиальная.** Вы описали **capability-модель** (права выдаются на задачу).
Callee работает по **статической модели** (права постоянны, гейты контролируют исполнение).
**Что это значит для вашей безопасности:** гейт `Human` защищает от **действия**, но не от
**возможности**. Если у процесса есть ssh-ключ — ключ есть всегда, вне зависимости от апрувов.
Изоляцию даёт **только то, чем процесс запущен и что смонтировано** — не граф.
### Что можно сделать, чтобы приблизиться к вашей модели
| Способ | Как |
|---|---|
| **Разные профили запуска** | контейнер «узкий» (без ssh, без docker) и хост-исполнитель «широкий» — выбор профиля роутером |
| **Роль на задачу** | задача → своя роль с нужным `cmd`/таймаутом; доступ = какой профиль она выбрала |
| **Апрув как гейт исполнения** | `Human` перед дорогой/опасной операцией |
| **Белый список команд в Script** | `Script` не «любой shell», а конкретный скрипт с фиксированной командой |
---
## 1.2 Правила работы: Hermes-конфигурация в Балде
В Hermes у Alex накоплены: правила ведения документации (Obsidian — источник истины),
@@ -248,21 +409,20 @@ spec:
## 2.5 Карта стека: кто кого дёргает
> ⚠️ Обновлено под топологию §1 (стек на Mac). Прежний вид — «Балда на Kraken + MCP +
> мост → callee» — **устарел**, оставлен ниже как справка по демонтированным узлам.
### Актуально (топология «стек на Mac»)
```
ВЫ (Mattermost / Telegram)
ВЫ (Mattermost)
│ задача словами
▼
Балда-оркестратор — Kraken, balda-mm:0.1.13, DeepSeek
│ понимает задачу, читает Obsidian, решает куда идти, отвечает
▼ MM API — исходящее соединение с Mac
Балда-оркестратор — Mac, balda-mm:0.1.13, DeepSeek
│ понимает задачу, читает Obsidian, планирует, держит крон, отвечает
│ НЕ пишет код, НЕ собирает, НЕ ходит по хостам
▼ MCP-инструмент executor_run
balda-executor-mcp — Kraken, :8890, HTTP + X-Bridge-Secret
│ транспорт без логики
▼ Cloudflare Tunnel (agents-bridge.qentra.top)
мост agents-bridge — Mac, :8790, launchd
│ транспорт без логики: идемпотентность, таймаут 900с, лимит 1
▼ docker exec
callee — Mac, контейнер balda-executor
▼
callee — Mac
│ ДВИЖОК ВОРКФЛОУ: компилирует граф, валидирует целиком до запуска
│ МОДЕЛИ НЕ СОДЕРЖИТ: дирижёр — раздаёт роли, гоняет циклы, собирает артефакт
├─ Role ────► ACP-процесс ───► МОДЕЛЬ (DeepSeek / позже Codex)
@@ -270,15 +430,24 @@ callee — Mac, контейнер balda-executor
└─ Human ───► пауза, ответ реплаем в треде MM
▼
bd (Beads) — трекинг Story → Tasks, живёт между сессиями
▼
артефакт → Балда → Mattermost
```
### Демонтированные узлы (не нужны в новой топологии)
| Узел | Почему больше не нужен |
|---|---|
| `balda-executor-mcp` (:8890) | его работа — пропихнуть вызов к мосту; Балда на Mac → пропускать нечего |
| мост `agents-bridge` (:8790) | был границей хостов; границы нет |
| Cloudflare Tunnel (для этого потока) | остаётся только для внешнего доступа к MM |
| `obsidian-mcp` на Kraken | Obsidian рядом с Балдой |
### Кто есть кто
| Компонент | Где | Что это |
|---|---|---|
| Балда | Kraken | Оркестратор верхнего уровня: понимает смысл задачи |
| balda-executor-mcp | Kraken | Проброс по HTTP, логики нет |
| мост `:8790` | Mac | Идемпотентность, таймаут, лимит 1, логики нет |
| Балда | **Mac** | Оркестратор верхнего уровня: понимает смысл задачи, планирует, крон |
| **callee** | Mac | Движок воркфлоу. Граф, роли, циклы. **Не модель** |
| Role / DynamicRole | Mac | Узел, который зовёт модель |
| **ACP** | Mac | Протокол «редактор ↔ агент». Транспорт, не модель |
@@ -286,8 +455,8 @@ bd (Beads) — трекинг Story → Tasks, живёт между сесси
| **Codex / Claude CLI** | Mac | Агент-харнесс: модель + умение править файлы и запускать команды |
| **bd (Beads)** | Mac | Память задач: что сделано, что нет |
| **prism** | Mac | Воркфлоу: specify → design → breakdown → human → apply → verify |
| tunnels | — | Cloudflare: Kraken→Mac по HTTP. **Не VPN** |
| **lume** | Mac | macOS-виртуалки. Только для задач, требующих настоящую macOS |
| **lume** | Mac (хост) | macOS-виртуалки: `macos-15-golden`, `macos-27-golden`, `uitest-26.4.1-golden`, `pdf-hud-macos27` |
| MM-сервер | **Kraken** | Только чат-сервер 24/7. Больше ничего |
📌 **`callee` не умеет ничего сам** — ни думать, ни писать код. Он берёт описание графа,
проверяет его и по шагам вызывает: роль → модель, скрипт → shell, human → пауза.
@@ -399,6 +568,9 @@ callee agent import baldaworks/prism-callee --path pack/callee/prism --prefix pr
## 5. Фазы
> ⚠️ Переписано под топологию §1 (стек на Mac). Прежняя фаза C3 («переписать `PROVIDERS`
> в мосте») **отменена**: мост в новой топологии не нужен.
### C0 — ✅ ЗАКРЫТА 2026-09-29
Разведка npm: готовый ACP-адаптер к DeepSeek существует (§2.1). Свой адаптер не нужен.
@@ -407,22 +579,39 @@ callee agent import baldaworks/prism-callee --path pack/callee/prism --prefix pr
`/-/v1/search?text=...`. ⛔ Поиск по `scope:normahq` и `scope:baldaworks` даёт пусто —
scoped-пакеты так не индексируются.
### C1 — установка в контейнер `balda-executor` (1-2 ч)
### C1 — переезд стека на Mac (2-3 ч)
Ни `callee`, ни `bd` нет ни в контейнере, ни на хосте.
Объём вырос относительно исходного варианта: теперь это не «установка callee в контейнер»,
а **переезд Балды на Mac + установка инструментов рядом**.
| # | Шаг | Проверка |
|---|---|---|
| 1 | `npm i -g @baldaworks/callee` | `callee --version` |
| 2 | `npm i -g @openma/deepseek-harness-acp` | `dsh-acp --help` |
| 3 | `callee setup codex` → стартовые ресурсы | `callee agent list` содержит `workflows/investigate` |
| 4 | Установить Beads `bd` | `bd where` |
| 5 | Импорт prism-пака | `callee agent view prism/lifecycle --json` → `Router` |
| 6 | Прогон стартового `investigate` | артефакт в stdout, exit 0 |
| 1 | Запустить второй инстанс `balda-mm:0.1.13` на Mac | контейнер Up, лог `Balda bot started` |
| 2 | 🔴 **Отдельный MM-бот и токен для Mac-инстанса** | два процесса на один WebSocket MM рвут события (`bad_seq`) |
| 3 | `npm i -g @baldaworks/callee` на Mac | `callee --version` |
| 4 | `npm i -g @openma/deepseek-harness-acp` | `dsh-acp --help` |
| 5 | `callee setup codex` → стартовые ресурсы | `callee agent list` содержит `workflows/investigate` |
| 6 | Установить Beads `bd` | `bd where` |
| 7 | Импорт prism-пака | `callee agent view prism/lifecycle --json` → `Router` |
| 8 | `obsidian-mcp` рядом с Балдой (том `~/obsidian:/vault`) | `/sse` отвечает |
| 9 | Прогон стартового `investigate` | артефакт в stdout, exit 0 |
📌 Персистентность: установки живут в слое контейнера. Чтобы переживали пересборку —
**Решения по хранению (принять на C1):** установки должны переживать пересборку образа —
том или `Dockerfile`.
### C1a — доступы и исполнители (объём зависит от развилки)
Закрывает шесть сценариев из §2.5. **Объём определяется выбором в §6.7:**
| Что нужно | Как |
|---|---|
| `~/.ssh` в контейнере | монтирование **конкретных ключей** с `command=` в `authorized_keys`, не каталога целиком |
| `docker build` | ❌ **не через `docker.sock`**; либо обёртка на хосте, либо хост-исполнитель |
| `lume` | ❌ в контейнер не пробрасывается; хост-исполнитель |
| `xcodebuild` | ❌ в Linux-контейнере невозможно; **lume VM с macOS** |
| `~/Developer` | рабочая копия в `~/agents/<проект>` + git (см. §1.1a) |
| Доступ к хостам (TrueNAS, Kraken) | ssh-ключ с ограничением `command=` |
### C2 — Role к DeepSeek (1 ч)
Описать `Role` по §2.3 и проверить один визит вживую.
@@ -430,24 +619,14 @@ scoped-пакеты так не индексируются.
**Гейт:** в логе видны `role_model` и `role_reasoning`. До успешного рендера провайдера
эти поля опускаются — их появление означает, что рендер прошёл.
### C3 — подключение к мосту (2-3 ч)
### C3 — ~~подключение к мосту~~ ОТМЕНЕНА
1. Бэкап `~/Developer/agents-bridge/executor.py`.
2. Замена argv-таблицы `PROVIDERS`:
```python
PROVIDERS = {
"callee": ["callee", "agent", "run", "prism/lifecycle", "--message"],
}
```
3. `EXECUTOR_PROVIDER=callee` в `~/Docker/balda-executor/.env`.
4. Перезапуск моста: `launchctl bootout` + `bootstrap`.
5. Прогон задачи через мост, проверка `exit_code`.
Мост `agents-bridge` в топологии «стек на Mac» не участвует. Фаза сохраняется только как
**план отката** к топологии «Kraken-оркестратор», если переезд не оправдается.
### C4 — таск-трекинг (1-2 ч)
1. `bd` инициализируется в `/agents`.
1. `bd` инициализируется в рабочей области.
2. Прогон `prism:story` на реальной задаче.
3. Проверить durable: прервать сессию, возобновить Story — состояние в Beads на месте.
@@ -472,9 +651,14 @@ PROVIDERS = {
| 1 | Провайдер обеих сторон | DeepSeek | команда Alex |
| 2 | Оркестрация | callee | документированная архитектура вместо самодельной |
| 3 | Таск-трекинг | prism + Beads | задание Alex |
| 4 | Host coding agent | callee CLI в контейнере `balda-executor` | Node 24 — родная среда; там смонтирован `~/agents`; мост уже умеет `docker exec` |
| 5 | Мост `:8790` | сохранить, сменить argv | идемпотентность, таймауты, лимит 1 задача уже проверены; выбрасывать работающий слой ради эстетики незачем |
| 4 | Где живёт стек | **Mac целиком** | исполнение работает с файлами Mac → обязано быть рядом |
| 5 | Где MM-сервер | **Kraken** | единственный, кому нужна доступность 24/7 |
| 6 | Тип провайдера | `generic_acp` | список типов закрытый, DeepSeek в нём нет |
| 7 | 🔴 **Открыто: доступ к Xcode/lume/docker** | **не решено** | узкий контейнер (три операции недоступны) vs гибрид с хост-исполнителем |
| 8 | Роли: где живут | статично, файлами в каталоге | рантайм-выдачи ролей в callee не существует (§1.1b) |
| 9 | Права | `права процесса × монтирование × граф` | гейт `Human` защищает действие, но не возможность (§1.1a) |
| 10 | Мост `:8790` / MCP `:8890` | **демонтируются** | лишние посредники без содержательной работы |
| 11 | Старый инстанс на Kraken | **оставить живым как запасной** | откат без пересборки; ⚠️ требует отдельного MM-бота |
---