Multiplayer — Identität, Mitgliedschaft und Präsenz
Control Center funktioniert für eine einzelne Person, das Modell darunter ist aber von Anfang an auf mehrere Akteure angelegt. Menschen und Agenten schreiben in denselben Bereichen, bearbeiten dieselben Tickets und stehen auf derselben Liste. Sicher und nachvollziehbar wird das durch drei Entscheidungen: wer als Akteur zählt, was den Zugang begrenzt und welcher Zustand dauerhaft geteilt und welcher nur live verteilt wird.
Prinzipale: Nutzer und Agenten sind gleichrangige Akteure
Abschnitt betitelt „Prinzipale: Nutzer und Agenten sind gleichrangige Akteure“Alles, was festhält, „wer das getan hat“ — Nachrichten, Tickets, Reviews,
Pläne, Laufprotokolle, Audit-Einträge — läuft über einen Prinzipal, eine
sealed Union aus UserPrincipal und AgentPrincipal mit einer kompakten
Drahtform (user:<id> / agent:<id>). Die Zuschreibung fasst nie N Menschen
(oder N Agenten) in einem Stellvertreterkonto zusammen. Ein Review eines
Agenten und ein Review einer Person bleiben überall unterscheidbar, wo
sie auftauchen.
Weil der Prinzipal die Einheit der Identität ist, ist er auch die Einheit der Begrenzung:
- Ratenbegrenzungen gelten pro Prinzipal, nicht pro Sitzung. Der Überlastschutz für entfernte Werkzeugaufrufe rechnet pro Nutzer, nicht pro Verbindung: Alle Geräte eines Mitglieds zehren von einem gemeinsamen gleitenden Fenster (120 Aufrufe pro Minute, davon dürfen höchstens 30 mutieren). Drei offene Geräte verdreifachen also niemandes Kontingent, und ein lautes Mitglied kann die übrigen nicht leer laufen lassen.
- Rückgängig gilt pro Prinzipal. Das Aktionsjournal eines Clients zeichnet nur die Mutationen auf, die sein Prinzipal ausgeführt hat. Deine eigene Ticketbearbeitung nimmst du mit ⌘Z zurück; die eines Teammitglieds nicht — bis du sie siehst, hat die dauerhafte Spur sie schon aufgelöst (siehe unten).
Die Mitgliedschaft ist die Zugriffsgrenze — nicht der Kopplungsschlüssel
Abschnitt betitelt „Die Mitgliedschaft ist die Zugriffsgrenze — nicht der Kopplungsschlüssel“Zwei getrennte Zugangsdaten beantworten zwei getrennte Fragen. Der Kopplungsschlüssel (siehe Fernsteuerung und Mobil) authentifiziert ein Gerät gegenüber dem Server. Die Mitgliedschaft berechtigt den Nutzer dieses Geräts, in einem Arbeitsbereich zu handeln. Ein gekoppeltes Gerät, dessen Nutzer in einem Arbeitsbereich keine Mitgliedschaft hat, kann ihn weder lesen noch verändern.
Die Mitgliedschaft ist eine Zeile pro (Arbeitsbereich, Nutzer), gespeichert in der eigenen Datenbankdatei dieses Arbeitsbereichs. „Wer ist in diesem Arbeitsbereich?“ ist damit ein gewöhnliches, begrenztes Lesen. Die umgekehrte Frage, „in welchen Arbeitsbereichen bin ich?“, spannt dagegen zwangsläufig mehrere Dateien auf und gehört zu den wenigen erlaubten arbeitsbereichsübergreifenden Fan-outs. Genau das führt die Arbeitsbereichsauswahl aus, bevor irgendein Arbeitsbereich gewählt ist.
Jede Zeile trägt eine Rolle aus einer fünfstufigen, nach Rang geordneten Leiter:
| Rolle | Darf |
|---|---|
owner |
Volle Kontrolle, einschließlich des Löschens des Arbeitsbereichs; genau eine pro Arbeitsbereich |
admin |
Mitglieder, Einladungen, Einstellungen und zerstörende Operationen verwalten |
member |
Alltag mit Lesen und Schreiben: Nachrichten, Tickets, Agentenläufe |
viewer |
Nur lesen im ganzen Arbeitsbereich; Ausschlussmuster für Geheimnisse blenden passende Pfade auf Oberflächen aus, die Code zeigen |
guest |
Dieselbe Nur-Lesen-Untergrenze wie der Betrachter, auf der untersten Stufe der Leiter |
Jede Zugriffsentscheidung läuft auf „hat die Rolle des Aufrufers mindestens
X“ hinaus. Die Untergrenze ergibt sich aus der Art der Operation, sofern sie
keine ausdrücklich nennt: Ein Lesen braucht guest, eine Mutation member,
eine zerstörende Operation admin. ACLs pro Objekt jenseits der Freigaben
pro Repo weiter unten sind bewusst nicht vorgesehen.
Einladungen vergeben die Mitgliedschaft, unter Einstellungen →
Arbeitsbereich → Mitglieder. Ein Admin stellt einen Einmalcode aus (als Link
oder QR), der standardmäßig nach sieben Tagen abläuft. Der Server speichert
nur den SHA-256-Hash des Codes, und der Code selbst wird genau einmal
angezeigt. Beim Einlösen legt der Server den Nutzer an, hält die
Mitgliedschaft in der Rolle der Einladung fest (nie owner — die
Eigentümerschaft wird ausdrücklich übertragen, nie per Einladung vergeben)
und übergibt an die Gerätekopplung. Die nach der Kopplung ausgegebenen
Gerätezugangsdaten sind selbst auf 30 Tage befristet, eine Kopplung ist also
kein dauerhafter Schlüssel.
Freigaben pro Repo liegen unter der Mitgliedschaft. Weil der Server
vollständige Checkouts hält, darf die Mitgliedschaft im Arbeitsbereich allein
nie mehr Rechte geben als der Code-Host: Jede Oberfläche, die Code zeigt —
Dateien, Diffs, PR-Review, Codesuche — prüft die Freigabe des Mitglieds für
dieses Repo. Freigaben gelten pro (Arbeitsbereich, Nutzer, Repo) und sind
gestuft none → read → review → write. write heißt, Agentenläufe
anzustoßen, die in das Repo schreiben. Fehlt eine Zeile, gilt none.
Eigentümer und Admins halten implizit write auf jedem verknüpften Repo und
tragen gar keine Zeilen.
Zwei Spuren des gemeinsamen Zustands
Abschnitt betitelt „Zwei Spuren des gemeinsamen Zustands“Control Center teilt gemeinsamen Zustand in zwei Spuren mit entgegengesetzten Regeln zur Haltbarkeit, weil die beiden Arten von Zustand auf entgegengesetzte Weise fehlschlagen.
Die flüchtige Präsenzspur
Abschnitt betitelt „Die flüchtige Präsenzspur“Präsenz — wer online ist, wo die Person gerade ist, was sie tippt, was ein
Agent gerade tut — ist in dem Moment wertlos, in dem sie veraltet. Wäre sie
gespeichert, müsste jede neue Verbindung „die Datenbank sagt, Sam bearbeitet
dieses Ticket“ mit der Wirklichkeit abgleichen. Präsenz wird deshalb nie in
die Datenbank geschrieben. Sie lebt in einem Präsenz-Hub im Speicher
des Servers: Clients veröffentlichen mit presence.update, andere
abonnieren mit presence.watch, und Einträge verfallen automatisch nach
drei verpassten Heartbeats (Heartbeats laufen alle 10 Sekunden; der Hub räumt
alle 5 auf). Abzugleichen gibt es nichts, nur etwas auslaufen zu lassen.
Zwei Eigenschaften halten die Spur verlässlich:
- Die Identität kommt aus der Sitzung, nie aus Client-Argumenten. Der
Server bildet das authentifizierte Gerät der Verbindung auf eine Nutzer-ID
ab. Ein Client kann immer nur die eigene Präsenz aktualisieren, niemand
kann also Status oder Locus einer anderen Person vortäuschen. Ein
ausdrückliches
offline-Update (Trennung der Verbindung oder der Schalter „Nicht stören“) entfernt den Eintrag sofort. - Die Liste läuft über den Hub auf dem Server. Teilnehmende tauschen Präsenz nie direkt aus, jede Person sieht also dieselbe Liste, unabhängig von der Topologie. Agenten haben keinen Client, der Server setzt ihre Einträge deshalb aus dem Live-Zustand der Läufe zusammen — denkt nach, läuft, bei einer Freigabe blockiert, fertig. Ein einzelner repräsentativer Lauf legt Zustand und Locus eines Agenten fest, damit parallele Läufe die Liste nicht hin- und herspringen lassen.
Die Verteilung ist je Konsument gestuft: Desktop und Web liegen auf der vollen Stufe (höchstens eine Listenausgabe alle 100 ms), das Smartphone auf einer Zusammenfassungsstufe (alle 500 ms), damit Updates im Takt des Cursors die PWA nicht überlasten.
Der Locus einer Person — wo sie gerade ist, bis hin zum Artefakt — ist
eine sealed Union: Space, File (Repo + Pfad + optionale Zeile), Pr,
Ticket, PlanNode. Nicht jeder Locus hat ein Sprungziel: Eine Route für
einen Dateibetrachter gibt es nicht, ein File-Locus erscheint deshalb als
Beschriftung und nicht als Ziel. Die Spur trägt außerdem weiche
Ansprüche („Sam bearbeitet dieses Ticket“, „ein Agent hält diesen
Worktree“): Konflikte werden sichtbar, gesperrt wird nichts.
Unbekannte Locus-Arten werden zu null geparst, ein alter Client stellt also
einfach nicht dar, was er nicht versteht.
Die dauerhafte gemeinsame Spur
Abschnitt betitelt „Die dauerhafte gemeinsame Spur“Datensätze, die überdauern müssen — Tickets, das gemeinsame Notizen-Dokument des Bereichs, Nachrichten — nehmen den entgegengesetzten Weg: autoritativ, geordnet und gespeichert. Gleichzeitige Bearbeitungen lösen sich nach Last-Writer-Wins in der Eingangsreihenfolge des Servers auf, nie nach Uhren der Clients. Die Auflösung ist bewusst feinkörnig: Ticketbearbeitungen gelten pro Spalte, zwei Personen, die verschiedene Felder desselben Tickets bearbeiten, landen also beide, und keine überschreibt die andere (Workflow-Übergänge behalten stattdessen die optimistische Sperre). Das Notizen-Dokument des Bereichs ist eine Spalte, das ganze Dokument ist also LWW, und weiche Ansprüche auf der Präsenzspur machen gleichzeitiges Bearbeiten sichtbar, statt es zu sperren. Bewusst kein CRDT: Die Beweislast liegt beim CRDT, und LWW nach Eingangsreihenfolge plus sichtbare Ansprüche deckt die tatsächlichen Muster der Zusammenarbeit ab.
„Letzter Schreiber“ heißt Eingangsreihenfolge des Servers, nie eine Uhr auf dem Client. Ein Laptop mit versetzter Uhr kann eine Bearbeitung nicht gewinnen, die er verloren hat. Hinter der dauerhaften Spur sitzt der Sync-Feed: SQLite-Trigger hängen jede Mutation innerhalb der schreibenden Transaktion an ein Änderungsprotokoll pro Arbeitsbereich und vergeben in derselben Transaktion wie die Mutation selbst eine monotone Sequenznummer pro Arbeitsbereich. Ein Dienst auf dem Server liest dieses Protokoll fortlaufend in versionierte Delta-Frames in Sequenzreihenfolge aus. Clients gehen von vollständigen Snapshots zu Deltas über. Eine Lücke in der Sequenz setzt diesen Speicher auf den Snapshot-Modus zurück, statt Frames falsch anzuwenden.
Folgen, lenken, übernehmen, zurückgeben
Abschnitt betitelt „Folgen, lenken, übernehmen, zurückgeben“Die Präsenzspur ist nicht nur eine Liste. Sie treibt die Verben der Zusammenarbeit:
- Folgemodus. Klicke eine Person an, und dein Client fährt ihren Locus mit und navigiert, wie sie navigiert. Folgen gilt „bis du selbst handelst“: Sobald du aus eigenem Antrieb navigierst, löst sich das Folgen. Ein Banner, das sich schließen lässt, meldet, wenn eine andere Person einen Bereich präsentiert, in dem du gerade bist.
- Lenken. Eine Nachricht an einen laufenden Agenten wird als Lenken eingereiht. Der Agent liest sie erneut, bevor er seinen nächsten Zug aufnimmt, du kannst einen Lauf also mitten in der Arbeit umlenken, ohne ihn abzubrechen.
- Übernehmen. Die Kontrolle über eine Unterhaltung zu übernehmen pausiert jeden aktiven Lauf an seiner nächsten sauberen Zuggrenze (ein Lauf von Claude Code, der keine sichere Grenze hat, wird gestoppt), hinter einem Git-Snapshot pro Zug. Die Übernahme lässt sich damit zurücknehmen. Eine dauerhafte Markierung blockiert neue Ausführungen in den Bereich und überlebt einen Neustart des Servers. Der Server setzt nie von selbst in eine halb fertige Bearbeitung eines Menschen fort. Ein lebendiger Bearbeiter pro Worktree ist ein weicher Anspruch: Eine zweite Übernahme ist sichtbar und wird abgelehnt, still zusammengeführt wird sie nicht.
- Zurückgeben. Die Kontrolle zurückzugeben stellt eine strukturierte Diff-Zusammenfassung des Worktrees in den Bereich, reiht sie als Lenken für die pausierten Läufe ein, setzt sie fort und löscht die Markierung.
Der Autonomieregler
Abschnitt betitelt „Der Autonomieregler“Lenken und Übernehmen sind Eingriffe im Nachhinein. Der Autonomieregler ist die dauerhafte Einstellung: pro Bereich, pro Agent, gesetzt aus der Kopfzeile des Bereichs. Er entscheidet, was geschieht, wenn ein Agent eine gegatterte Aktion erreicht.
| Stufe | Drahtwert | Wirkung auf einen gegatterten Werkzeugaufruf |
|---|---|---|
| Nur vorschlagen | proposeOnly |
Wird rundweg abgelehnt, mit einem Grund, den der Agent weitergeben soll — beschreiben kann er die Aktion, ausführen nicht |
| Mit Genehmigung handeln | actWithApproval |
Das im Fehlerfall geschlossene Freigabetor: Eine Bestätigung erscheint bei einem verbundenen Operator, und ohne zuständige Person heißt das Ablehnung |
| Frei handeln | actFreely |
Vorab freigegeben, außer wo eine harte deny-Regel der Schutzregeln greift — eine Deny-Regel gewinnt immer |
| (nicht gesetzt) | null |
Wie „Mit Genehmigung handeln“: der Standard des Servers |
Zwei Dinge stehen in beide Richtungen außerhalb des Reglers. Das eigene
Ausgabe-Verb eines Modus ist fest verankert und lässt sich nie ablehnen, egal
was der Regler sagt. Der Orchestrierungsmodus konnte einmal strukturell sein
einziges Lieferergebnis nicht ausgeben, weil eine Voreinstellung die
Wirkungsklasse abgelehnt hat, die dieses Verb deklariert. Und ein hartes
deny aus den Schutzregeln für Aktionen lässt sich durch actFreely nicht
überstimmen.
Keine Regression im Alleinbetrieb
Abschnitt betitelt „Keine Regression im Alleinbetrieb“All das kommt hinzu. Der Ablauf einer einzelnen Person bleibt unverändert: Bei einem Menschen und ohne Agenten hat der Präsenz-Hub nichts zu verteilen, die Spur gibt also ihren ersten Snapshot aus und liegt danach bei null Verkehr still. Die LWW- und Sync-Maschinerie ist derselbe Einzel-Schreiber-Pfad wie immer, und der Kopplungsschlüssel bleibt das Gerätezugangsdatum, das er immer war. Die Mitgliedschaft ist bereits die Zugriffsgrenze. Bei einem Menschen gibt es eine einzelne Eigentümerzeile und keine Oberfläche der Anwesenheitsliste.
Verwandte Konzepte
Abschnitt betitelt „Verwandte Konzepte“- Arbeitsbereiche und Isolation: die Grenze, die die Mitgliedschaft durchsetzt
- Fernsteuerung und Mobil: Kopplungsschlüssel und Gerätezugangsdaten als die andere Seite der Identität
- Das Agentenmodell: die Agentenhälfte der Prinzipal-Union
- Lebenszyklus der Agenten-Ausführung: die Läufe, die Lenken und Übernehmen pausieren und fortsetzen
- Schutzregeln: die Richtlinie der Wirkungsklassen, mit der der Autonomieregler zusammenspielt
- Domain-Ereignisse: der Bus, auf dem Live-Updates fahren