Multijugador — identidad, pertenencia y presencia
Control Center funciona para un único operador, pero el modelo que hay debajo es multi-actor desde el principio. Las personas y los agentes envían mensajes en los mismos espacios, editan los mismos tickets y aparecen en la misma lista. Conseguir que eso sea seguro y comprensible se reduce a tres decisiones: quién cuenta como actor, qué controla el acceso y qué estado se comparte de forma duradera frente al que solo se difunde en vivo.
Principales: usuarios y agentes son actores en pie de igualdad
Sección titulada «Principales: usuarios y agentes son actores en pie de igualdad»Todo lo que registra «quién hizo esto» —mensajes, tickets, revisiones, planes,
registros de ejecución, entradas de auditoría— se resuelve a través de un
Principal, una unión sellada de UserPrincipal y AgentPrincipal con una
forma compacta en el protocolo (user:<id> / agent:<id>). La atribución
nunca colapsa N personas (o N agentes) en una sola cuenta centinela, así que
una revisión dejada por un agente y una revisión dejada por una persona se
distinguen allí donde aparezcan.
Como el Principal es la unidad de identidad, también es la unidad de contención:
- Los límites de velocidad son por Principal, no por sesión. La protección contra inundaciones de las llamadas remotas a herramientas presupuesta por usuario, no por conexión: todos los dispositivos de un miembro beben de una única ventana deslizante compartida (120 llamadas por minuto, de las cuales como máximo 30 pueden mutar), de modo que abrir tres dispositivos no triplica la cuota de nadie y un miembro ruidoso no puede dejar sin recursos a los demás.
- Deshacer es por Principal. El diario de acciones de cada cliente registra solo las mutaciones que realizó su Principal. Puedes hacer ⌘Z sobre tu propia edición de un ticket; no puedes deshacer la de un compañero de equipo: cuando la ves, el carril duradero ya la ha resuelto (ver más abajo).
La pertenencia es la frontera de acceso, no la clave de emparejamiento
Sección titulada «La pertenencia es la frontera de acceso, no la clave de emparejamiento»Dos credenciales distintas responden a dos preguntas distintas. La clave de emparejamiento (ver Control remoto y móvil) autentica un dispositivo ante el servidor. La pertenencia autoriza al usuario de ese dispositivo a actuar dentro de un espacio de trabajo. Un dispositivo emparejado cuyo usuario no tenga pertenencia a un espacio de trabajo no puede leerlo ni modificarlo.
La pertenencia es una fila por (espacio de trabajo, usuario), almacenada en el propio archivo de base de datos de ese espacio de trabajo, así que «¿quién está en este espacio de trabajo?» es una lectura con ámbito normal, mientras que la pregunta inversa, «¿en qué espacios de trabajo estoy?», necesariamente abarca varios archivos y es una de las pocas distribuciones entre espacios de trabajo autorizadas. Es lo que ejecuta el selector de espacio de trabajo antes de que se haya elegido ninguno.
Cada fila lleva un rol de una escala de cinco niveles ordenados por rango:
| Rol | Puede |
|---|---|
owner |
Control total, incluida la eliminación del espacio de trabajo; exactamente uno por espacio de trabajo |
admin |
Gestionar miembros, invitaciones, ajustes y operaciones destructivas |
member |
Lectura y escritura del día a día: mensajes, tickets, ejecuciones de agentes |
viewer |
Solo lectura en todo el espacio de trabajo; los patrones glob de exclusión de secretos ocultan las rutas coincidentes en las superficies con código |
guest |
El mismo suelo de solo lectura que viewer, en el escalón más bajo de la escala |
Toda decisión de acceso se reduce a «¿tiene el rol de quien llama al menos X?».
El mínimo se deriva del tipo de operación, salvo que esta declare uno
explícitamente: una lectura necesita guest, una mutación necesita member y
una operación destructiva necesita admin. Las ACL por objeto más allá de los
permisos por repositorio que se describen a continuación quedan deliberadamente
fuera de alcance.
Las invitaciones son la forma de conceder la pertenencia, desde
Ajustes → Espacio de trabajo → Miembros. Un administrador genera un código de
un solo uso (como enlace o QR) que caduca a los siete días de forma
predeterminada; el servidor solo almacena el hash SHA-256 del código, y el
código en sí se muestra exactamente una vez. Canjearlo aprovisiona al usuario
justo a tiempo (JIT), registra la pertenencia con el rol de la invitación
(nunca owner: la propiedad se transfiere de forma explícita y jamás se
concede mediante una invitación) y da paso al emparejamiento del dispositivo.
Las credenciales de dispositivo generadas tras el emparejamiento tienen a su
vez un límite de 30 días, así que un emparejamiento no es una clave permanente.
Los permisos por repositorio se sitúan por debajo de la pertenencia. Como
el servidor alberga copias de trabajo completas, la pertenencia al espacio de
trabajo por sí sola nunca debe otorgar más privilegios que la forja: toda
superficie con código —archivos, diffs, revisión de PR, búsqueda de código—
comprueba el permiso del miembro sobre ese repositorio. Los permisos son por
(espacio de trabajo, usuario, repositorio) y se ordenan
none → read → review → write, donde write significa dirigir
ejecuciones de agentes que escriben en el repositorio. La ausencia de una fila
significa none; los propietarios y los administradores tienen implícitamente
write en todos los repositorios vinculados y no tienen ninguna fila.
Dos carriles de estado de colaboración
Sección titulada «Dos carriles de estado de colaboración»Control Center divide el estado compartido en dos carriles con reglas de durabilidad opuestas, porque los dos tipos de estado fallan de maneras opuestas.
El carril efímero de presencia
Sección titulada «El carril efímero de presencia»La presencia —quién está en línea, dónde está, qué está escribiendo, qué está
haciendo un agente en este momento— no vale nada en cuanto se queda obsoleta.
Si se persistiera, cada reconexión tendría que conciliar «la base de datos dice
que Sam está editando este ticket» con la realidad. Así que la presencia
nunca se escribe en la base de datos. Vive en un centro de presencia en
memoria del servidor: los clientes publican con presence.update, los pares se
suscriben con presence.watch y las entradas caducan automáticamente tras tres
latidos perdidos (los latidos se envían cada 10 segundos; el centro barre cada
5). No hay nada que conciliar: solo dejar que caduque.
Dos propiedades mantienen fiable el carril:
- La identidad procede de la sesión, nunca de los argumentos del cliente.
El servidor asigna el dispositivo autenticado de la conexión a un id de
usuario; un cliente solo puede actualizar su propia presencia, así que nadie
puede suplantar el estado o el locus de otro participante. Una actualización
explícita
offline(desconexión o el conmutador de invisibilidad) elimina la entrada de inmediato. - La lista se centraliza en el servidor. Los pares nunca intercambian presencia directamente, de modo que todos los participantes ven la misma lista sea cual sea la topología. Los agentes no tienen cliente, así que el servidor sintetiza sus entradas a partir del estado de las ejecuciones en vivo —pensando, en curso, bloqueado en una compuerta de aprobación, listo—, y una única ejecución representativa decide el estado y el locus del agente para que las ejecuciones simultáneas no hagan parpadear la lista.
La distribución está escalonada por consumidor: el escritorio y la web usan un nivel completo (como máximo una emisión de la lista cada 100 ms) y el teléfono, un nivel resumido (cada 500 ms), de modo que las actualizaciones al ritmo del cursor no fundan la PWA.
El locus de un participante —dónde está en cada momento, hasta el nivel del
artefacto— es una unión sellada: Space, File (repo + ruta + línea
opcional), Pr, Ticket, PlanNode. No todos los locus tienen un destino al
que saltar: no hay una ruta de visor de archivos, así que un locus File se
representa como una etiqueta en lugar de como un destino. El carril también
transporta avisos no vinculantes («Sam está editando este ticket», «un
agente tiene este worktree ocupado»): visibilidad de conflictos, no bloqueos.
Los tipos de locus desconocidos se resuelven a null, así que un cliente antiguo
simplemente no representa lo que no entiende.
El carril compartido duradero
Sección titulada «El carril compartido duradero»Los registros que deben perdurar —tickets, el documento de Notas compartido del espacio, los mensajes— siguen el camino opuesto: autoritativos, ordenados y persistidos. Las ediciones simultáneas se resuelven por último escritor gana según el orden de recepción del servidor, nunca por los relojes de los clientes. La resolución es deliberadamente granular: las ediciones de tickets son por columna, de modo que si dos personas editan campos distintos del mismo ticket ambas se aplican y ninguna pisa a la otra (las transiciones de flujo de trabajo conservan el bloqueo optimista); el documento de Notas del espacio es una sola columna, así que todo el documento es LWW, con los avisos no vinculantes del carril de presencia haciendo visible la edición simultánea en lugar de bloquearla. Deliberadamente no es un CRDT: la carga de la prueba recae sobre el CRDT, y el LWW por orden de recepción junto con los avisos visibles cubre los patrones de colaboración reales.
«Último escritor» significa orden de recepción del servidor, nunca un reloj de cliente: un portátil con un reloj desajustado no puede ganar una edición que perdió. Detrás del carril duradero está el flujo de sincronización: los disparadores de SQLite añaden cada mutación a un registro de cambios por espacio de trabajo dentro de la transacción de escritura, asignando un número de secuencia monotónico por espacio de trabajo en la misma transacción que la propia mutación. Un servicio del servidor sigue ese registro y lo convierte en tramas delta versionadas por orden de secuencia. Los clientes pasan de instantáneas completas a deltas; un hueco en la secuencia devuelve ese almacén al modo de instantánea en lugar de aplicar mal las tramas.
Seguir, orientar, tomar el control y devolverlo
Sección titulada «Seguir, orientar, tomar el control y devolverlo»El carril de presencia no es solo una lista: impulsa los verbos de la colaboración:
- Modo Seguir. Haz clic en un participante y tu cliente cabalga sobre su locus, navegando a medida que él navega. Seguir dura «hasta que actúes»: en el momento en que navegas por tu cuenta, el seguimiento se desengancha. Un aviso descartable anuncia cuándo otro participante está destacando (presentando) un espacio en el que estás.
- Orientar. Un mensaje dirigido a un agente en ejecución se pone en cola como orientación; el agente lo relee antes de reanudar su siguiente turno, así que puedes reconducir una ejecución en pleno vuelo sin matarla.
- Tomar el control. Asumir el control de una conversación pausa todas las ejecuciones activas en su siguiente límite de turno limpio (una ejecución de Claude Code, que no tiene un límite seguro, se detiene) tras una instantánea de git por turno, de modo que la toma de control es reversible. Un marcador duradero bloquea nuevos despachos al espacio y sobrevive a un reinicio del servidor: el servidor nunca se reanuda automáticamente dentro de la edición a medias de una persona. Que haya un solo editor activo por worktree es un aviso no vinculante: una segunda toma de control es visible y se rechaza, no se fusiona en silencio.
- Devolver el control. Devolver el control publica en el espacio un resumen estructurado del diff del worktree, lo pone en cola como orientación para las ejecuciones pausadas, las reanuda y borra el marcador.
El dial de autonomía
Sección titulada «El dial de autonomía»Orientar y tomar el control son intervenciones a posteriori. El dial de autonomía es el ajuste permanente: por espacio y por agente, se establece desde la cabecera del espacio y decide qué ocurre cuando un agente llega a una acción restringida.
| Nivel | Valor de protocolo | Efecto en una llamada de herramienta restringida |
|---|---|---|
| Solo proponer | proposeOnly |
Se deniega sin más, con un motivo que se indica al agente para que lo transmita: puede describir la acción, pero no ejecutarla |
| Actuar con aprobación | actWithApproval |
La compuerta de aprobación que falla en cerrado: se muestra una confirmación a un operador conectado y, sin aprobador, se deniega |
| Actuar libremente | actFreely |
Preaprobado, salvo cuando se aplica una regla de barandilla deny estricta: una regla de denegación siempre gana |
| (sin definir) | null |
Igual que actuar con aprobación: el valor predeterminado del servidor |
Dos cosas quedan fuera del dial en ambos sentidos. El propio verbo de salida de
un modo está fijado y nunca se puede denegar, diga lo que diga el dial: el modo
orchestrate llegó a ser estructuralmente incapaz de emitir su único entregable
porque un preajuste denegaba la clase de efecto que declara ese verbo. Y un
deny estricto de las barandillas de acción no se puede anular con actFreely.
Cero regresiones en solitario
Sección titulada «Cero regresiones en solitario»Todo esto es aditivo. El flujo de trabajo de un operador en solitario no cambia: con una sola persona y ningún agente, el centro de presencia no tiene nada que difundir, así que el carril emite su instantánea inicial y luego permanece inactivo con tráfico cero; la maquinaria de LWW y sincronización es el mismo camino de escritor único de siempre; y la clave de emparejamiento sigue siendo la credencial de dispositivo que siempre ha sido. La pertenencia ya es la frontera de acceso: con una sola persona hay una única fila de propietario y ninguna interfaz de lista.
Conceptos relacionados
Sección titulada «Conceptos relacionados»- Espacios de trabajo y aislamiento: la frontera que aplica la pertenencia
- Control remoto y móvil: la cara de la identidad que ocupan la clave de emparejamiento y la credencial de dispositivo
- El modelo de agentes: la mitad agente de la unión Principal
- Ciclo de vida del despacho de agentes: las ejecuciones que orientar y tomar el control pausan y reanudan
- Barandillas: la política de clases de efecto con la que se compone el dial de autonomía
- Eventos de dominio: el bus por el que circulan las actualizaciones en vivo