Přeskočit na obsah

Multiplayer — identita, členství a přítomnost

Control Center funguje i pro jednoho operátora, ale model pod ním je od začátku pro víc aktérů. Lidé a agenti píší zprávy ve stejných prostorech, upravují stejné tickety a objevují se na stejné soupisce. Bezpečnost a srozumitelnost stojí na třech rozhodnutích: kdo se počítá jako aktér, co hlídá přístup a který stav se sdílí trvale a který se jen živě vysílá.

Principálové: uživatelé a agenti jsou rovnocenní aktéři

Sekce “Principálové: uživatelé a agenti jsou rovnocenní aktéři”

Všechno, co zaznamenává „kdo to udělal“ (zprávy, tickety, revize, plány, záznamy běhů, auditní záznamy), se vyřeší přes principála, zapečetěné sjednocení UserPrincipal a AgentPrincipal s kompaktním tvarem na drátě (user:<id> / agent:<id>). Atribuce nikdy neslije N lidí (ani N agentů) do jednoho zástupného účtu, takže revize od agenta a revize od člověka se dají rozlišit všude, kde se ukážou.

Protože je principál jednotkou identity, je i jednotkou omezení:

  • Limity četnosti platí na principála, ne na relaci. Pojistka proti záplavě u vzdálených volání nástrojů rozpočítává podle uživatele, ne podle spojení: všechna zařízení člena čerpají z jednoho sdíleného posuvného okna (120 volání za minutu, z nichž nejvýš 30 smí mutovat). Otevření tří zařízení tedy nikomu neztrojnásobí příděl a jeden hlučný člen nepřipraví ostatní o jejich příděl.
  • Vrácení zpět platí na principála. Deník akcí každého klienta zapisuje jen mutace, které provedl jeho principál. Vlastní úpravu ticketu vrátíš přes ⌘Z. Úpravu kolegy ne: než ji uvidíš, trvalá dráha už ji vyřešila (viz níže).

Členství je hranice přístupu, ne párovací klíč

Sekce “Členství je hranice přístupu, ne párovací klíč”

Dva různé údaje odpovídají na dvě různé otázky. Párovací klíč (viz Vzdálené ovládání a mobil) ověřuje zařízení vůči serveru. Členství opravňuje uživatele toho zařízení jednat uvnitř pracovního prostoru. Spárované zařízení, jehož uživatel v pracovním prostoru členství nemá, ten prostor nesmí ani číst, ani měnit.

Členství je jeden řádek na dvojici (pracovní prostor, uživatel), uložený v databázovém souboru toho pracovního prostoru. „Kdo je v tomhle pracovním prostoru?“ je tak obyčejné čtení v rozsahu. Opačná otázka, „ve kterých pracovních prostorech jsem?“, nutně sahá přes soubory a patří k těm pár povoleným čtením napříč pracovními prostory. Přesně to spouští výběr pracovního prostoru, než je nějaký vybraný.

Každý řádek nese roli z pětiúrovňového žebříčku seřazeného podle hodnosti:

Role Smí
owner Plná kontrola včetně smazání pracovního prostoru; v každém právě jeden
admin Správa členů, pozvánek, nastavení a destruktivních operací
member Běžné čtení a zápis: zprávy, tickety, běhy agentů
viewer Jen čtení v celém pracovním prostoru; globy pro vyloučení tajemství skryjí odpovídající cesty na plochách, které nesou kód
guest Stejná spodní hranice jen pro čtení jako u prohlížeče, na nejnižším stupni žebříčku

Každé rozhodnutí o přístupu se scvrkne na „je role volajícího alespoň X“. Spodní hranice se odvodí z druhu operace, pokud si ji operace neřekne výslovně: čtení vyžaduje guest, mutace member, destruktivní operace admin. ACL po objektech, která by šla dál než přidělení na repozitář níže, jsou schválně mimo rozsah.

Pozvánky jsou způsob, jakým se členství uděluje, z Nastavení → Pracovní prostor → Členové. Admin vystaví jednorázový kód (jako odkaz nebo QR), který ve výchozím stavu vyprší za sedm dní. Server uloží jen hash SHA-256 a kód samotný ukáže právě jednou. Uplatnění kódu uživatele založí až v tu chvíli (JIT), zapíše členství v roli pozvánky (nikdy owner: vlastnictví se předává výslovně, pozvánkou se neuděluje) a předá párování zařízení. Přihlašovací údaje zařízení vystavené po párování mají samy platnost omezenou na 30 dní, takže párování není trvalý klíč.

Přidělení na repozitář sedí pod členstvím. Protože server drží plné checkouty, samotné členství v pracovním prostoru nesmí nikdy překročit práva na hostiteli kódu: každá plocha, která nese kód (soubory, diffy, revize PR, hledání v kódu), kontroluje přidělení člena na tom repozitáři. Přidělení je na trojici (pracovní prostor, uživatel, repozitář) a jde v řadě none → read → review → write, kde write znamená řídit běhy agentů, které do repozitáře zapisují. Chybějící řádek znamená none. Vlastníci a admini mají na každém propojeném repozitáři implicitně write a žádné řádky nenesou.

Dvě dráhy stavu spolupráce

Sekce “Dvě dráhy stavu spolupráce”

Control Center dělí sdílený stav do dvou drah s opačnými pravidly trvanlivosti, protože oba druhy stavu selhávají opačně.

Prchavá dráha přítomnosti

Sekce “Prchavá dráha přítomnosti”

Přítomnost (kdo je online, kde je, co píše, co agent dělá právě teď) je k ničemu v okamžiku, kdy zastará. Kdyby se ukládala, každé opětovné připojení by muselo srovnat „databáze říká, že Sam upravuje tenhle ticket“ se skutečností. Přítomnost se proto do databáze nikdy nezapisuje. Žije v paměti, v hubu přítomnosti na serveru: klienti publikují přes presence.update, ostatní se přihlašují k odběru přes presence.watch a záznamy samy vyprší po třech zmeškaných heartbeatech (heartbeat jde každých 10 sekund, hub je sbírá každých 5). Není co srovnávat. Záznam se nechá vypršet.

Dráhu drží důvěryhodnou dvě vlastnosti:

  • Identita plyne z relace, nikdy z argumentů klienta. Server namapuje ověřené zařízení spojení na id uživatele. Klient smí aktualizovat jen vlastní přítomnost, takže nikdo nepodvrhne stav ani lokus jiného účastníka. Výslovná aktualizace offline (odpojení nebo přepínač neviditelnosti) záznam odebere hned.
  • Soupiska jde přes hub na serveru. Účastníci si přítomnost přímo nevyměňují, takže každý vidí stejnou soupisku bez ohledu na topologii. Agenti nemají klienta, takže server jejich záznamy sestavuje ze živého stavu běhu (přemýšlí, běží, blokováno na bráně schválení, hotovo). Jeden reprezentativní běh určí stav a lokus agenta, aby souběžné běhy soupiskou nekmitly.

Rozesílání je odstupňované podle spotřebitele: počítač a web jedou na plné úrovni (nejvýš jedno vyslání soupisky za 100 ms), telefon na souhrnné (za 500 ms), aby aktualizace v rytmu kurzoru PWA neroztavily.

Lokus účastníka, tedy kde právě je, až po artefakt, je zapečetěné sjednocení: Space, File (repozitář + cesta + volitelný řádek), Pr, Ticket, PlanNode. Ne každý lokus má kam skočit. Trasa pro prohlížení souborů neexistuje, takže se lokus File vykreslí jako popisek, ne jako cíl. Dráha nese i měkké nároky („Sam upravuje tenhle ticket“, „agent drží tenhle worktree“): viditelnost konfliktu, ne zámky. Neznámé druhy lokusu se parsují na null, takže starý klient prostě nevykreslí to, čemu nerozumí.

Trvalá sdílená dráha

Sekce “Trvalá sdílená dráha”

Záznamy, které musí přežít (tickety, sdílený dokument Poznámky v prostoru, zprávy), jdou opačnou cestou: jsou autoritativní, seřazené a uložené. Souběžné úpravy vyhrává poslední zápis v pořadí přijetí na serveru, nikdy hodiny klienta. Rozlišení je schválně jemné. Úpravy ticketu jdou po sloupcích, takže když dva lidé upraví různá pole téhož ticketu, uloží se obě a jedna druhou nepřepíše (přechody workflow si nechávají optimistické zamykání). Dokument Poznámky v prostoru je jeden sloupec, takže celý dokument je LWW a měkké nároky na dráze přítomnosti dělají souběžnou úpravu viditelnou místo zamčené. Schválně to není CRDT. Důkazní břemeno leží na CRDT. LWW podle pořadí přijetí spolu s viditelnými nároky pokryje skutečné vzorce spolupráce.

„Poslední zapisující“ znamená pořadí přijetí na serveru, nikdy hodiny klienta. Notebook s posunutými hodinami nevyhraje úpravu, kterou prohrál. Za trvalou dráhou sedí synchronizační feed. Triggery SQLite připíšou každou mutaci do protokolu změn daného pracovního prostoru uvnitř zapisující transakce a ve stejné transakci jako mutace přidělí monotónní pořadové číslo pracovního prostoru. Služba na serveru ten protokol sleduje a v pořadí posloupnosti z něj skládá verzované delta rámce. Klienti přejdou z plných snímků na delty. Mezera v posloupnosti vrátí dané úložiště do režimu snímku, místo aby rámce použilo špatně.

Sledovat, usměrnit, převzít, vrátit

Sekce “Sledovat, usměrnit, převzít, vrátit”

Dráha přítomnosti není jen soupiska. Pohání slovesa spolupráce:

  • Režim sledování. Klikni na účastníka a klient pojede po jeho lokusu a přechází s ním. Sledování trvá „dokud nezasáhneš“: jakmile přejdeš z vlastní vůle, odpojí se. Zavíratelný pruh oznámí, když jiný účastník prezentuje prostor, ve kterém jsi.
  • Usměrnit. Zpráva běžícímu agentovi se zařadí do fronty jako usměrnění. Agent si ji před dalším tahem znovu přečte, takže běh uprostřed postrčíš, aniž bys ho ukončil.
  • Převzít. Převzetí konverzace pozastaví každý aktivní běh na nejbližší čisté hranici tahu (běh Claude Code, který bezpečnou hranici nemá, se zastaví), a to za git snímkem po každém tahu, takže převzetí jde vrátit. Trvalá značka blokuje nová odeslání do prostoru a přežije restart serveru. Server nikdy sám neobnoví běh doprostřed nedokončené lidské úpravy. Jeden živý editor na worktree je měkký nárok: druhé převzetí je vidět a odmítne se, tiše se nesloučí.
  • Vrátit. Vrácení řízení zveřejní do prostoru strukturovaný souhrn diffu worktree, zařadí ho pozastaveným běhům jako usměrnění, běhy obnoví a značku smaže.

Usměrnění a převzetí jsou zásahy až potom. Volič autonomie je trvalé nastavení, na prostor a na agenta, ze záhlaví prostoru. Rozhoduje, co se stane, když agent dojde k hlídané akci.

Úroveň Hodnota na drátě Účinek na hlídané volání nástroje
Jen navrhovat proposeOnly Zamítnuto rovnou, s důvodem, který má agent předat. Akci umí popsat, ale neprovést
Jednat se schválením actWithApproval Brána schválení uzavřená při selhání: jedno potvrzení se ukáže připojenému operátorovi a žádný schvalovatel znamená zamítnutí
Jednat volně actFreely Předschváleno, kromě místa, kde platí tvrdé pravidlo guardrailu deny. Pravidlo zamítnutí vyhraje vždy
(nenastaveno) null Stejné jako jednat se schválením: výchozí stav serveru

Mimo volič leží dvě věci, na obě strany. Výstupní sloveso režimu je připnuté a volič ho nikdy nesmí zamítnout, ať je nastavený jakkoli. Režim Orchestrace kdysi strukturálně neuměl vydat svůj jediný výstup, protože předvolba zamítla třídu účinku, kterou to sloveso deklaruje. A tvrdé deny z guardrailů akcí se přes actFreely přebít nedá.

Tohle všechno je navíc. Postup operátora, který pracuje sám, se nemění. Při jednom člověku a bez agentů nemá hub přítomnosti co vysílat, takže dráha pošle úvodní snímek a pak stojí při nulovém provozu. Stroj LWW a synchronizace je pořád ta samá cesta jednoho zapisujícího. Párovací klíč zůstává přihlašovacím údajem zařízení, kterým byl vždy. Členství už je hranice přístupu. Při jednom člověku je jeden řádek vlastníka a pruh přítomnosti se neukáže.