Mehrere Claudes auf einem Repo. Sperren, Übergaben und E-Mail von 1982
Autorin: Anastasiia Butova, Ingenieurin für ComfyUI und Diffusionsmodelle, Belgrad · veröffentlicht 2026-04-23 · aktualisiert 2026-09-23
Übersetzung der englischen Fassung: English
Ich betreibe Claude Code parallel auf drei Pro-Abos, und meine Kollegen betreiben ihre eigenen, oft an denselben Dateien am selben Tag. Dieser Beitrag behandelt die drei Koordinationsprobleme, die sich daraus ergeben haben, in der Reihenfolge, in der sie auftauchten: Kontext von einer Sitzung zur nächsten weitergeben, einer Sitzung exklusive Nutzung einer Ressource geben und zwei laufenden Sitzungen erlauben, miteinander zu sprechen. Jedes davon erwies sich als Muster aus verteilten Systemen, rund vierzig Jahre alt: ein nur anhängbares Protokoll, eine Sperrdatei mit Heartbeat und E-Mail. Am Ende steht, wie die drei Teile als mclaude gepackt sind, wie echte Nutzung aussah und wo es bricht.
Übergaben kamen zuerst, als ich es leid war, jedem neuen Chat den Stand der Dinge neu zu erzählen. Zusammenfassungen kamen, als sich bei langen Projekten achtzig Übergaben pro Projekt angesammelt hatten. Die Post kam, als ich es leid war, die Claude-Status meiner Kollegen über einen Messenger weiterzugeben.
Schicht 1: Übergaben, „ich bin fertig, mach weiter“
Der erste Versuch: alles beim Abschluss einer Sitzung in eine einzige HANDOFF.md schreiben. Das bricht, sobald es mehr als einen Chat gibt. Der letzte Schreiber gewinnt, und alle anderen werden überschrieben.
Also bin ich zu nur anhängendem Schreiben übergegangen. Jede Sitzung schreibt ihre eigene Datei mit einem eindeutigen Namen:
.claude/handoffs/
├── 2026-04-09_14-32_373d1618_drift-validator.md
├── 2026-04-09_16-47_b858f500_dashboard-refactor.md
├── 2026-04-10_11-20_a1c2d3e4_auth-fix.md
└── INDEX.md (append-only log)
Der Name kodiert Datum, Uhrzeit, die ersten acht Zeichen der Sitzungs-ID und einen Aufgaben-Slug. Zwei Chats können nicht denselben Namen erzeugen: Selbst in derselben Sekunde unterscheiden sich ihre Sitzungs-IDs. Die Race Condition ist konstruktionsbedingt unmöglich, ohne Sperren und ohne Broker.
INDEX.md ist ebenfalls nur anhängend: eine neue Zeile unten, nie eine Änderung weiter oben. Um einen Status von ACTIVE auf CLOSED zu setzen, hängst du einen neuen Eintrag an, statt den alten zu bearbeiten. Ein Write-ahead-Log in einer Datenbank funktioniert genau so, und aus demselben Grund: Stirbt ein Prozess mitten im Schreiben, bleibt das Protokoll konsistent.
Eine Übergabe ist kein Tagebuch, sie ist ein Vertrag mit der nächsten Sitzung. Fünf Abschnitte, je eine Frage: das Ziel, was getan wurde (mit Pfaden, damit nichts neu gebaut wird), was NICHT funktioniert hat und warum, der aktuelle Stand (funktioniert, kaputt, blockiert) und eine nächste Aktion. Eine Zeile pro Abschnitt aus einer echten Colour-Checker-Sitzung:
- Ziel: „Colour checker: CNN sweep + diffusion, first results“
- Erledigt: „CNN baseline median 1.99 deg, 11M params, 21 MB“
- Funktioniert nicht: „lr=3e-4, NaN after epoch 10-13, no gradient clipping“
- Kaputt: „Diffusion training OOM at bs=16“
- Nächster Schritt: „Inference script for diffusion + visual sheets“
Den vollständigen Text einer Übergabe aus demselben Projekt findest du in Meine CLAUDE.md hat 582 Zeilen.
Der Abschnitt, der sich bezahlt macht, ist „hat NICHT funktioniert“. In vier Tagen sammelten sich 27 Übergaben an, und keine Sackgasse wurde zweimal betreten, weil jede neue Sitzung die Warnung sah. Eine Übergabe passt in etwa 1.500 Token, gegenüber rund 100K Roh-Sitzungskontext: etwa 67-mal kleiner, und nichts verloren, was für die Fortsetzung zählt.
Das Schreiben ist automatisiert. Ein Stop-Hook prüft, wie lange die Sitzung lief, und fragt, falls sie erheblich war und nichts festgehalten wurde, ob eine Übergabe gespeichert werden soll. Zusätzlich bietet das Modell selbst in langen Sitzungen an, eine zu schreiben, bevor es weitermacht, meist nach einer großen Aufgabe oder wenn der Kontext zu 70 bis 80 % voll ist. Meine Hooks haben keine Längenprüfung dafür; das ist die eigene Entscheidung des Modells. Gelesen wird mit einem Satz zu Beginn der nächsten Sitzung, „übernimm die Übergabe zum Colour Checker“, oder mit einem Link auf die Datei.
Zusammenfassungen, wenn sich Übergaben stapeln
Arbeitest du einen oder zwei Monate an etwas, sammeln sich Dutzende Übergaben an, voll mit bereits gegangenen Sackgassen und bereits widerlegten Hypothesen. Eine neue Sitzung liest die letzten paar, schaut nicht tiefer und schlägt seelenruhig einen Ansatz vor, den ich zwei Wochen zuvor widerlegt habe.
Die Lösung ist, die alten zu falten. Eine Zusammenfassung verdichtet zwanzig frühere Übergaben, markiert das Grenzdatum und versieht jede abgedeckte Übergabe mit einer Zeile rolled_up_into:, die die Zusammenfassungsdatei nennt. Eine neue Sitzung liest eine Zusammenfassung plus nur die Übergaben, die nach ihrem Grenzdatum liegen. Der Rest bleibt für die Historie, geht aber nicht in den Kontext.
Die Zusammenfassung trägt im Dateinamen das Segment rollup_, damit sie in INDEX.md auffällt, und ihr Frontmatter listet auf, was sie abdeckt (das Format aus dem mclaude-README, gekürzt):
---
type: rollup
covers:
- 2026-03-01_12-00_session-01_kickoff
- 2026-03-02_14-30_session-02_auth-refactor
- 2026-03-15_18-00_session-12_ci-pipeline
through: 2026-03-15 18:00
---
Der Text ist eine gewöhnliche Übergabe, die zwanzig Sitzungen statt einer beschreibt. Das Muster ist nicht meins. PavelMuntyan/MF0-1984 hat dasselbe Problem in einem SQLite-Schema mit einer Spalte covered_until_message_id gelöst, und ich habe die Tabelle durch Markdown-Frontmatter ersetzt. Nichts Raffiniertes: die vertraute Zusammenfassung „der bisherigen Kapitel“.
Schicht 2: Sperren, „diese Ressource gehört mir gerade“
Das nächste Problem tauchte bei Trainingsläufen auf Remote-Servern auf. Übergaben werden nur angehängt, deshalb überschreibt niemand jemanden. Echte Ressourcen sind anders. Zwei Sitzungen können nicht gleichzeitig ein Modul umbauen (Merge-Konflikt), nicht gleichzeitig auf Staging deployen (die zweite überschreibt die erste), nicht gleichzeitig auf einer GPU trainieren (die zweite bekommt einen OOM). Eine von ihnen muss die Ressource exklusiv halten.
Eine Datei pro Ressource, nach ihr benannt: auth-middleware.lock, staging-deploy.lock, gpu-host-a_gpu2.lock. Darin steht, wer sie genommen hat, wann und wofür. Eine neue Sitzung listet den Ordner, sieht, was belegt ist, und wählt etwas Freies, ohne mich zu fragen.
Der Trick steckt darin, wie die Datei angelegt wird. os.open(path, O_CREAT | O_EXCL) ist atomar: Existiert die Datei schon, schlägt der Aufruf fehl. Zwei Prozesse können nicht gleichzeitig dieselbe Datei anlegen, weil der Kernel das garantiert. Es ist dieselbe Grundlage, auf der /var/lock und PID-Dateien seit den 1980er-Jahren beruhen.
Eine Sperre ohne Heartbeat überlebt nur bis zum ersten Absturz: Die Sitzung stirbt, die Sperre bleibt, und alle warten ewig. Deshalb aktualisiert nach der Dateikonvention eine aktive Sitzung ihren Zeitstempel alle 30 bis 60 Minuten, und eine Sperre, deren Heartbeat älter als etwa vier Stunden ist, ist möglicherweise tot. Möglicherweise ist nicht sicher. Bevor ich sie übernehme, prüfe ich die Ressource selbst: ps über SSH, nvidia-smi auf der GPU, die Deploy-Warteschlange, git status auf dem Modul, was auch immer zeigt, ob sie wirklich belegt ist oder nur nie freigegeben wurde. Erst dann nehme ich sie. Chubby und ZooKeeper prüfen aus demselben Grund vor einer Übernahme von außen.
Neben jeder Sperre liegen JSON-Metadaten: Aufgaben-Slug, Sitzungs-ID, Zeitpunkt der Belegung, Beschreibung, Dateiliste. Eine neue Sitzung liest .claude/locks/ und sieht in einer Sekunde, dass auth-middleware von ani für einen Umbau gehalten wird, zweite Stunde, und dass GPU 2 mit dem Training von Embeddings belegt ist. Die Frage „was läuft hier eigentlich?“ kommt nicht mehr auf.
Mein erster Instinkt war ein PreToolUse-Hook, der Befehle parst und Ressourcen automatisch als belegt markiert: Ein Befehl berührt Ressource X, also ist X genommen. Sackgasse. Befehle sind zu verschieden, und ein Parser mit regulären Ausdrücken wird zur Maschine für Fehlalarme. Die richtige Reihenfolge war die umgekehrte: erst Konvention und Dateien, Automatisierung später, und nur für Muster, die sich tatsächlich wiederholen.
Schicht 3: Post, „Artem, schau dir meinen Code an“
Übergaben und Zusammenfassungen tragen Kontext durch die Zeit. Sperren regeln Ressourcen. Eine dritte Aufgabe passt in keines von beiden: zwei lebende Sitzungen synchron zu halten.
Ein typischer Tag: Ich ließ Claude eines Kollegen die auth-middleware umbauen und ging in meinem eigenen Chat ein Plug-in schreiben. Eine Stunde später wollte ich wissen, wo er steht und ob er hängt. Die alte Antwort: Ich öffne einen Messenger und schreibe meinem Kollegen, der Kollege schaut in seinen Chat und kopiert den Text zurück. Dreimal Weitergabe durch einen Menschen, jeden Abend, über mehrere Projekte.
Was ich brauchte, waren adressierte Nachrichten zwischen Sitzungen, die vielleicht nie gleichzeitig online sind, mit garantierter Zustellung: Wird eine Nachricht jetzt nicht gelesen, wird sie in der nächsten Sitzung gelesen. Ich habe darüber nachgedacht und erkannt, dass ich E-Mail beschreibe.
.claude/mailbox/
├── ani/
│ ├── inbox/
│ ├── sent/
│ └── archive/
├── artem/
│ └── ...
└── all/ (broadcast)
Jede Nachricht ist eine eigene Markdown-Datei mit YAML-Frontmatter (from, to, type, subject, message_id, in_reply_to, status) und dem Brief darunter. Ein einfacher Fall: Anis Sitzung schickt Artem eine Frage mit dem Betreff „Re: cmake libsodium“ und dem Text „Hast du die Libsodium-Verlinkung in CMakeLists geändert? Mein Build bricht bei target_link_libraries“. Eine Stunde später öffnet Artem Claude Code auf seinem Rechner, ein UserPromptSubmit-Hook prüft sein Postfach vor dem ersten Prompt, sieht die ungelesene Nachricht und legt sie in den Kontext. Er antwortet sofort, und die Antwort landet in meinem Postfach.
Threads laufen über in_reply_to, wie bei RFC 822. Zustellbestätigungen sind eigene Dateien. Eine Nachricht an to: * landet in all/, das alle prüfen. Zur Deduplizierung merkt sich der Hook in .watcher_state.json, welche Nachrichten er schon gezeigt hat, sodass nichts zweimal in den Kontext gerät. Kein Polling, kein Broker, kein Hintergrunddämon: Alles ruht auf einem Hook, der im richtigen Moment feuert. Jede Funktion lässt sich eins zu eins auf klassische E-Mail abbilden:
| E-Mail (RFC 822, 1982) | Agenten-Postfach |
|---|---|
| To / Cc | Adressierung einer bestimmten Sitzung |
| From / Reply-To | Rückadresse für die Antwort |
| Subject | Sortierung, ohne den Text zu lesen |
| In-Reply-To | Nachrichtenstränge |
| Date | wie frisch die Anfrage ist |
| Gelesen / Ungelesen | gesehen oder noch nicht |
| Posteingang pro Benutzer | Warteschlange pro Empfänger |
| Gesendet-Ordner | Prüfpfad des Absenders |
| Zustellbestätigung | Bestätigung ohne „hast du das gesehen?“ |
mclaude: die drei Schichten als ein Werkzeug
Ich habe die Schichten als Open-Source-Werkzeug verpackt, mclaude: Python, keine Abhängigkeiten im Kern, 193 Tests, Version 0.6.0. Wie ich es damals beschrieb, hatte es sechs Schichten, drei Grundschichten und drei später hinzugefügte:
| Schicht | Was sie tut | Codezeilen |
|---|---|---|
| Sperren | atomare Ressourcenbelegung + Heartbeat | 469 |
| Übergaben | Zusammenfassungen pro Sitzung, anhängbares INDEX | 371 |
| Memory-Graph | hierarchischer Speicher für Entscheidungen und Fallstricke | ~400 |
| Identitätsregister | Namen für Sitzungen (ani, artem, nastya) | 246 |
| Nachrichten und Active Mail | Nachrichten zwischen Sitzungen, mit Strängen | 400 + 236 |
| Code-Indexer | AST-Scanner → code-map.md + llms.txt | neu in v0.6.0 |
Der Indexer durchläuft das Projekt und schreibt zwei Dateien für den Agenten: code-map.md, eine Karte der Module, Klassen und Funktionen, und llms.txt, einen kompakten Index zum schnellen Laden. Eine neue Sitzung verbringt ihre ersten Minuten nicht damit, den Code erneut zu lesen, weil die Karte schon im Repository neben den Übergaben liegt.
Darauf kommen fünf Hooks, vier an Claude-Code-Ereignissen (SessionStart, PreToolUse, UserPromptSubmit, Stop) und ein Git-Pre-Commit-Wächter, der einen Commit verweigert, der eine Datei berührt, die jemand anderes gesperrt hat. Ein MCP-Server mit mehr als 20 Werkzeugen liefert strukturiertes JSON direkt in Claude Code, ohne dass CLI-Ausgaben geparst werden müssen.
So sieht eine Sperre auf der Kommandozeile aus, aus dem mclaude-README:
$ mclaude lock claim --slug fix-auth-middleware-race \
--description "Race condition when two requests write the same session key" \
--files src/auth/middleware.py src/auth/session.py
[lock] claimed fix-auth-middleware-race
# a second session tries the same slug
$ mclaude lock claim --slug fix-auth-middleware-race --description "same bug I guess"
[lock] fix-auth-middleware-race already held
by session: 665a44027f424db7
doing: Race condition when two requests write the same session key
Die Entwurfsregel: Der Hub ist ein Beschleuniger, keine Abhängigkeit. Alles funktioniert über Dateien. Läuft der Hub, laufen Nachrichten per WebSocket in Echtzeit. Ist der Hub ausgefallen, schreibt die Brücke in dasselbe .claude/messages/inbox/ im selben Format, und der Empfänger sieht nach git pull genau dasselbe. Entfernt man das mclaude-Paket, bleiben die Dateien in .claude/ lesbar: Markdown für die Erzählung, JSON für die Metadaten.
mclaude demo --no-pause dauert etwa 30 Sekunden. Es lässt zwei simulierte Sitzungen, ani und vasya, durch die Schichten laufen und gibt ein Mermaid-Diagramm aus, was passiert ist, das in einem Pull Request oder einem Beitrag so wie es ist dargestellt wird.
Die Lücke im Ökosystem
Dann sah ich nach, wer sonst dieses Problem löst. Isolation hat viele Antworten: Git-Worktrees (parallel-cc), Sandboxes (Kmux), ein eigener Port pro Agent mit weitergereichten Umgebungsvariablen, Anthropics Agent Teams mit einer Aufgabenwarteschlange und dem Beanspruchen von Aufgaben. Sie alle lösen „Sitzungen stören sich nicht“, indem sie dafür sorgen, dass es keinen gemeinsamen Zustand gibt.
Die Koordination gemeinsamen Zustands war fast leer. Nur Dicklesworthstones claude_code_agent_farm arbeitet mit dateibasierten Sperren, und das ist eine Orchestrierung für 20+ Agenten in tmux, nicht für viele Chats an einem Projekt. Der eigene Tracker von Claude Code zeigt die Lücke von der anderen Seite: #19364, eine Anfrage für eine Sitzungs-Sperrdatei, wurde nie umgesetzt, und #29217 dokumentiert, wie .claude.json durch gleichzeitige Schreibzugriffe mehrerer Sitzungen beschädigt wird.
Das Ökosystem ist ausgereift darin, gemeinsamen Zustand zu vermeiden, und fast leer darin, ihn zu koordinieren.
Nichts davon ist neu. Heartbeat-Timeouts, nach der Ressource benannte Sperrdateien, ein Protokoll, an das man nur anhängt, eine externe Prüfung vor der Übernahme einer veralteten Sperre, eine Datei pro Ressource statt einer gemeinsamen Tabelle: Googles Chubby, ZooKeeper und jedes Koordinationssystem der letzten vierzig Jahre beruhen auf denselben Dingen. KI-Agenten sind eine weitere Art Prozess in einem verteilten System, das wir schon verstehen.
RFC 822 passt aus demselben Grund. E-Mail löst genau dieses abstrakte Problem: asynchrone Zustellung zwischen Prozessen, die vielleicht nicht gleichzeitig aktiv sind, mit Strängen und Zustellbestätigung. Jede Generation verteilter Systeme entdeckt wieder, dass E-Mail richtig war. In den 2010ern waren es Nachrichtenwarteschlangen, RabbitMQ und Kafka. 2026 sind es KI-Agenten.
Echte Nutzung
Drei Personen, ein C++-Plug-in-Projekt, sechs parallele Claude-Code-Sitzungen, jede Person an ihrem eigenen Teil: Architektur, die Inferenzpipeline, die Oberfläche.
Ein Agent entschied, die Schlüsselableitung im Scramble-Modul zu ändern, und schickte eine Rundmail über mailbox/all/. Ein anderer Agent sah sie zu Beginn seiner nächsten Sitzung, bevor er Code schrieb, und berücksichtigte die Änderung. Ohne das Postfach hätte er gegen ein inkompatibles Format geschrieben: bestenfalls ein Merge-Konflikt, im schlimmsten Fall beschädigte Daten.
Übergaben zwischen Abos, Abschluss in einem und Fortsetzung in einem anderen, sparten etwa vier Stunden, die sonst fürs Wiederaufbauen des Kontexts draufgegangen wären.
Wo es bricht, und was ich nicht weiß
- Skalierung. Getestet mit 3 bis 6 parallelen Sitzungen, 2 oder 3 Personen, einem C++-Projekt, dazu eine Menge Sitzungen an einer Website. Ich habe nicht 20+ Sitzungen von 10 Agenten gemessen. Ich vermute, dass das Postfach zuerst bricht: Die Deduplizierung über
.watcher_state.jsonist linear in der Zahl der Nachrichten und wird bei hunderten langsam. - Mehrere Rechner ohne Hub. Der Dateikern synchronisiert sich über ein gemeinsames Repository, deshalb sieht die andere Seite eine Nachricht nach
git pull: Minuten, nicht Sekunden. Der Hub liefert Sekunden über WebSocket, aber er ist ein weiterer Prozess, der laufen muss, und ich habe ihn nicht im Produktivbetrieb getestet. - Race Conditions bei Sperren auf Netzwerkdateisystemen.
O_CREAT | O_EXCList auf einem lokalen Dateisystem atomar. Auf NFS oder SMB hängt die Garantie von der Implementierung ab, wer also.claude/locks/dorthin legt, kann auf eine Race Condition stoßen. - Die Übernahme veralteter Sperren ist halbautomatisch. Die Prüfungen über
nvidia-smi,psundgit statussind für einen Menschen geschrieben. Sie zu automatisieren braucht einen Handler, der für jede Art von Ressource weiß, wie sie zu prüfen ist. - Post weckt keine Sitzung. Mit dem Hook allein erscheinen Nachrichten bei UserPromptSubmit, nur wenn die aktive Sitzung gerade etwas tippt. Echte Push-Zustellung gibt es nicht (siehe das Update unten).
- Ein Mensch bleibt eingebunden (Human in the Loop). Wenn sechs Agenten gleichzeitig entscheiden, dass auth-middleware umgeschrieben werden muss, verhandeln sie per Post, aber wer es übernimmt, bleibt eine menschliche Entscheidung. mclaude gibt die Werkzeuge zum Verhandeln, keine autonome Orchestrierung.
Was sich seit April geändert hat
Update, September 2026. Drei Korrekturen und zwei Ergänzungen, alle am 23. September 2026 geprüft.
- Installation.
pip install mclaude, wie ich es im April schrieb, funktioniert nicht: Das Paket ist nicht auf PyPI veröffentlicht, dort gibt es für den Namen 404. Installiere stattdessen aus einem Klon des Repositorys, wie unten. - Issue #19364. Ich habe es als offene Anfrage beschrieben. Ein Bot hatte es bereits am 28. Februar 2026 als „not planned“ geschlossen, bevor dieser Beitrag erschien. Es wurde nie umgesetzt, und genau das sagt der Abschnitt, aber es war nicht offen.
- Issue #29217. Ich habe „315 kaputte Sicherungen in sieben Tagen“ zitiert. Diese Zahl steht im Thread in dieser Form nicht mehr. Der ursprüngliche Bericht zählt 165 beschädigte Sicherungen in etwa 40 Minuten, und ein Kommentator zählte mindestens 423 Beschädigungen über sieben Tage. Das Issue wurde am 25. Mai 2026 als inaktiv geschlossen.
- Siebte Schicht. Das mclaude-README beschreibt außerdem dateibasierte Heartbeats für Sitzungen, die dieser Beitrag nicht behandelt, und das Paket führt Sperren strenger als die oben beschriebene Konvention: einen Heartbeat alle 30 Sekunden, und eine Sperre gilt nach 3 Minuten Stille als veraltet.
- Post mitten im Gespräch. Der erste Plan vom Ende der russischen Originalfassung wurde noch am selben Tag umgesetzt. Pull Request #5, am 23. April um 17:22 UTC zusammengeführt, einige Stunden nach dem Beitrag, ergänzt ein Skript für das Monitor-Werkzeug von Claude Code: Es fragt das Postfach alle 30 Sekunden ab und übergibt jede neue Nachricht der laufenden Sitzung, ohne dass jemand einen Prompt tippt. Die Grenze „Post weckt keine Sitzung“ gilt also nur für den Aufbau mit reinem Hook.
Der Hauptzweig hat seit dieser Zusammenführung keine Commits mehr. Das MF0-1984-Repository, aus dem die Idee der Zusammenfassungen stammt, liefert jetzt 404.
Ausprobieren
git clone https://github.com/AnastasiyaW/mclaude.git
cd mclaude
pip install -e .
mclaude hooks install --apply
mclaude demo --no-pause
MIT-Lizenz, Python 3.9+. Die Prinzipien dahinter sind separat als Prinzip 18, Koordination mehrerer Sitzungen beschrieben, im Konfigurations-Repository, das auf der Seite codex-claude-code-config vorgestellt wird. Die Projektseite des Werkzeugs ist mclaude. Warum ich mich in meinem eigenen Repository auf Übergaben verlasse statt auf den Sitzungsverlauf der Desktop-App, steht in 45 MB Claude-Code-Sitzungen, die du nie ansiehst.
Das russische Original erschien auf Habr.