Несколько Клодов над одним проектом: locks, handoffs и email 1982 года

Несколько Клодов над одним проектом: locks, handoffs и email 1982 года

Я работаю с Claude Code параллельно на трёх подписках Pro. Коллеги — на своих. Часто работаем в одних и тех же файлах, в один день. Чтобы избежать потерь контекста, конфликтов и дублирования работы, я построила систему координации, вдохновлённую распределёнными системами 1980-х. Три слоя: handoffs, locks и почта. Ниже — как это работает.

Слой 1: Handoffs — «я закончил, подхватывай»

Когда новые сессии Claude приходилось каждый раз заново вводить в курс дела, я внедрила handoffs — короткие сводки в конце сессии, которые читает следующая.

Сначала писала всё в один файл HANDOFF.md. Но при нескольких чатах он быстро сломался: последняя запись перезаписывала остальные. Перешла на append-only модель: каждая сессия создаёт свой файл с уникальным именем.

Имя включает дату, время, первые 8 символов session-id и slug задачи. Два чата не могут создать файл с одинаковым именем — даже в одну секунду, потому что session-id разные. Гонка состояний невозможна без блокировок и брокеров.

Файл INDEX.md тоже append-only: новые записи добавляются в конец, старые не редактируются. Это аналог write-ahead log в базах данных: если сессия умрёт посреди записи, состояние останется согласованным.

Handoff — не дневник, а контракт со следующей сессией. Состоит из пяти секций:

  • Цель — зачем сессия проводилась.
  • Сделано — конкретные артефакты с путями.
  • НЕ сработало — проваленные подходы с причинами.
  • Текущее состояние — что работает, что сломано, что заблокировано.
  • Следующий шаг — одно конкретное действие.

Пример handoff’а по проекту color-checker:

  • Цель: «Color checker: CNN sweep + diffusion, первые результаты»
  • Сделано: «CNN baseline median 1.99 deg, 11M params, 21 MB»
  • НЕ сработало: «lr=3e-4 — NaN после epoch 10–13, нет gradient clipping»
  • Сломано: «Diffusion training OOM на bs=16»
  • Следующий шаг: «Inference скрипт для diffusion + visual sheets»

Самая ценная часть — «НЕ сработало». Она экономит часы на повторении ошибок. За четыре дня накопилось 27 handoff’ов — и ни одна ошибка не повторилась.

Handoff укладывается в 1500 токенов. Исходный контекст — около 100K. Компрессия — ~67×, без потерь ключевой информации.

Хуки вместо напоминаний

Писать handoff вручную неудобно. Решение — Stop-hook в Claude Code. Перед закрытием сессии он спрашивает: «Хочешь сохранить handoff?» если работа была содержательной.

На старте новой сессии я пишу: «подхвати handoff по color-checker» — и Claude читает нужный файл.

Иногда модель сама предлагает записать handoff — особенно после завершения крупной задачи или при заполнении контекста на 70–80%. Это её инициатива, а не мой запрос.

Запись — автоматическая (хук или модель). Чтение — одна фраза в начале сессии.

Rollups — когда handoff’ов становится слишком много

На долгих проектах накапливается по 80 handoff’ов. Новая сессия читает последние, а старые — игнорирует. В итоге повторяются уже опровергнутые гипотезы.

Решение — rollup’ы: один файл сворачивает 20 предыдущих handoff’ов. В нём — итог и граница: «свёрнуто до этой даты». Каждому исходному handoff’у добавляется строка rolled_up_into с именем rollup-файла.

Новая сессия читает только rollup и handoff’ы после его границы. Остальное — в архиве.

Имя rollup’а начинается с rollup_ — чтобы было видно в INDEX.md. Frontmatter содержит:

  • type: rollup
  • covers — список подчинённых файлов
  • through — дата-граница

Текст rollup’а — обычный handoff, но за 20 сессий. Паттерн не мой: аналог есть у PavelMuntyan/MF0-1984 в SQLite-схеме. Я адаптировала его под markdown-frontmatter.

Слой 2: Locks — «этот ресурс сейчас мой»

Handoff’ы не решают конфликты за ресурсы: модули кода, GPU, деплой-слоты. Две сессии не могут одновременно рефакторить один модуль — будет конфликт слияния. Или тренировать модель на одной GPU — вторая получит OOM.

Нужны блокировки. Для каждого ресурса — отдельный файл: auth-middleware.lock, gpu-host-a_gpu2.lock. В нём — кто взял, когда, для чего.

Создание файла — атомарное: os.open(path, O_CREAT | O_EXCL). Если файл уже есть — вызов падает. Это гарантирует ядро ОС. Тот же механизм, что у /var/lock и .pid-файлов с 1980-х.

Но lock без heartbeat бесполезен: если сессия упадёт, файл останется. Поэтому активная сессия обновляет timestamp раз в 30–60 минут. Если lock старше четырёх часов — возможно, сессия мертва.

«Возможно» — не «точно». Перед перехватом проверяю ресурс: SSH, nvidia-smi, git-статус. Только если ресурс свободен — перехватываю. Это принцип «доверяй, но проверяй» из Chubby и ZooKeeper.

Рядом с lock-файлом — JSON с метаданными: slug задачи, session-id, время захвата, описание, список файлов. Новая сессия за секунду видит: «auth-middleware занят под рефакторинг», «GPU 2 занята под тренировку».

Я пробовала автоматизировать lock’и через хук PreToolUse, но отказалась: команды слишком разнообразны, парсер дал бы ложные срабатывания. Правильный порядок — сначала соглашение и файлы, автоматизация — только для устойчивых паттернов.

Слой 3: Почта — «Артём, посмотри мой код»

Handoffs — для передачи контекста во времени. Locks — для ресурсов. Но не хватало синхронизации между живыми сессиями.

Раньше я спрашивала у коллеги: «Ты ещё делаешь middleware?» — он смотрел в свой чат, копировал ответ. Три перекладки. Каждый день.

Нужна прямая коммуникация между сессиями. Чтобы я написала — он ответил, я получила. Без одновременности. С гарантиями доставки.

Я поняла: это email.

Структура:

  • Папка на каждого пользователя: inbox, sent, archive
  • Папка all/ — для рассылок
  • Каждое письмо — markdown-файл с YAML frontmatter: from, to, type, subject, message_id, in_reply_to, status

Пример: сессия ani пишет Артёму:

Ты менял линковку libsodium в CMakeLists? У меня build падает на target_link_libraries.

Через час Артём открывает свой чат. Хук проверяет inbox, видит письмо, подсовывает в контекст. Он отвечает — и ответ попадает в мой inbox.

Цепочки — через in_reply_to, как в RFC 822. Подтверждения — отдельные файлы-квитанции. Рассылка: to: * → папка all/. Дедупликация — через .watcher_state.json. Никакого брокера: всё на хуках.

Каждая фича — копия классического email:

  • Mailbox агентов — Inbox per user
  • Адресация — From / Reply-To
  • Цепочки — In-Reply-To
  • Статус — Read/Unread
  • Аудит — Sent folder
  • Подтверждение — Delivery receipt

Email решает ту же задачу: асинхронная доставка между процессами. Claude Code — новая упаковка старой проблемы.

mclaude: всё вместе

Я собрала три слоя в open-source инструмент — mclaude. Python, без зависимостей в ядре, 193 теста, v0.6.0.

Шесть слоёв:

  • Locks — атомарный захват ресурсов + heartbeat
  • Handoffs — per-session сводки, append-only INDEX
  • Memory Graph — иерархическое хранилище решений и gotchas
  • Identity Registry — имена сессий (ani, artem, nastya)
  • Messages + Active Mail — межсессионная коммуникация с threading
  • Code Indexer — AST-сканер → code-map.md + llms.txt

Code Indexer — новинка v0.6.0. Сканирует проект, создаёт:

  • code-map.md — карта модулей, классов, функций
  • llms.txt — компактный индекс для быстрой загрузки

code-map.md читаем и человеком — формат взят из нашей базы знаний. Новая сессия не тратит время на перечитывание кода.

Ещё 5 хуков: на события Claude Code (SessionStart, PreToolUse, UserPromptSubmit, Stop) и git pre-commit guard, который блокирует коммит в заблокированный файл. MCP-сервер с 20+ инструментами отдаёт структурированный JSON — без парсинга CLI-вывода.

Принцип: hub — ускоритель, не зависимость. Всё работает на файлах. Hub поднят — сообщения через WebSocket. Упал — bridge пишет в .claude/messages/inbox/. Удалил mclaude — файлы остаются читаемыми. Markdown для нарратива, JSON для метаданных.

Чтобы увидеть паттерн за 30 секунд: pip install mclaude && mclaude demo --no-pause. Скрипт запускает две симулированные сессии (ani и vasya), проходит все слои и выводит Mermaid-диаграмму — её можно вставить в PR.

Дыра в экосистеме

Изоляция — решений много: Git worktrees, песочницы, отдельные порты, Anthropic Agent Teams. Всё это избегает общего состояния.

Координация общего состояния — почти пусто. Только claude_code_agent_farm от Dicklesworthstone делает file-based locks. Но это оркестрация 20+ агентов в tmux, а не сценарий многих чатов над одним проектом.

GitHub issue #19364 — запрос на «session lock file». Не реализован. Issue #29217 — .claude.json повреждается при параллельных записях. У одного пользователя — 315 битых бэкапов за 7 дней.

Экосистема зрелая в избегании общего состояния. Пустая в его координации.

Распределённые системы, вид сбоку

Всё это — паттерны из 1980-х:

  • Heartbeat-таймауты для обнаружения мёртвых процессов
  • Lock-файлы с именами по ресурсу
  • Append-only журналы
  • Внешняя проверка перед перехватом устаревшего замка
  • Отдельный файл на ресурс — маленькое окно конфликта

Chubby, ZooKeeper, любая координационная система — всё это использует. AI-агенты — просто новый вид процессов в старой распределённой системе.

Email (RFC 822, 1982) оказался готовым решением для межагентной коммуникации. Не потому что «мы знаем email», а потому что он решает ту же абстрактную задачу: асинхронная доставка между процессами с цепочками и подтверждением.

Каждое поколение переоткрывает: email был прав. В 2010-х — это были очереди (RabbitMQ, Kafka). В 2026-м — AI-агенты.

Реальное использование

Три человека, один C++ проект, 6 параллельных сессий. Каждый работает над своей частью: архитектура, inference, UI.

Один агент решил изменить key derivation в scramble-модуле. Отправил рассылку через mailbox/all/. Другой увидел сообщение — и учёл изменение до начала кода. Без почты — был бы конфликт слияния или повреждение данных.

27 handoffs за 4 дня. 0 повторённых тупиков. Handoff между подписками сэкономил ~4 часа восстановления контекста.

Где сломается и чего не знаю

Масштаб. Тестировалось на 3–6 сессий, 2–3 человека, 1 C++ проект. Что будет при 20+ сессий от 10 агентов — неизвестно. Подозреваю, что .claude/messages/inbox/{user}/ станет узким местом: дедупликация через .watcher_state.json линейна, на сотнях писем будет медленно.

Между машинами без hub. Файловое ядро работает через общий репозиторий: закоммитил — другой увидел при git pull. Задержка — минуты, а не секунды. Hub даёт WebSocket, но требует отдельного процесса. В продакшне не испытан.

Race при взятии lock. O_CREAT | O_EXCL атомарен на локальной ФС. На сетевой (NFS, SMB) — зависит от реализации. При использовании .claude/locks/ на NFS возможна гонка.

Проверка устаревшего lock — полуавтоматическая. Перед перехватом я вручную проверяю nvidia-smi, ps, git-статус. Для полной автоматизации нужен обработчик, знающий типы ресурсов. Пока — человек решает.

Почта без push-доставки. Сейчас письма показываются на UserPromptSubmit — только когда сессия что-то пишет. Настоящая push-доставка (сессия просыпается при письме) — в планах.

Human-in-the-loop нужен. Система не заменяет координатора. Если 6 агентов решат переделать auth-middleware — они договорятся, но кто именно возьмёт задачу, решает человек. Mclaude даёт инструменты, не автономность.

Что дальше

mclaude — open source, MIT. 193 теста, Python 3.9+.

Репозиторий: github.com/AnastasiyaW/mclaude. Принципы: claude-code-config/principles/18.

Что доделываю:

  • Автопробуждение сессии при новом письме. Сейчас хук срабатывает на UserPromptSubmit. Правильно — будить сессию при приходе письма. Будет в ближайшем обновлении.
  • Наблюдение за перепиской команды. Дашборд, который читает .claude/messages/ и показывает ленту: кто кого спрашивает, где затыки. Мониторинг без открытия чатов.
  • Трекер задач для агентской команды. Хаб, где задача ставится конкретному Claude’у коллеги. Видно состояние, можно переключить исполнителя. Почему это не Jira — отдельный разговор.
Читать оригинал