Multiplayer – identitet, medlemskap och närvaro
Control Center fungerar för en enskild operatör, men modellen under den är multiaktörs från början. Människor och agenter skickar meddelanden i samma ytor, redigerar samma ärenden och syns i samma personallista. Att göra det säkert och begripligt kokar ner till tre beslut: vem som räknas som aktör, vad som grindar åtkomsten och vilket tillstånd som delas varaktigt kontra bara sänds ut live.
Principaler: användare och agenter är jämbördiga aktörer
Avsnitt med rubriken “Principaler: användare och agenter är jämbördiga aktörer”Allt som registrerar ”vem gjorde detta” – meddelanden, ärenden, granskningar,
planer, körningsloggar, revisionsposter – går genom en Principal, en
förseglad union av UserPrincipal och AgentPrincipal med en kompakt
trådform (user:<id> / agent:<id>). Tillskrivningen slår aldrig samman N
människor (eller N agenter) till ett enda sentinelkonto, så en granskning från
en agent och en granskning från en person går att skilja åt överallt där de
dyker upp.
Eftersom principalen är identitetsenheten är den också inneslutningsenheten:
- Hastighetsgränser gäller per principal, inte per session. Översvämningsskyddet för fjärranrop till verktyg budgeteras per användare, inte per anslutning: alla en medlems enheter hämtar från ett gemensamt glidande fönster (120 anrop i minuten, varav högst 30 får mutera), så att öppna tre enheter tredubblar inte någons kvot och en stökig medlem kan inte svälta ut de andra.
- Ångra gäller per principal. Varje klients åtgärdsjournal registrerar bara de mutationer som dess principal utförde. Du kan ⌘Z din egen ärenderedigering; du kan inte ångra en kollegas – när du ser den har den varaktiga banan redan löst den (se nedan).
Medlemskapet är åtkomstgränsen – inte parkopplingsnyckeln
Avsnitt med rubriken “Medlemskapet är åtkomstgränsen – inte parkopplingsnyckeln”Två separata autentiseringsuppgifter svarar på två separata frågor. Parkopplingsnyckeln (se Fjärrstyrning och mobil) autentiserar en enhet mot servern. Medlemskapet auktoriserar den enhetens användare att agera inuti en arbetsyta. En parkopplad enhet vars användare inte har något medlemskap i en arbetsyta kan varken läsa eller mutera den.
Medlemskapet är en rad per (arbetsyta, användare), lagrad i arbetsytans egen databasfil – så ”vem är med i den här arbetsytan?” är en vanlig avgränsad läsning, medan den omvända frågan, ”vilka arbetsytor är jag med i?”, nödvändigtvis spänner över filer och är en av de få sanktionerade utskickningarna mellan arbetsytor. Det är vad arbetsyteväljaren kör innan någon arbetsyta har valts.
Varje rad bär en roll från en rangordnad stege med fem nivåer:
| Roll | Får |
|---|---|
owner |
Full kontroll, inklusive borttagning av arbetsytan; exakt en per arbetsyta |
admin |
Hantera medlemmar, inbjudningar, inställningar, destruktiva operationer |
member |
Vardaglig läs- och skrivåtkomst: meddelanden, ärenden, agentkörningar |
viewer |
Skrivskyddad i hela arbetsytan; globmönster för hemlighetsundantag döljer matchande sökvägar på kodbärande ytor |
guest |
Samma skrivskyddade golv som visare, på stegens lägsta rang |
Varje åtkomstbeslut kokar ner till ”har anroparens roll minst X”. Golvet härleds
från operationens slag om den inte deklarerar ett eget: en läsning kräver
guest, en mutation kräver member, en destruktiv operation kräver admin.
ACL:er per objekt utöver behörigheterna per repo nedan ligger medvetet utanför
omfattningen.
Inbjudningar är hur medlemskap beviljas, från Inställningar → Arbetsyta →
Medlemmar. En admin skapar en engångskod (som länk eller QR) som som standard
går ut efter sju dagar; servern lagrar bara kodens SHA-256-hash och själva
koden visas exakt en gång. Att lösa in den JIT-provisionerar användaren,
registrerar medlemskap med inbjudningens roll (aldrig owner – ägarskap
överförs uttryckligen och beviljas aldrig via inbjudan) och lämnar över till
enhetsparkopplingen. Enhetsuppgifter som skapas efter parkopplingen är själva
tidsbegränsade till 30 dagar, så en parkoppling är inte en permanent nyckel.
Repo-behörigheter ligger under medlemskapet. Eftersom servern har fulla
utcheckningar får ett arbetsytemedlemskap ensamt aldrig ge högre privilegier än
forge-tjänsten: varje kodbärande yta – filer, diffar, PR-granskning, kodsökning
– kontrollerar medlemmens behörighet på det repot. Behörigheter är per
(arbetsyta, användare, repo) och rangordnas none → read → review →
write, där write innebär att driva agentkörningar som skriver till repot.
Avsaknad av en rad betyder none; ägare och admins har implicit write på
varje länkat repo och bär inga rader alls.
Två banor för samarbetstillstånd
Avsnitt med rubriken “Två banor för samarbetstillstånd”Control Center delar upp delat tillstånd i två banor med motsatta varaktighetsregler, eftersom de två slagen av tillstånd misslyckas på motsatta sätt.
Den flyktiga närvarobanan
Avsnitt med rubriken “Den flyktiga närvarobanan”Närvaro – vem som är online, var de är, vad de skriver, vad en agent gör just
nu – är värdelös i samma stund den blir inaktuell. Om den bevarades skulle
varje återanslutning behöva stämma av ”databasen säger att Sam redigerar det
här ärendet” mot verkligheten. Så närvaro skrivs aldrig till databasen. Den
lever i en närvarohubb i minnet på servern: klienter publicerar med
presence.update, peers prenumererar med presence.watch och poster upphör
automatiskt efter tre missade hjärtslag (hjärtslag körs var 10:e sekund;
hubben sveper var 5:e). Det finns inget att stämma av – bara att låta förfalla.
Två egenskaper gör banan pålitlig:
- Identiteten kommer från sessionen, aldrig från klientargument. Servern
mappar anslutningens autentiserade enhet till ett användar-id; en klient kan
bara uppdatera sin egen närvaro, så ingen kan förfalska en annan deltagares
status eller locus. En uttrycklig
offline-uppdatering (frånkoppling eller växlingsknappen för osynlig) tar bort posten omedelbart. - Närvarolistan hubbas på serversidan. Peers utbyter aldrig närvaro direkt, så varje deltagare ser samma närvarolista oavsett topologi. Agenter har ingen klient, så servern syntetiserar deras poster från livedata om körningar – tänker, kör, blockerad på en godkännandegrind, klar – där en enda representativ körning avgör en agents tillstånd och locus, så att samtidiga körningar inte får närvarolistan att fladdra.
Utskickningen nivåindelas per konsument: skrivbordet och webben ligger på en full nivå (högst ett utskick av närvarolistan per 100 ms), telefonen på en sammanfattningsnivå (per 500 ms), så att uppdateringar i markörens takt inte kan smälta PWA:n.
En deltagares locus – var de befinner sig just nu, ända ner till artefakten
– är en förseglad union: Space, File (repo + sökväg + valfri rad), Pr,
Ticket, PlanNode. Alla locus har inte någonstans att hoppa till: det finns
ingen rutt för filvisaren, så en File-locus renderas som en etikett i
stället för ett mål. Banan bär också mjuka anspråk (”Sam redigerar det här
ärendet”, ”en agent håller den här worktreen”): konflikt_synlighet_, inte lås.
Okända locus-typer parsas till null, så en gammal klient renderar helt enkelt
inte det den inte förstår.
Den varaktiga delade banan
Avsnitt med rubriken “Den varaktiga delade banan”Poster som måste överleva – ärenden, ytans delade Anteckningar-dokument, meddelanden – tar den motsatta vägen: auktoritativa, ordnade och bevarade. Samtidiga redigeringar löses med last-writer-wins i serverns mottagningsordning, aldrig av klientklockor. Upplösningen är medvetet finkornig: ärenderedigeringar sker per kolumn, så två personer som redigerar olika fält i samma ärende får båda genomslag och ingen skriver över den andra (arbetsflödesövergångar behåller i stället optimistisk låsning); ytans Anteckningar-dokument är en enda kolumn, så hela dokumentet är LWW, där mjuka anspråk på närvarobanan gör samtidig redigering synlig i stället för låst. Det är medvetet inte en CRDT – bevisbördan ligger på CRDT:n, och LWW i mottagningsordning plus synliga anspråk täcker de faktiska samarbetsmönstren.
”Senaste skribenten” betyder serverns mottagningsordning, aldrig en klientklocka – en laptop med felställd klocka kan inte vinna en redigering som den förlorade. Bakom den varaktiga banan ligger synkflödet: SQLite-triggers lägger till varje mutation i en ändringslogg per arbetsyta inuti skrivtransaktionen och tilldelar ett monotont sekvensnummer per arbetsyta i samma transaktion som själva mutationen. En servertjänst följer den loggen och omvandlar den till versionshanterade deltaramar i sekvensordning. Klienter utvecklas från fullständiga ögonblicksbilder till deltan; en lucka i sekvensen sätter tillbaka den butiken i ögonblicksläge i stället för att felaktigt tillämpa ramar.
Följ, styr, ta över, lämna tillbaka
Avsnitt med rubriken “Följ, styr, ta över, lämna tillbaka”Närvarobanan är inte bara en närvarolista – den driver samarbetsverben:
- Följläge. Klicka på en deltagare och din klient följer deras locus och navigerar när de navigerar. Följ gäller ”tills du agerar”: i samma stund du navigerar på egen hand kopplas följningen loss. En banderoll som går att stänga meddelar när en annan deltagare spotlightar (presenterar) en yta du är i.
- Styr. Ett meddelande till en körande agent köas som styrning; agenten läser om det innan den återupptar sin nästa tur, så du kan knuffa en körning medan den pågår utan att döda den.
- Ta över. Att ta kontrollen över ett samtal pausar varje aktiv körning vid dess nästa rena turgräns (en Claude Code-körning, som saknar säker gräns, stoppas) bakom en git-ögonblicksbild per tur, så att övertagandet går att ångra. En varaktig markör blockerar nya dispatchar in i ytan och överlever en serveromstart – servern återupptar aldrig automatiskt in i en människas halvfärdiga redigering. En levande redigerare per worktree är ett mjukt anspråk: ett andra övertagande syns och avvisas, det slås inte tyst samman.
- Lämna tillbaka. Att lämna tillbaka kontrollen publicerar en strukturerad diffsammanfattning av worktreen i ytan, köar den som styrning för de pausade körningarna, återupptar dem och rensar markören.
Autonomireglaget
Avsnitt med rubriken “Autonomireglaget”Styrning och övertagande är ingripanden i efterhand. Autonomireglaget är den ständiga inställningen: per yta, per agent, inställd från ytans huvud, och den avgör vad som händer när en agent når en grindad åtgärd.
| Nivå | Trådvärde | Effekt på ett grindat verktygsanrop |
|---|---|---|
| Bara föreslå | proposeOnly |
Nekas direkt, med en anledning som agenten uppmanas att vidarebefordra – den kan beskriva åtgärden men inte utföra den |
| Agera med godkännande | actWithApproval |
Fail-closed-godkännandegrinden: en bekräftelse visas för en ansluten operatör och ingen godkännare betyder nekat |
| Agera fritt | actFreely |
Förhandsgodkänd, utom där en hård deny-regel bland räckena gäller – en deny-regel vinner alltid |
| (osatt) | null |
Samma som agera med godkännande: serverns standard |
Två saker ligger utanför reglaget i båda riktningarna. Ett läges eget
utdataverb är fäst och kan aldrig nekas, vad reglaget än säger –
orkestreringsläget kunde en gång i strukturell mening inte sända ut sin enda
leverabel eftersom en förinställning nekade den effektklass som verbet
deklarerar. Och ett hårt deny från åtgärdsräckena kan inte åsidosättas av
actFreely.
Solo utan regression
Avsnitt med rubriken “Solo utan regression”Allt detta är additivt. En ensam operatörs arbetsflöde är oförändrat: med en människa och inga agenter har närvarohubben inget att sända ut, så banan skickar sin första ögonblicksbild och står sedan stilla med noll trafik; LWW- och synkmaskineriet är samma enkelskrivarväg som det alltid har varit; och parkopplingsnyckeln förblir den enhetsuppgift den alltid har varit. Medlemskapet är redan åtkomstgränsen – med en människa finns en enda ägarrad och ingen krom för någon närvarolista.
Relaterade koncept
Avsnitt med rubriken “Relaterade koncept”- Arbetsytor och isolering: gränsen som medlemskapet upprätthåller
- Fjärrstyrning och mobil: parkopplingsnyckeln och enhetsuppgifterna i identiteten
- Agentmodellen: agenthälften av principalunionen
- Livscykel för agentdispatch: körningarna som styrning och övertagande pausar och återupptar
- Räcken: effektklasspolicyn som autonomireglaget samverkar med
- Domänhändelser: bussen som liveuppdateringarna rider på