Multica Docs

Project Resources

Прикрепляйте типизированные указатели (Git-репозитории, локальные каталоги и др. позже) к project, чтобы agent получал их как scoped context.

Project Resource — типизированный указатель: URL Git-репозитория, путь на вашей машине, страница Notion завтра — прикреплённый к project. Когда agent выполняет task по issue внутри этого project, daemon автоматически записывает список ресурсов project в рабочий каталог agent и в его meta-skill prompt.

В результате agent знает, какой репозиторий checkout (или в каком локальном каталоге работать) и какие документы — «основные ссылки» для этого project, без копирования контекста в тело issue.

Ментальная модель

Project — уже не просто метка. Это небольшой контейнер ресурсов:

  • У project есть 0..N resources.
  • У resource есть resource_type (например github_repo, local_directory) и resource_ref (JSON payload, типизированный resource_type).
  • Новые типы resource добавляют строку и handler. Без миграции схемы. Без переписывания frontend.

Форма намеренная — тот же паттерн, что Multica уже использует для provider agent: дискриминатор type и типизированный payload. Схема остаётся стабильной, поэтому добавление «Notion page», «Google Doc», «uploaded file» или «external URL» позже — небольшое аддитивное изменение.

Сейчас доступны два типа resource: github_repo (clone-per-task в изолированный worktree) и local_directory (выполнение напрямую в папке на машине конкретного daemon).

Resource type: github_repo

Тип resource по умолчанию — checkout на каждый task в изолированный worktree:

{
  "resource_type": "github_repo",
  "resource_ref": {
    "url": "https://github.com/owner/repo",
    "default_branch_hint": "main"
  }
}

default_branch_hint необязателен — если указан, daemon показывает его в meta-skill, чтобы agent знал, на какую ветку опираться.

Resource type: local_directory

Для репозиториев, которые нельзя разумно переклонировать на каждый task — многогигабайтные game checkout, крупные monorepo или любой project, где модель worktree-per-task неудобна — project может указывать на существующий каталог на машине конкретного daemon. Agent работает напрямую в этой папке, без clone, копирования и worktree.

{
  "resource_type": "local_directory",
  "resource_ref": {
    "local_path": "/Users/me/code/big-game",
    "daemon_id": "0001234e-…",
    "label": "main checkout"
  }
}

Компромисс относительно github_repo намеренный: только привязанный daemon может забирать task для этого каталога, и task на одном каталоге выполняются последовательно, а не параллельно. Взамен вы сохраняете текущий checkout, ветку и dirty state — Multica никогда не переклонирует каталог.

Когда выбрать local_directory вместо github_repo

Аспектgithub_repo (worktree)local_directory
Стоимость checkout на taskСвежий clone + worktreeНет — agent работает на месте
Параллелизм на одном repoМного task параллельноПо одному на каталог
Ветка / dirty stateКаждый task получает свежую ветку от defaultЧто есть в каталоге сейчас
Где может выполнятьсяЛюбой daemonРовно один daemon (привязанный)
ДискОдин worktree на taskНулевые накладные — ваша папка

Выбирайте local_directory, если хотя бы одно из условий выполняется:

  1. Переклонирование слишком дорого — многогигабайтный game checkout, monorepo с тяжёлыми LFS-ассетами или всё, где git clone на task доминирует над самой работой. Вы жертвуете параллелизмом ради запуска без clone.
  2. Изменения мелкие, и вы хотите review локально по ходу — вы итерируете один компонент, переключаетесь между правками agent и редактором каждые несколько минут и предпочитаете существующий checkout как source of truth, а не per-task worktree из ~/multica_workspaces/.

Компромисс в обоих случаях один: в этой версии нет file-level write lock. Единственная защита — последовательный gate на каталог (один task за раз на одну папку) от одновременного доступа agent из разных issue к одним файлам. Если направить agent двух issue на один local_directory, их task встают в очередь, а не параллелятся — так задумано. Нужен настоящий параллелизм на одной кодовой базе — оставайтесь на github_repo.

Прикрепление локального каталога

Folder picker есть только в Desktop app — веб-приложение не может читать пути ОС, поэтому кнопка «Add local directory» там скрыта. В Desktop:

  1. Откройте project → панель Resources.
  2. Нажмите Add local directory. Откроется нативный folder picker.
  3. Выберите папку. Путь привязывается к daemon, зарегистрированному этой установкой Desktop — в записи resource хранятся и путь, и ID этого daemon.

В Desktop кнопка видна, но отключена с подсказкой, когда daemon на этой машине offline или когда у project уже есть local_directory, привязанный к этому daemon — чтобы было видно, почему недоступно. (В веб-приложении кнопка скрыта полностью — folder picker там недоступен.) Чтобы привязать каталог другой машины, установите Desktop на ней и добавьте resource оттуда.

Из CLI (работает и в web-only средах, если вы сами укажете daemon ID):

multica project resource add <project-id> \
  --type local_directory \
  --local-path /Users/me/code/big-game \
  --daemon-id <daemon-uuid> \
  --ref-label "main checkout"        # optional

multica project resource update <project-id> <resource-id> \
  --local-path /Users/me/code/big-game-new

--daemon-id берётся из multica daemon list. CLI также принимает универсальный --ref '<json>', если хотите передать payload напрямую.

Правила для путей

Прикрепляемый путь должен пройти проверку при attach и при каждом task. Обе проверки выполняет daemon, владеющий resource — сервер хранит только JSON. Путь, нарушающий любое правило, завершает task с типизированной ошибкой и не трогает ваш каталог:

  • Должен быть абсолютным.
  • Должен существовать и быть каталогом (не файлом, symlink на файл или device node).
  • Должен быть readable и writable для процесса daemon.
  • Нельзя указывать системный корень или весь профиль пользователя — /, /Users, /home, /root, /etc, /tmp, /var, /usr, /opt, /Users/Shared, ваш $HOME, корень любого диска Windows (C:\, D:\, …) или C:\Users / C:\ProgramData / C:\Program Files / C:\Program Files (x86) / C:\Windows.
  • Symlink, разрешающийся в любой из перечисленных путей, отклоняется; то же для канонической формы OS-aliased path (например на macOS /private/tmp отклоняется так же, как /tmp).

Blacklist намеренно жёсткий — выбор home directory поместил бы runtime-файлы Multica в корень аккаунта, чего вы не хотите. Указывайте подпапку (обычно ваш project checkout).

Один на (project, daemon)

У project может быть не более одного local_directory на daemon. Попытка добавить второй на том же daemon возвращает 409 от API; кнопка Desktop скрывается при достижении лимита и показывает tooltip с объяснением.

Разные daemon независимы — общий project может иметь по одному local_directory на машину каждого коллеги, привязывая один project к разным папкам на разных хостах. Когда daemon забирает task, он выбирает строку со своим ID и игнорирует остальные.

Смешение типов resource и несколько local_directory

На практике встречаются две кросс-resource конфигурации:

  • github_repo + local_directory в одном project. На daemon с подходящей привязкой local_directory локальный каталог имеет приоритет: agent работает в вашей папке, daemon не создаёт и не использует worktree github_repo для этого task. (Per-workspace repo cache может синхронизироваться как обычно — это фоновое поведение, не связанное с worktree этого task.) URL github_repo всё равно появляется в .multica/project/resources.json и в секции ## Repositories agent для справки — но worktree, который редактирует agent, ваш локальный, а не worktree. На daemon без строки local_directory для этого project (другая машина или до attach коллегой) task идёт по обычному flow worktree github_repo. По сути local_directory — per-daemon override пути worktree.
  • Два local_directory в одном project. Поскольку каждый local_directory привязан ровно к одному daemon, это возможно только на двух разных машинах (API отклоняет два на одном daemon при attach, см. выше). Task маршрутизируются по runtime assignment agent, а не по тому, у какого daemon есть local directory: task попадает на daemon, владеющий runtime принимающего agent; этот daemon выбирает строку local_directory со своим ID и игнорирует остальные. Load-balancing нет — нужна конкретная машина, отправьте agent, привязанный к runtime этой машины.

Daemon без строки local_directory для project, у которого она привязана на другой машине, не блокируется — его task идут через другие resource project (обычно fallback github_repo). local_directory важен только для привязанного daemon.

Выполнение task против local directory

Когда task отправлен по issue, чей project имеет local_directory, привязанный к принимающему daemon, daemon:

  1. Повторно проверяет путь (правила выше).
  2. Берёт per-directory lock по symlink-resolved real path — два маршрута к одной папке (через symlink и напрямую) всё равно сериализуются.
  3. Записывает CLAUDE.md / AGENTS.md agent (и .multica/project/resources.json) в каталог пользователя. Agent работает там, как если бы вы сами открыли папку.
  4. Хранит runtime-артефакты Multica (output/, logs/, .gc_meta.json) в отдельном envRoot вне каталога пользователя.

Если второй task для того же каталога приходит, пока первый выполняется, он паркуется со status Waiting for local directory. Status виден везде, где отображается task — task pill в чате, banner agent, execution log и activity indicator — и припаркованный task учитывается в «queued» presence agent. Отмена припаркованного task сразу освобождает слот; отмена выполняющегося — следующий продвигается.

Ожидание не по таймауту — припаркованный task остаётся припаркованным, пока lock не освободится или пользователь / agent не отменит его.

Что Multica трогает и не трогает в вашем каталоге

  • Запишет CLAUDE.md / AGENTS.md (или эквивалент для provider agent) и .multica/project/resources.json в корень каталога, чтобы у agent были meta-skill и список resource. Добавьте их в .gitignore, если не хотите коммитить.
  • Запишет любые правки кода, которые решит внести agent — так же, как если бы вы запустили agent локально сами.
  • Никогда физически не удалит каталог или его содержимое. Garbage collection учитывает путь: для local_directory envRoots очищаются только собственные output/ и logs/ под workspacesRoot; каталог пользователя вне зоны доступа.

Ограничения v1 (будут ужесточены позже)

Первый релиз намеренно острее, чем github_repo. Список будет сокращаться — ниже то, что верно сегодня:

  • Нет автоматического переключения веток. Agent работает в той ветке, что у вас checkout. Переключите ветку до dispatch, если это важно.
  • Нет защиты dirty tree и auto-commit. Незакоммиченные изменения видны agent, могут меняться на месте и не stash'атся. Считайте каталог реальным working tree и коммитьте перед рискованными запусками.
  • Нет автоматического PR. По завершении task изменения остаются на той ветке, где сделаны — ничего не push'ится и PR не открывается. Push и PR — сами, когда готовы.
  • waiting_local_directory показывает status, а не holder. Badge говорит, что task припаркован; не показывает, какой task или путь сейчас держит каталог.

Это отслеживается как follow-up agent-task-lifecycle к local-directory; пока он не вышел, считайте local_directory как «agent работает в вашей папке так же, как вы».

Прикрепление repo при создании project

В Web или Desktop app при открытии New project появляется pill Repos рядом со Status / Priority / Lead. Выбор repo, привязанных к workspace (или вставка ad-hoc URL), прикрепляет их как github_repo resource в момент создания project.

Из CLI:

# Create + attach in one shot. The server attaches resources in the same
# transaction as the project create — invalid resources roll back the whole
# operation, so you never end up with a project that has half its resources.
multica project create \
  --title "Agent UX 2026" \
  --repo https://github.com/multica-ai/multica

# Manage resources later
multica project resource list <project-id>
multica project resource add  <project-id> --type github_repo --url <url>
multica project resource remove <project-id> <resource-id>

# Generic escape hatch for any resource_type the server understands —
# no CLI change needed when a new type ships:
multica project resource add <project-id> \
  --type notion_page \
  --ref '{"page_id":"…","title":"…"}'

--repo можно повторять; каждое значение прикрепляется как отдельный github_repo resource.

Что agent видит в runtime

Когда daemon запускает agent для issue внутри project, происходят две вещи:

1. .multica/project/resources.json

Структурированный pass-through ответа API, записанный в рабочий каталог agent:

{
  "project_id": "…",
  "project_title": "Agent UX 2026",
  "resources": [
    {
      "id": "…",
      "resource_type": "github_repo",
      "resource_ref": {
        "url": "https://github.com/multica-ai/multica",
        "default_branch_hint": "main"
      }
    }
  ]
}

Skill, helper scripts или сам agent могут парсить этот файл, когда нужен точный набор resource для запуска.

2. Секция «Project Context» в meta-skill prompt

CLAUDE.md / AGENTS.md agent (в зависимости от provider) теперь включает читаемое резюме:

## Project Context

This issue belongs to **Agent UX 2026**.

Project resources (also written to `.multica/project/resources.json`):

- **GitHub repo**: https://github.com/multica-ai/multica (default branch: `main`)

Resources are pointers — open them only when relevant to the task. For
`github_repo` resources, use `multica repo checkout <url>` to fetch the code.

Текст намеренно минимален. Полный payload на диске; prompt лишь ориентирует agent, что project существует и что прикреплено.

Режим сбоя

Получение resource — best-effort. Если вызов API не удался, секция project опускается из prompt и файл не записывается, но task всё равно стартует. Agent никогда не блокируется из-за отсутствующего project context.

Добавление нового типа resource

Смысл абстракции — новые типы дешёвы. Полный путь:

  1. Server validator (server/internal/handler/project_resource.go) — case в validateAndNormalizeResourceRef, который парсит и нормализует новый payload.
  2. Daemon meta-skill formatter (server/internal/daemon/execenv/runtime_config.go) — case в formatProjectResource, чтобы prompt agent рендерил новый тип читаемым bullet.
  3. TypeScript types (packages/core/types/project.ts) — расширить ProjectResourceType и добавить payload interface.
  4. UI renderer (packages/views/projects/components/project-resources-section.tsx) — case в ResourceRow для нового типа.

Нет миграции схемы, нового sqlc query, нового endpoint и изменения CLI — универсальный флаг --ref '<json>' CLI принимает любой payload, который понимает validator, поэтому day-one поддержка нового типа — только четыре шага выше. (Опционально можно добавить per-type CLI shortcut позже; не обязательно.)

Та же таблица project_resource и те же три CRUD-вызова обрабатывают каждый тип.

Workspace repo vs. project repo

Список repo для agent (блок ## Repositories в CLAUDE.md / AGENTS.md) выбирается claim handler daemon с таким приоритетом:

  • У project есть хотя бы один github_repo resource → agent видит только эти repo. Repo, привязанные к workspace, намеренно скрыты, чтобы agent не угадывал, какой относится к этому issue.
  • У project нет github_repo resource (или issue не в project) → fallback на список repo workspace, как раньше.

Так working set agent остаётся узким: когда project явно указывает repo, это авторитетный ответ. Структурированный список в .multica/project/resources.json всегда несёт полный набор — skill, которому нужно всё, всё равно может прочитать.

Daemon зеркалит это на стороне checkout: когда task приходит с project-scoped github_repo URL, эти URL объединяются в per-workspace allowlist и синхронизируются в локальный repo cache до spawn agent. URL project repo, не привязанный на уровне workspace, всё равно валиден для multica repo checkout — daemon не отклонит его как «not configured». Разделение allowlist внутреннее: workspace-bound URL и task-scoped URL отслеживаются отдельно, поэтому refresh workspace repo не отзовёт project URL mid-run.

Что намеренно вне scope

  • Cross-project sharing. Каждый resource сегодня живёт ровно в одном project.
  • Per-skill resource scoping. Все resource видны каждому skill при запуске agent; type-aware filtering — follow-up.
  • Caching / sync. github_repo — только metadata; checkout по-прежнему через multica repo checkout по требованию. Кэшированный текст документов для Notion / Google Docs придёт с этими типами.

Это намеренные упущения — цель первого среза: проверить абстракцию с минимальным набором движущихся частей.