Bỏ qua để đến nội dung

Đa người dùng — danh tính, tư cách thành viên và hiện diện

Control Center vẫn dùng được cho một người vận hành duy nhất, nhưng mô hình bên dưới nó vốn đã là đa tác nhân ngay từ đầu. Con người và agent nhắn tin trong cùng những không gian, sửa cùng những phiếu và xuất hiện trên cùng một danh sách. Để điều đó vừa an toàn vừa dễ hiểu, tất cả quy về ba quyết định: ai được tính là một tác nhân, điều gì kiểm soát quyền truy cập và trạng thái nào được chia sẻ bền vững so với chỉ được phát trực tiếp.

Chủ thể: người dùng và agent là những tác nhân ngang hàng

Phần tiêu đề “Chủ thể: người dùng và agent là những tác nhân ngang hàng”

Mọi thứ ghi lại “ai đã làm điều này” — tin nhắn, phiếu, đánh giá, kế hoạch, nhật ký chạy, mục kiểm toán — đều phân giải qua một chủ thể, một union kín của UserPrincipal và AgentPrincipal với dạng truyền gọn (user:<id> / agent:<id>). Việc quy kết không bao giờ gộp N con người (hay N agent) thành một tài khoản đại diện duy nhất, nên một đánh giá do agent để lại và một đánh giá do con người để lại luôn phân biệt được ở mọi nơi chúng xuất hiện.

Vì chủ thể là đơn vị của danh tính, nó cũng là đơn vị của việc giới hạn:

  • Giới hạn tốc độ tính theo chủ thể, không theo phiên. Cơ chế chống dồn dập cho các lời gọi công cụ từ xa đặt hạn mức theo người dùng, chứ không theo kết nối: mọi thiết bị của một thành viên cùng rút từ một cửa sổ trượt chung (120 lời gọi mỗi phút, trong đó tối đa 30 lời gọi được phép thay đổi dữ liệu), nên mở ba thiết bị không làm hạn mức của ai tăng gấp ba và một thành viên ồn ào cũng không thể bỏ đói những người khác.
  • Hoàn tác tính theo chủ thể. Nhật ký hành động của mỗi máy khách chỉ ghi những thay đổi do chính chủ thể của nó thực hiện. Bạn có thể ⌘Z bản sửa phiếu của mình; bạn không thể hoàn tác thay đổi của đồng đội — khi bạn nhìn thấy nó thì làn bền vững đã phân giải xong rồi (xem bên dưới).

Tư cách thành viên là ranh giới truy cập — không phải khóa ghép nối

Phần tiêu đề “Tư cách thành viên là ranh giới truy cập — không phải khóa ghép nối”

Hai thông tin xác thực riêng biệt trả lời hai câu hỏi riêng biệt. Khóa ghép nối (xem Điều khiển từ xa và di động) xác thực một thiết bị với máy chủ. Tư cách thành viên cấp quyền cho người dùng của thiết bị đó hành động bên trong một không gian làm việc. Một thiết bị đã ghép nối mà người dùng của nó không có tư cách thành viên trong một không gian làm việc thì không thể đọc cũng không thể thay đổi không gian đó.

Tư cách thành viên là một hàng cho mỗi cặp (không gian làm việc, người dùng), được lưu trong tệp cơ sở dữ liệu của chính không gian làm việc đó — nên “ai đang ở trong không gian làm việc này?” là một phép đọc có phạm vi thông thường, còn câu hỏi ngược lại, “tôi đang ở trong những không gian làm việc nào?”, bắt buộc phải trải qua nhiều tệp và là một trong số ít truy vấn mở rộng xuyên không gian làm việc được phép. Đây cũng chính là thứ mà bộ chọn không gian làm việc chạy trước khi bất kỳ không gian làm việc nào được chọn.

Mỗi hàng mang một vai trò từ một thang năm bậc có thứ tự:

Vai trò Được phép
owner Toàn quyền kiểm soát, kể cả xóa không gian làm việc; đúng một người cho mỗi không gian làm việc
admin Quản lý thành viên, lời mời, cài đặt, các thao tác phá hủy
member Đọc-ghi hằng ngày: tin nhắn, phiếu, các lần chạy agent
viewer Chỉ đọc trong toàn không gian làm việc; các glob loại trừ bí mật ẩn những đường dẫn khớp trên bề mặt chứa mã
guest Cùng mức sàn chỉ đọc như người xem, ở bậc thấp nhất của thang

Mọi quyết định truy cập đều quy về câu hỏi “vai trò của người gọi có ít nhất là X không”. Mức sàn được suy ra từ loại thao tác, trừ khi thao tác đó khai báo tường minh: một phép đọc cần guest, một thay đổi cần member, một thao tác phá hủy cần admin. Các ACL theo từng đối tượng nằm ngoài phạm vi cấp quyền theo kho mã bên dưới được cố ý để ngoài phạm vi.

Lời mời là cách cấp tư cách thành viên, từ Cài đặt → Không gian làm việc → Thành viên. Một quản trị viên tạo ra mã dùng một lần (dưới dạng liên kết hoặc mã QR) mặc định hết hạn sau bảy ngày; máy chủ chỉ lưu hash SHA-256 của mã, còn bản thân mã chỉ được hiển thị đúng một lần. Đổi mã sẽ cấp phát người dùng theo kiểu JIT, ghi tư cách thành viên theo vai trò của lời mời (không bao giờ là owner — quyền sở hữu được chuyển giao tường minh, không bao giờ được cấp qua lời mời) và chuyển tiếp sang ghép nối thiết bị. Thông tin xác thực thiết bị được tạo sau khi ghép nối cũng có thời hạn 30 ngày, nên một lần ghép nối không phải là một khóa vĩnh viễn.

Cấp quyền theo kho mã nằm bên dưới tư cách thành viên. Vì máy chủ giữ toàn bộ bản checkout, chỉ riêng tư cách thành viên không gian làm việc không bao giờ được phép vượt quyền trên máy chủ mã: mọi bề mặt chứa mã — tệp, diff, đánh giá PR, tìm kiếm mã — đều kiểm tra cấp quyền của thành viên trên chính kho mã đó. Cấp quyền được ghi theo bộ (không gian làm việc, người dùng, kho mã) và xếp hạng none → read → review → write, trong đó write nghĩa là được điều khiển các lần chạy agent ghi vào kho mã. Không có hàng nào nghĩa là none; chủ sở hữu và quản trị viên mặc nhiên giữ write trên mọi kho mã đã liên kết và không có hàng nào cả.

Control Center tách trạng thái chia sẻ thành hai làn với những quy tắc bền vững trái ngược nhau, vì hai loại trạng thái này hỏng theo hai cách trái ngược.

Hiện diện — ai đang trực tuyến, họ đang ở đâu, họ đang gõ gì, agent đang làm gì ngay lúc này — trở nên vô giá trị ngay khi cũ đi. Nếu nó được lưu lại, mỗi lần kết nối lại sẽ phải dung giải “cơ sở dữ liệu nói rằng Sam đang sửa phiếu này” với thực tế. Vì vậy hiện diện không bao giờ được ghi vào cơ sở dữ liệu. Nó nằm trong một hub hiện diện trong bộ nhớ trên máy chủ: máy khách công bố bằng presence.update, các máy ngang hàng đăng ký bằng presence.watch, và các mục tự động hết hạn sau ba nhịp tim bị lỡ (nhịp tim chạy mỗi 10 giây; hub quét dọn mỗi 5 giây). Không có gì phải dung giải — chỉ cần để nó tự hết hạn.

Hai tính chất giữ cho làn này đáng tin:

  • Danh tính đến từ phiên, không bao giờ từ đối số của máy khách. Máy chủ ánh xạ thiết bị đã xác thực của kết nối sang một id người dùng; một máy khách chỉ có thể cập nhật hiện diện của chính nó, nên không ai có thể mạo danh trạng thái hay vị trí của người tham gia khác. Một cập nhật offline tường minh (ngắt kết nối hoặc nút bật chế độ ẩn) sẽ xóa mục đó ngay lập tức.
  • Danh sách được tập trung ở máy chủ. Các máy ngang hàng không bao giờ trao đổi hiện diện trực tiếp với nhau, nên mọi người tham gia đều thấy cùng một danh sách bất kể cấu trúc kết nối. Agent không có máy khách, nên máy chủ tổng hợp các mục của chúng từ trạng thái chạy trực tiếp — đang nghĩ, đang chạy, bị chặn ở cổng phê duyệt, đã xong — với một lần chạy đại diện duy nhất quyết định trạng thái và vị trí của agent, để những lần chạy đồng thời không làm danh sách nhấp nháy.

Việc phát tỏa được chia tầng theo từng loại máy khách: máy tính để bàn và web dùng tầng đầy đủ (tối đa một lần phát danh sách mỗi 100 ms), điện thoại dùng tầng tóm tắt (mỗi 500 ms), nên những cập nhật theo nhịp con trỏ không thể làm nóng chảy PWA.

Vị trí của một người tham gia — họ đang ở đâu, chi tiết đến tận hiện vật — là một union kín: Space, File (kho mã + đường dẫn + số dòng tùy chọn), Pr, Ticket, PlanNode. Không phải vị trí nào cũng có nơi để nhảy tới: không có tuyến trình xem tệp, nên một vị trí File chỉ hiển thị dưới dạng nhãn chứ không phải đích đến. Làn này còn mang những đánh dấu mềm (“Sam đang sửa phiếu này”, “một agent đang giữ cây làm việc này”): tính hữu hình của xung đột, chứ không phải khóa. Những loại vị trí không nhận biết được sẽ phân giải thành null, nên một máy khách cũ đơn giản là không hiển thị thứ nó không hiểu.

Những bản ghi phải tồn tại lâu dài — phiếu, tài liệu Ghi chú dùng chung của không gian, tin nhắn — đi theo con đường ngược lại: có thẩm quyền, có thứ tự và được lưu bền vững. Các chỉnh sửa đồng thời được phân giải theo người ghi sau thắng theo thứ tự máy chủ nhận được, không bao giờ theo đồng hồ máy khách. Việc phân giải được cố ý làm ở mức chi tiết: các chỉnh sửa phiếu là theo từng cột, nên hai người sửa những trường khác nhau của cùng một phiếu đều được ghi lại và không ai ghi đè người kia (các chuyển trạng thái quy trình vẫn dùng khóa lạc quan); còn tài liệu Ghi chú của không gian chỉ có một cột, nên toàn bộ tài liệu theo cơ chế người ghi sau thắng, với những đánh dấu mềm trên làn hiện diện giúp việc chỉnh sửa đồng thời trở nên hữu hình thay vì bị khóa. Nó cố ý không phải là CRDT — nghĩa vụ chứng minh thuộc về CRDT, còn cơ chế người ghi sau thắng theo thứ tự nhận được cùng những đánh dấu hữu hình đã bao quát các mẫu cộng tác thực tế.

“Người ghi sau” nghĩa là thứ tự máy chủ nhận được, không bao giờ là đồng hồ máy khách — một máy tính có đồng hồ lệch không thể thắng một chỉnh sửa mà nó đã thua. Phía sau làn bền vững là luồng đồng bộ: các trigger SQLite gắn mọi thay đổi vào một nhật ký thay đổi theo từng không gian làm việc ngay bên trong giao dịch ghi, cấp một số thứ tự đơn điệu theo không gian làm việc trong cùng giao dịch với chính thay đổi đó. Một dịch vụ máy chủ theo dõi nhật ký đó để tạo ra những khung delta có phiên bản, theo đúng thứ tự. Máy khách tiến hóa từ ảnh chụp toàn bộ sang delta; một khoảng trống trong chuỗi số thứ tự sẽ đưa kho đó trở về chế độ ảnh chụp toàn bộ thay vì áp dụng sai các khung.

Theo dõi, điều hướng, tiếp quản, giao lại

Phần tiêu đề “Theo dõi, điều hướng, tiếp quản, giao lại”

Làn hiện diện không chỉ là một danh sách — nó còn điều khiển các động từ cộng tác:

  • Chế độ theo dõi. Nhấp vào một người tham gia và máy khách của bạn sẽ bám theo vị trí của họ, di chuyển khi họ di chuyển. Theo dõi là “cho đến khi bạn hành động”: ngay khi bạn tự mình di chuyển, chế độ theo dõi sẽ tách ra. Một biểu ngữ có thể đóng sẽ thông báo khi một người tham gia khác đang trình bày một không gian mà bạn có mặt.
  • Điều hướng. Một tin nhắn gửi cho agent đang chạy được xếp hàng chờ dưới dạng điều hướng; agent đọc lại nó trước khi tiếp tục lượt kế tiếp, nên bạn có thể thúc nhẹ một lần chạy đang diễn ra mà không cần dừng nó.
  • Tiếp quản. Việc nắm quyền kiểm soát một cuộc hội thoại sẽ tạm dừng mọi lần chạy đang hoạt động tại ranh giới lượt sạch kế tiếp (một lần chạy Claude Code, vốn không có ranh giới an toàn, sẽ bị dừng) phía sau một ảnh chụp nhanh git theo từng lượt, nên việc tiếp quản có thể đảo ngược. Một dấu hiệu bền vững chặn các lần điều phối mới vào không gian này và tồn tại qua cả lần khởi động lại máy chủ — máy chủ không bao giờ tự động chạy tiếp vào giữa một chỉnh sửa dở dang của con người. Chỉ một người chỉnh sửa trực tiếp cho mỗi cây làm việc là một đánh dấu mềm: lần tiếp quản thứ hai sẽ hữu hình và bị từ chối, chứ không được âm thầm gộp lại.
  • Giao lại. Việc trả lại quyền kiểm soát sẽ đăng một bản tóm tắt diff có cấu trúc của cây làm việc lên không gian, xếp nó vào hàng chờ điều hướng cho các lần chạy đang tạm dừng, tiếp tục các lần chạy đó và xóa dấu hiệu.

Điều hướng và tiếp quản là những can thiệp sau khi sự việc đã xảy ra. Núm tự chủ là thiết lập thường trực: theo từng không gian, theo từng agent, được đặt từ phần đầu của không gian, nó quyết định điều gì xảy ra khi một agent chạm tới một hành động có cổng.

Mức Giá trị truyền Tác động lên một lời gọi công cụ có cổng
Chỉ đề xuất proposeOnly Bị từ chối thẳng, kèm một lý do mà agent được yêu cầu chuyển đạt lại — nó có thể mô tả hành động nhưng không thể thực hiện
Hành động khi được duyệt actWithApproval Cổng phê duyệt đóng khi thiếu người duyệt: một xác nhận được đưa tới người vận hành đang kết nối, và không có người phê duyệt nghĩa là bị từ chối
Hành động tự do actFreely Được phê duyệt trước, trừ khi có quy tắc rào chắn deny cứng áp dụng — quy tắc từ chối luôn thắng
(chưa đặt) null Giống như hành động khi được duyệt: giá trị mặc định của máy chủ

Hai thứ nằm ngoài núm này theo cả hai hướng. Động từ đầu ra của một chế độ là cố định và không bao giờ bị từ chối, bất kể núm nói gì — chế độ orchestrate từng không thể phát ra sản phẩm bàn giao duy nhất của nó vì một preset đã từ chối lớp hiệu ứng mà động từ đó khai báo. Và một lệnh deny cứng từ rào chắn hành động thì actFreely không thể ghi đè.

Tất cả những điều này chỉ là phần bổ sung. Quy trình làm việc của một người vận hành đơn lẻ không thay đổi: với một con người và không có agent nào, hub hiện diện không có gì để phát, nên làn này phát ảnh chụp ban đầu rồi nằm im với lưu lượng bằng không; bộ máy người-ghi-sau-thắng và đồng bộ vẫn là cùng một con đường ghi-đơn như trước giờ; và khóa ghép nối vẫn là thông tin xác thực thiết bị như nó vốn là. Tư cách thành viên vốn đã là ranh giới truy cập — với một con người, chỉ có một hàng chủ sở hữu duy nhất và không có phần giao diện danh sách nào.