Repository navigation
Add workspace admission approvals and invite-by-email to Settings - #3291
Conversation
Pragmatic code review (medium): APPROVEThe PR follows the existing
The UI gate is cosmetic, since every write is cloud-authorized and Critical: none. Improvements
Nits: |
Address-review (commit on top of 13a0d58)Fixed
Mutation-checked:
Declined (nits)
|
Re-review of 29451a4: APPROVEAll three fixes are correct and complete, and there are no regressions.
Findings: no critical issues and no suggested improvements. Nits
Address-review: no re-review needed. The branch is review-clean. Not merged: merging is the user's call. |
Owners and tenant admins get a "Workspace members" card in Settings → Account listing pending admissions (from ListTenantMembers, status pending) with Approve (ApproveAdmission) and an invite-by-email form (InitiateAdmission). The card renders only when the caller's own roster row is an active owner/tenant_admin. A pending user is told they are waiting for approval, both in Account and in the add-synced-database dialog's no-workspace step. New Tauri commands pro_list_tenant_members, pro_approve_admission and pro_initiate_admission rewrite daemon status codes into actionable messages. The vendored Pro proto is refreshed to include the admission RPCs. Closes #3288 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The members card re-mounts on activeDatabaseId, which switchTo sets before pro_activate_database resolves, so its first ListTenantMembers could read the previous database's tenant. databaseStore now exposes proSyncSettled() and the card awaits it, and a load-sequence guard drops results from a superseded load. The Unavailable admission error keeps the daemon's detail, since it also carries worker-side 5xx failures. Adds tests for the settle wait and for a failed refresh keeping the admin card with Retry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
61fcec5 to
ef55cfa
Compare
Summary
People who sign in to a workspace (tenant) with admission enforcement land
pendingand see nothing until an owner or tenant admin approves them. Until now there was no UI for that. This PR adds it.Settings → Account → "Workspace members" (Pro + Labs "Team synchronization", signed in):
ListTenantMembers(the cloudlist_tenant_membersreturns every non-removed row with itsstatus), each with an Approve button (ApproveAdmission). After an approval the list reloads.InitiateAdmission(the daemon resolves the email through the worker'sPOST /admission/resolve-invitee, then callsinitiate_admission). This creates a pending admission, which the admin then approves above. Handlesinitiatedandalready_member(pending/active/other).GetIdentity's person id) is an activeowner/tenant_admin. Plain members, pending users, un-bound devices, local-only databases, and failed loads (role unknown) see nothing. The card is re-keyed on the active database.The pending user's own state. The daemon had no pending UX, so this PR adds it in two places:
ListTenantMemberships, which is global).Actionable errors. The Tauri layer (
admission_error) maps the daemon's status codes to next steps:Unauthenticated: sign in again.NotFoundon invite: the invitee must sign in to Pro once.PermissionDeniedon approve: only owners/admins can approve.ResourceExhausted: wait a minute.FailedPrecondition: re-do the browser sign-in.Unavailable: check the connection.Unimplemented: update NodeSpace.RPCs
ListTenantMembers,ApproveAdmission,InitiateAdmission,GetIdentity,ListTenantMemberships.ListTenantMembersalready carries pending rows with their status.remove_from_tenant, but the daemon doesn't expose it.nodespace_pro.protois refreshed from syncmain. The diff is purely additive: the two admission RPCs and their messages.Not included
packages/clionly talks to the core daemon (nodespace-proto) and has noCloudSyncServiceclient.Tests
workspace-members-card.test.ts(15 tests):already_memberpending/active, and the no-account error.account-settings.test.ts(+3): the pending notice; no card and no notice for a plain member (with the real wire-shape identity); the card for an owner.add-synced-database-dialog.test.ts: the pending-only case now shows the approval notice, plus a new truly-empty case.admission_error..trim(). That mutant is equivalent, becausetype=emailinputs already sanitize whitespace. The one Rust mutant (moving the NotFound arm) was killed.bun run test:changedpassed on every tier (frontend, scripts, Chromium, Rust nextest).quality:fixis clean. Crate-scoped clippy with-D warnings -D clippy::unused-asyncis clean.Live verification
Not done in the app. The browser dev-proxy routes no
pro_*commands, sopro_tierfalls back to community and the Account page shows "Not available". Exercising this for real needs the Tauri app, the Pro daemon, and an OAuth sign-in to a live cloud tenant, which weren't available in this session. Coverage rests on the component and unit tests above.Closes #3288
🤖 Generated with Claude Code