Files
obsidian-vault/personal/plans/balda-callee-prism-plan.md
T

51 KiB
Executable File
Raw Blame History

Балда → Callee + Prism: делегирование

Обновлён 2026-09-29. Статус: C0 закрыта. Топология пересмотрена: стек переезжает на Mac, на Kraken остаётся только MM-сервер. C1-C5 не начаты. Часть personal/projects/balda/balda-mm-delegation-plan. Опорная заметка по контрактам инструментов: personal/tech/callee-prism.

🔴 Открытая развилка (§6.7): как давать доступ к xcodebuild / lume / docker build — узкий контейнер (три операции недоступны) или гибрид с хост-исполнителем. Определяет объём C1a.


1. Целевая архитектура

Принцип: стек — единое целое по признаку «работает с файлами на Mac». Всё, что трогает код, сборку, тесты, хосты, обязано быть рядом. Всё, что только принимает сообщения — может жить отдельно.

┌─ Mac ─────────────────────────────────────────────┐
│  Балда (оркестрация, планирование, крон)          │
│  callee (движок воркфлоу)                         │
│  движки: codex / claude / dsh-acp                 │
│  bd (Beads) — состояние задач                     │
│  тулчейн: ~/Developer, xcodebuild, lume, docker   │
└───────────────────────────────────────────────────┘
              ▲
              │  Mattermost API — ИСХОДЯЩЕЕ соединение с Mac
              │
┌─ Kraken ──────────────────────────────────────────┐
│  MM-сервер + postgres      ← только это           │
└───────────────────────────────────────────────────┘

Что это убирает

Узел Почему больше не нужен
balda-executor-mcp (:8890) его работа — пропихнуть вызов к мосту. Балда на Mac → пропускать нечего
мост agents-bridge (:8790) был границей хостов. Границы нет — нет моста
Cloudflare Tunnel для этого потока нужен только для внешнего доступа к MM
MCP-сервер obsidian-mcp на Kraken Obsidian рядом с Балдой

📌 Соединение — исходящее с Mac к MM. Никаких входящих портов, туннеля внутрь Mac, «Mac спит → мост недоступен». При недоступности MM — переподключение.

Что остаётся на Kraken

Только MM-сервер + postgres. Доступность 24/7, один сетевой вход, ничего тяжёлого. Kraken разгружается: уходят Балда, MCP-сервер, obsidian-mcp (при load 8.03 на 4 ядрах это заметно).

Разделение расходов

Роль Где Провайдер
Оркестратор Mac DeepSeek deepseek-chat
Исполнитель-роли callee Mac DeepSeek

📌 «DeepSeek с обоих концов» = обе стороны на DeepSeek. Codex/Claude подключаются позже заменой провайдера в DynamicRole — без правки графа.


1.1 Балда на Mac: контейнер и границы доступа

Где живёт

В Docker-контейнере. Причины те же, что были на Kraken: изоляция, воспроизводимость, образ уже собран (balda-mm:0.1.13), откат одним тегом.

Проблема: контейнер изолирован, а нужен тулчейн

В контейнере нет и не должно быть по умолчанию:

Чего нет Почему это правильно
~/.ssh ключи дают доступ ко всем хостам сразу
docker.sock эквивалент root на хосте
пароли, .env с токенами Балде они не нужны
~/.aws, ~/.config/gh, прочие креды то же

Но нужны: xcodebuild, lume, docker build, опционально ssh.

Решение: три класса доступа

Класс Что Как Права
1. Прямой файлы проектов, git bind-mount ~/Developer (только нужные проекты, не весь ~) чтение-запись в проекте
2. Через шлюз docker build, lume не давать docker.sock и бинарь — вместо этого узкий сокет/скрипт-обёртка на хосте с белым списком команд только разрешённые операции
3. Через ssh доступ к хосту, TrueNAS, Kraken ssh с ключом ограниченным command= в authorized_keys одноразовые команды, без шелла

🔴 docker.sock в контейнер не монтировать никогда. Это тождественно выдаче root на хосте: через сокет можно запустить контейнер с -v /:/host и получить всё. Если нужны сборки — либо docker build через ограниченную обёртку на хосте, либо вынести сборку в отдельный Script-узел, исполняемый на хосте, а не в контейнере.

Xcode и lume: где физически

Задача Где исполняется Почему
xcodebuild не в Linux-контейнере — в lume VM с macOS контейнер Linux, Xcode только на macOS
lume на хосте Mac бинарь на хосте, в контейнере недоступен
docker build на хосте Mac docker.sock в контейнер не даём

📌 Это означает: часть ролей исполняется НЕ в контейнере Балды. Значит нужен явный механизм «роль → хост-исполнитель». Вариант — callee запускается не только в контейнере, но и на хосте (для ролей, требующих Xcode/lume/docker); маршрутизация роли по хосту описывается в графе.

Границы: что Балда НЕ должна получить

Ресурс Статус
пароли, токены в открытом виде ❌ только через env-файл с ограниченным набором (DeepSeek-ключ)
~/.ssh целиком ❌ только конкретные ключи
docker.sock ❌ никогда
весь ~ ❌ только выбранные проекты
root на хосте ❌
host-шелл ❌ только белый список команд

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-файл в каталоге. Не «право», не «токен», а описание листа графа: какой провайдер, какой промпт, какие параметры, какой таймаут.

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 — источник истины), правила работы (план → апрув → работа, бэкапы, запрет удаления), формат ответов (артефакт первой строкой, единица счёта, ADHD-friendly), правила приёмки (от учётки заказчика, а не подставной).

Эти правила — не код, а контент. Значит их надо переносить как контент, а не зашивать в Балду.

Слой Hermes Аналог в Балде Как переносится
System prompt / SOUL balda.global_instruction текст в конфиг
Память (memory) session memory Балды + Obsidian Obsidian MCP
Скиллы (процедуры) callee-ресурсы (Role/Sequential) правила работы как воркфлоу
Правила документирования global_instruction + Role, обновляющий Obsidian текст
Формат ответов deliveryfmt / questionfmt рендеринг, не промпт
Правила приёмки Script + Human в графе исполняемый граф

🔴 Ключевое различие: в Hermes правила лежат в промпте и памяти — это мягкое принуждение (модель может нарушить). В callee часть правил становится жёсткой: валидация графа, Human-гейт, Script-проверка. Например «план → апрув → работа» превращается в узел Human между фазами, а не в просьбу в промпте.

Что это значит на практике: правила, которые важны как инвариант (апрув, бэкап, приёмка) — переносятся в граф как узлы. Правила, которые про стиль (формат ответа, тон) — остаются в global_instruction.

Obsidian

Балда ходит в Obsidian по MCP. На Mac поднимается obsidian-mcp рядом (по образцу Kraken — контейнер mcp-proxy --port 3101 -- mcpvault /vault, type: sse), том ~/obsidian:/vault.


2. Провайдер DeepSeek для callee

2.1 ACP-адаптер — выбран @openma/deepseek-harness-acp@0.4.33

Свой адаптер писать не надо.

Значение
Пакет @openma/deepseek-harness-acp
Версия 0.4.33
bin dsh-acp → dist/bin.js — запускаемый ACP-сервер
Родословная обёртка вокруг официального DeepSeek Harness
Node >= 22.15 (в контейнере 24.21.0 ✅)
Лицензия Apache-2.0
Зависимости @agentclientprotocol/sdk 1.4.0, tar, zod

Установка (standalone-вариант, подходит для generic_acp):

npm i -g @openma/deepseek-harness-acp
# запуск: dsh-acp

2.2 Креды

Приоритет: флаги → переменные окружения → дефолты.

Способ Значение
Флаг api-key, либо api-key:<provider> при нескольких провайдерах
Env DEEPSEEK_API_KEY
Env DEEPSEEK_BASE_URL — OpenAI-совместимый endpoint override

🔴 DEEPSEEK_BASE_URL — это OpenAI-совместимый override, значит переезд на Colex сводится к смене одной переменной, без правки графа.

📌 Ключ DEEPSEEK_API_KEY лежит в ~/.hermes/hermes-whale/.env (длина 35).

📌 Креды не попадают в конфиг клиента: адаптер переиспользует ключ из credential store (dsh web / dsh-acp login), либо берёт DEEPSEEK_API_KEY из окружения процесса.

2.3 Конфигурация Role

Тип провайдера — generic_acp, потому что список поддерживаемых типов закрытый: codex, claude, opencode, copilot, grok, cursor, generic_acp. DeepSeek в нём отсутствует.

provider:
  type: generic_acp
  cmd: dsh-acp          # одно имя исполняемого файла, НЕ shell-строка
  model: deepseek-chat
  timeout: 20m
Поле Контракт
cmd одно имя исполняемого файла; для generic_acp обязателен
extraArgs упорядоченный argv-список; каждый элемент непустой
model / reasoning / mode опциональны; пустые → дефолт бэкенда
timeout Go duration, по умолчанию 15m
Конфиг провайдера обязан быть вложенным (spec.provider.*), плоские поля не поддержаны

2.4 DynamicRole — провайдер из состояния

Все поля провайдера — шаблоны, рендерятся из состояния в момент визита:

kind: DynamicRole
spec:
  provider:
    type: '{{ .State.provider }}'
    model: '{{ .State.model }}'
    timeout: '{{ .State.timeout }}'
  state:
    provider: generic_acp
    model: deepseek-chat
    timeout: 20m
Факт Значение
Что рендерится type, cmd, model, reasoning, mode, timeout, каждый extraArgs
Из чего .Prompt, текущий .Input, один согласованный post-node снапшот .State, .Params
Чего нельзя .Output, env, файловая система, сеть, часы, рандом, криптография, мутаторы
Когда валидируется до поиска и старта процесса; ошибка → визит падает, провайдер не стартует
Периметр визита конфиг фиксирован на весь визит, включая REPL-ходы; новый визит Loop рендерит заново
Переиспользование процесса одинаковые effective type+cmd → один процесс; сессия всегда новая

🔴 Безопасность: state-driven cmd и extraArgs могут выбирать исполняемый код. Не подавать туда вывод модели без ограничения через авторскую шаблонную логику.


2.6 Отлов опасных команд: возможные механизмы

⚠️ Исследование 2026-09-29. Решения нет — зафиксированы варианты и границы, выбор не сделан.

Где физически можно перехватить команду модели:

Место перехвата Что видит Кто решает
Внутри ACP-сессии (адаптер dsh-acp) каждую команду модели адаптер / обёртка вокруг него
Script-узел в графе только команды сборок/деплоев, вынесенные в граф граф callee

🔴 Ограничение охвата: узел Human в графе защищает только Script-шаги. Команды, которые модель запускает внутри своей ACP-сессии (обычный bash харнесса), граф не видит вообще. Значит «отловить все опасные команды» и «отловить только свои сборки» — принципиально разные объёмы работы: первое требует правки адаптера, второе делается чисто в графе.

Механизм 1 — regexp / белый список. Детерминированно, ~0 мс, не обходится вероятностной ошибкой. Ловит только известные шаблоны (rm -rf, обращение к docker.sock, ~/.ssh, curl | sh); обфускация обходит.

Механизм 2 — Jev (TypeSafeJev / типизированная оценка). Возвращает числа, а не решение: choice (вероятности по опциям), score (0–3 по рубрике), noul (0–1 для утверждения). Не LLM, не вызывает инструменты, текст не генерирует. Порог и действие пишет ваш код:

risk(0–3) / injection(noul) / exfiltration(noul)
   ├─ if risk >= 1.5 || injection >= 0.8  → deny
   ├─ if risk >= 0.5 || exfiltration >= 0.5 → ask
   └─ иначе                                  → allow

🔴 Если if не написан — тул выполнится как обычно. Jev сам не запрещает и не спрашивает; без обвязки он даёт «команда опасная» и ничего не меняет. Латентность — сотни мс на каждый тул-колл. Вендор позиционирует его как один слой defense-in-depth, не замену sandboxing.

Механизм 3 — Script + Human в графе. Апрув только для того, что само вынесено в Script. Внутри ACP-сессии не работает.

Граница, которая важнее гейта: любой из трёх механизмов защищает действие, но не возможность (§1.1a). Права определяет процесс и монтирование; гейт лишь разрешает выполнить уже технически возможное. Если ключ есть у процесса — он есть всегда.

Сопутствующие факты:

  • TypeSafeJev — один из 10 публичных kinds callee ([personal/tech/callee-prism]] §2.1), то есть нативный узел графа; схема порогов выше — из вендорской доки tool-approval.
  • permissions.mode (allow_all/ask/deny_all) — режим разрешений внутри ACP-сессии. В Балде ask работает для MM, вопрос уходит реплаем в тред (§4).
  • 🔴 Не подавать вывод модели в cmd/extraArgs DynamicRole — исполнение произвольного кода мимо любого гейта внутри сессии (питфолл 8).
  • Разбор слоёв привратника: Research/Gatekeeper Architecture for Agent Tool Calls — подходы 3 (OPA), 4 (Cedar), 5 (MCP-прокси), 8 (Jev).

2.5 Карта стека: кто кого дёргает

⚠️ Обновлено под топологию §1 (стек на Mac). Прежний вид — «Балда на Kraken + MCP + мост → callee» — устарел, оставлен ниже как справка по демонтированным узлам.

Актуально (топология «стек на Mac»)

ВЫ (Mattermost)
  │  задача словами
  ▼  MM API — исходящее соединение с Mac
Балда-оркестратор — Mac, balda-mm:0.1.13, DeepSeek
  │  понимает задачу, читает Obsidian, планирует, держит крон, отвечает
  │  НЕ пишет код, НЕ собирает, НЕ ходит по хостам
  ▼
callee — Mac
  │  ДВИЖОК ВОРКФЛОУ: компилирует граф, валидирует целиком до запуска
  │  МОДЕЛИ НЕ СОДЕРЖИТ: дирижёр — раздаёт роли, гоняет циклы, собирает артефакт
  ├─ Role ────► ACP-процесс ───► МОДЕЛЬ (DeepSeek / позже Codex)
  ├─ Script ──► shell на Mac: git, docker build, xcodebuild, pytest
  └─ Human ───► пауза, ответ реплаем в треде MM
  ▼
bd (Beads) — трекинг Story → Tasks, живёт между сессиями
  ▼
артефакт → Балда → Mattermost

Демонтированные узлы (не нужны в новой топологии)

Узел Почему больше не нужен
balda-executor-mcp (:8890) его работа — пропихнуть вызов к мосту; Балда на Mac → пропускать нечего
мост agents-bridge (:8790) был границей хостов; границы нет
Cloudflare Tunnel (для этого потока) остаётся только для внешнего доступа к MM
obsidian-mcp на Kraken Obsidian рядом с Балдой

Кто есть кто

Компонент Где Что это
Балда Mac Оркестратор верхнего уровня: понимает смысл задачи, планирует, крон
callee Mac Движок воркфлоу. Граф, роли, циклы. Не модель
Role / DynamicRole Mac Узел, который зовёт модель
ACP Mac Протокол «редактор ↔ агент». Транспорт, не модель
dsh / dsh-acp Mac Адаптер: отдаёт DeepSeek по протоколу ACP
Codex / Claude CLI Mac Агент-харнесс: модель + умение править файлы и запускать команды
bd (Beads) Mac Память задач: что сделано, что нет
prism Mac Воркфлоу: specify → design → breakdown → human → apply → verify
lume Mac (хост) macOS-виртуалки: macos-15-golden, macos-27-golden, uitest-26.4.1-golden, pdf-hud-macos27
MM-сервер Kraken Только чат-сервер 24/7. Больше ничего

📌 callee не умеет ничего сам — ни думать, ни писать код. Он берёт описание графа, проверяет его и по шагам вызывает: роль → модель, скрипт → shell, human → пауза. Вся содержательная работа — либо в модели через Role, либо в shell через Script.

📌 Модель ≠ агент-харнесс. dsh-acp только отдаёт DeepSeek. codex — харнесс: модель плюс умение править файлы, запускать команды, искать. Через ACP callee зовёт и то, и другое.

Разбор сценариев

Сценарий Механизм Где исполняется Статус
Настроить на Mac Script → локальный shell контейнер, только ~/agents ⚠️ ~/Developer не смонтирован
Настроить на TrueNAS Script → ssh truenas_admin@mallexxx.duckdns.org контейнер ❌ ~/.ssh не смонтирован
Настроить на Kraken Script → ssh kraken контейнер ❌ ssh не настроен; 🔴 риск потерять канал управления
Собрать Xcode Script → xcodebuild lume VM с macOS ❌ в Linux-контейнере невозможно
Тесты в lume Script → lume хост Mac ❌ lume стоит на хосте, не в контейнере
Собрать докер-образ Script → docker build хост Mac ❌ docker.sock не смонтирован

🔴 Корень: шесть сценариев делятся на две группы, и текущая схема закрывает одну.

Группа Сценарии Где
В контейнере файлы Mac, git, Linux-сборки balda-executor
Вне контейнера TrueNAS/Kraken по ssh, Xcode, lume, docker build нужен доступ к хосту

Причина одна: контейнер изолирован. Нет docker.sock, ~/.ssh, lume, Xcode, ~/Developer.

Как закрывать: роли по исполнителям

Вариант Суть Оценка
A. Роли по исполнителям ✅ контейнер чистый для Linux-задач; для остального роли ходят через ssh на хост и узлы. Требует ~/.ssh:ro в контейнер + ssh admin@host.docker.internal изоляция сохраняется; роли явно описаны в графе: «linux-задача», «macOS-задача», «удалённый хост»
B. Дать контейнеру больше прав смонтировать docker.sock, lume, ~/Developer проще, но контейнер теряет изоляцию — одну из причин своего существования

Решение: A.

Чего нет в схеме (докрутить в C1)

Нужно Статус
~/.ssh в контейнере ❌
docker.sock ❌
lume в контейнере ❌ (стоит на хосте, v0.5.3)
ssh с контейнера на Kraken/TrueNAS ❌
lume VM как исполнитель ❌ VM есть (macos-15-golden, macos-27-golden, uitest-26.4.1-golden, pdf-hud-macos27), интеграции нет
Разделение ролей по хостам ❌ в графе не описано

3. Prism — таск-трекинг

3.1 Callee и prism — независимые установки

Установка одного плагина не ставит другой.

baldaworks/prism baldaworks/prism-callee
Что это плагин в coding agent (Codex/Claude/Grok/Copilot/Cursor) callee-агенты для тех же воркфлоу
Требования coding agent + bd (Beads) coding agent + bd + callee ≥ 0.19.0
Команды $prism:story, $prism:epic, $prism:lifecycle $prism-callee:lifecycle
Состояние Beads (bd) Beads (bd)

Импорт callee-агентов:

callee agent import baldaworks/prism-callee --path pack/callee/prism --prefix prism

🔴 Проверка импорта: callee agent view prism/lifecycle --json → kind Router. Sequential = устаревший импорт, обновлять --force.

3.2 Жизненный цикл

Story: Specify → Design → Breakdown → Human approval → Apply → Verify. Фазы: phase:story:specify → design → breakdown → human → apply → verify.

Epic: Frame → Architecture → Roadmap → approval → Deliver Stories → Validation.

Правило Значение
Дети Epic только Stories
Дети Story только Tasks
Вложенные Epic / Task напрямую в Epic невалидно
Фиксированного диапазона числа детей нет — оценка качественная
Apply каждый переход требует явной авторизации человека
Одобрение Epic → Story не переносится

4. Одобрения в Mattermost — работают

Одобрение проходит ответом в треде, как в Slack.

Механизм Статус
Вопрос с опциями (markdown-нумерация) ✅ questionfmt + questioncmd + deliveryfmt, покрыто тестами
Приём ответа реплаем ✅ BuildInboundReply через RootID (channel/mattermost/inbound.go)
ask как режим разрешений ✅ mattermost в списке каналов (permissions/service.go)

📌 ask — это режим политики разрешений агента; механизм вопросов — отдельный слой. Оба в MM работают.

⛔ Инлайн-кнопок и реакций в MM нет — только Telegram. Событие reaction сознательно отбрасывается в channel/mattermost/ingress.go.


5. Фазы

⚠️ Переписано под топологию §1 (стек на Mac). Прежняя фаза C3 («переписать PROVIDERS в мосте») отменена: мост в новой топологии не нужен.

C0 — ✅ ЗАКРЫТА 2026-09-29

Разведка npm: готовый ACP-адаптер к DeepSeek существует (§2.1). Свой адаптер не нужен.

Метод поиска: прямые URL registry.npmjs.org/<scope>%2f<name> и /-/v1/search?text=.... ⛔ Поиск по scope:normahq и scope:baldaworks даёт пусто — scoped-пакеты так не индексируются.

C1 — переезд стека на Mac (2-3 ч)

Объём вырос относительно исходного варианта: теперь это не «установка callee в контейнер», а переезд Балды на Mac + установка инструментов рядом.

# Шаг Проверка
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 и проверить один визит вживую.

Гейт: в логе видны role_model и role_reasoning. До успешного рендера провайдера эти поля опускаются — их появление означает, что рендер прошёл.

C3 — подключение к мосту ОТМЕНЕНА

Мост agents-bridge в топологии «стек на Mac» не участвует. Фаза сохраняется только как план отката к топологии «Kraken-оркестратор», если переезд не оправдается.

C4 — таск-трекинг (1-2 ч)

  1. bd инициализируется в рабочей области.
  2. Прогон prism:story на реальной задаче.
  3. Проверить durable: прервать сессию, возобновить Story — состояние в Beads на месте.

C5 — сквозная приёмка (1 ч)

От учётки Alex, не подставным аккаунтом:

@balda добавь CSV-экспорт в отчёт
→ Specify → Design → Breakdown → Human approval → Apply → Verify
→ ответ в Mattermost + артефакт

Доказательство — лента qentra:balda-test, автор поста cbwsorzwoigbtrdnteugnx4aqo.


6. Решения

⚠️ Решения по §2.6 (отлов опасных команд) не приняты — это зафиксированное исследование, не выбор. Развилки ниже помечены явно.

# Вопрос Решение Почему
1 Провайдер обеих сторон DeepSeek команда Alex
2 Оркестрация callee документированная архитектура вместо самодельной
3 Таск-трекинг prism + Beads задание Alex
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-бота

7. Питфоллы

  1. 🔴 Два инстанса Балды на один MM-бот = рвут WebSocket друг другу. Симптом — bad_seq, события не приходят. Mac-инстансу нужен отдельный бот и токен.
  2. 🔴 docker.sock в контейнер не монтировать никогда — эквивалент root на хосте (через сокет запускается контейнер с -v /:/host).
  3. 🔴 ~/.ssh целиком не монтировать — один ключ даёт доступ ко всем хостам. Только конкретные ключи + command= в authorized_keys.
  4. 🔴 ACP-провайдер — дочерний процесс callee. Движок на хосте + callee в контейнере невозможны: один процесс-тред. Где callee, там и движки, и Script'ы.
  5. 🔴 Гейт Human защищает действие, но не возможность. Права определяет процесс и монтирование, не граф (§1.1a).
  6. ⚠️ prism/lifecycle должен быть Router — Sequential означает устаревший импорт, лечится --force.
  7. ⚠️ Provider cmd — одно имя файла, не shell-строка. Аргументы — только в extraArgs.
  8. ⚠️ Никогда не маршрутизировать вывод модели в cmd/extraArgs.
  9. ⚠️ Установки в контейнере не переживают пересборку — том или Dockerfile.
  10. ⚠️ docker-compose (дефис) на Mac, docker compose (пробел) на Kraken.
  11. ⚠️ Node >= 22.15 требуется адаптером (в образе 24.21.0).
  12. ⚠️ Динамических маунтов на macOS Docker нет — только up -d с пересозданием. Доступ к проектам — через рабочую копию в ~/agents + git, не через маунт под задачу.
  13. 🔴 Граф не видит команды внутри ACP-сессии. Узел Human/Script закрывает только то, что само вынесено в граф; обычный bash модели идёт мимо (§2.6).
  14. 🔴 Jev не запрещает и не спрашивает — он возвращает числа. Без вашего if по порогам тул выполнится как обычно (§2.6).

8. Риски

Риск Оценка Митигация
🔴 Mac спит → бот не отвечает высокий caffeinate / Energy Saver; MM-сервер на Kraken жив всегда, Mac переподключается
🔴 Развилка доступа к Xcode/lume/docker не решена высокий решить до C1a (§6.7)
Хост-исполнитель = дыра в изоляции средний узкий набор операций (3 шт.), явный и названный
Два инстанса Балды конфликтуют по MM средний отдельный бот/токен для Mac-инстанса
Импорт prism/* устаревает при обновлении средний проверять kind: Router после каждого импорта
callee обновляется и меняет контракт средний пиннить версию в ресурсах
Переезд стека с Kraken не оправдается средний оставить Kraken-инстанс живым как запасной (§6.11)
Стоимость DeepSeek растёт низкий исполнитель-роли — короткие визиты
Установка в контейнер не переживёт пересборку низкий том или Dockerfile

9. Порядок и запреты

Порядок: C1 → C1a → C2 → C4 → C5. (C3 отменена.)

НЕ трогать в ~/Developer/balda: go.mod, Dockerfile.release, go.sum, internal/apps/balda/app.go, internal/apps/balda/preflight.go — вне PR-дерева, нужны для сборки стенда. git checkout -- . / stash / reset --hard их снесёт. См. personal/plans/balda-mm-pr-plan §5.

НЕ начинать работу без явного «поехали». Ничего не удалять без отдельного подтверждения. В частности: Kraken-инстанс Балды, образы для отката, диагностические контейнеры, старые теги.


Связанные заметки