Skip to content

fix(workspaces): address review comments across stacked PRs#988

Merged
raivieiraadriano92 merged 38 commits into
raivieiraadriano92/thu-556-settings-workspace-permissions-manage_members-change_rolesfrom
raivieiraadriano92/workspaces-review-followups
Jun 16, 2026
Merged

fix(workspaces): address review comments across stacked PRs#988
raivieiraadriano92 merged 38 commits into
raivieiraadriano92/thu-556-settings-workspace-permissions-manage_members-change_rolesfrom
raivieiraadriano92/workspaces-review-followups

Conversation

@raivieiraadriano92

@raivieiraadriano92 raivieiraadriano92 commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Single follow-up PR addressing review comments across the stacked workspaces feature PRs. One commit per fix; each original thread has a `Fixed in ` reply pointing here.

Stacked PRs covered (base → tip):


Note

High Risk
Changes authorization on membership and pending-invite uploads, expands who receives pending-invite sync data, and reorders sign-out/token/credential wiping—mistakes could allow privilege escalation, leave sessions or keys on disk, or break multi-tab logout.

Overview
This PR tightens workspace membership authorization end-to-end: PowerSync upload handlers no longer treat “workspace admin” as the only gate. Writes use workspace_permissions keys (invite_users, change_roles, remove_users) via shared callerSatisfiesPermission, with extra guards so invite_users alone cannot mint or promote admins, PUT upserts cannot demote admins or bypass change_roles, and last-admin protection applies on membership PUT as well as PATCH/DELETE. Pending-invite handling is hardened: email format validation, invited_by_user_id forced to the caller, promote-on-insert uses insert-if-missing (no role downgrade) and delete pending by workspace+email after unique-key conflicts.

Sync rules move workspace_pending_memberships from the admin-only bucket into workspace_data so members granted invite/role/remove permissions can see pending rows; upload handlers remain the write authority.

On the client, the Members route is readable by any shared-workspace member; Add / role / remove controls align with the granular keys (pending lifecycle gated on invite_users). useActiveWorkspaceId can resolve from the URL before the workspace row exists; cross-workspace navigation resets chat detail paths to /chats/new. Sign-out wipe clears activeTrustDomain early for multi-tab safety, passes captured serverId into credential and encryption key cleanup, and defers token clearing until after authenticated signOut (withCapturedAuthToken). Smaller fixes include workspace PUT preserving omitted slug/icon, SERVER_ID validation messages, app error boundary, mode-picker stale validation, and duplicate-workspace slug suffixes.

Reviewed by Cursor Bugbot for commit 3b9607c. Bugbot is set up for automated code reviews on this repo. Configure here.

@raivieiraadriano92 raivieiraadriano92 self-assigned this Jun 16, 2026
@raivieiraadriano92 raivieiraadriano92 changed the base branch from raivieiraadriano92/thu-580-desktop-app-fails-to-start-when-network-is-offline to raivieiraadriano92/thu-556-settings-workspace-permissions-manage_members-change_roles June 16, 2026 11:39
@raivieiraadriano92 raivieiraadriano92 changed the title Raivieiraadriano92/workspaces review followups fix(workspaces): address review comments across stacked PRs Jun 16, 2026
@raivieiraadriano92 raivieiraadriano92 marked this pull request as ready for review June 16, 2026 11:42
@github-actions

Copy link
Copy Markdown

Semgrep Security Scan

No security issues found.

Comment thread src/components/boot/mode-picker.tsx
Comment thread src/settings/workspace/general.tsx Outdated
Comment thread src/layout/sidebar/create-workspace-modal.tsx Outdated
Comment thread src/components/boot/mode-picker.tsx
Comment thread backend/src/powersync/upload-handlers/workspace-memberships.ts
Comment thread src/lib/cleanup.ts
Comment thread backend/src/powersync/upload-handlers/workspace-memberships.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit bd4a338. Configure here.

Comment thread src/settings/workspace/members.tsx
Comment thread powersync-service/config/config.yaml
@raivieiraadriano92 raivieiraadriano92 merged commit 24cc392 into raivieiraadriano92/thu-556-settings-workspace-permissions-manage_members-change_roles Jun 16, 2026
10 of 17 checks passed
@raivieiraadriano92 raivieiraadriano92 deleted the raivieiraadriano92/workspaces-review-followups branch June 16, 2026 20:08
raivieiraadriano92 added a commit that referenced this pull request Jun 16, 2026
…D enforcement (#978)

* feat(THU-556): setWorkspacePermissionRequiredRole DAL

* feat(THU-556): RequireWorkspaceAdmin route guard

* refactor(THU-556): consolidate workspace permission keys into shared/workspaces.ts

* feat(THU-556): Permissions page (11 keys, labels only)

* feat(THU-556): Permissions sidebar entry

* feat(THU-556): mount Permissions route

* feat(THU-556): refine Permissions page copy and layout

* feat(THU-556): scope Permissions to agents/skills/models/mcps

* feat(THU-556): use Lock icon for Permissions sidebar entry

* feat(THU-556): permissionAllows + BE permission lookup helpers

* feat(THU-556): permission-key gating in workspace-scoped handler

* feat(THU-556): enforce add_agents/remove_agents permissions

* feat(THU-556): treat soft-delete PATCH as remove for permission gating

* feat(THU-556): enforce add_skills/remove_skills permissions

* feat(THU-556): enforce add_models/remove_models permissions

* feat(THU-556): enforce add_mcp_servers/remove_mcp_servers permissions

* feat(THU-556): gate edit + chat bypasses, add permission gating tests

* fix(workspaces): address review comments across stacked PRs (#988)

* fix(THU-549): clear auth token after signOut so server can revoke session

* fix(THU-549): clear registry before db-closing so reloaded tabs land at ModePicker

* fix(THU-549): guard LogoutModal handleLogout against rapid double-click

* fix(THU-549): friendlier ZodError for unset/invalid SERVER_ID

* fix(THU-549): remove Cloud URL dev-settings input (use ModePicker flow instead)

* fix(THU-552): preserve existing membership role on promote-on-insert

* fix(THU-552): delete pending row by (workspace_id, email) after promote-on-insert

* fix(THU-552): skip onCreated when create-workspace modal dismissed mid-flight

* fix(THU-554): preserve slug/icon on PUT when payload omits them

* fix(THU-554): reflect remote workspace updates into settings form

* fix(THU-554): append random suffix to duplicate workspace slug

* fix(THU-555): resolve workspace permission state when user has no membership

* fix(THU-555): gate member actions on granular permission keys

* fix(THU-551): expose URL workspace id immediately + collapse chat-id on workspace switch

* fix(THU-550): wrap BrowserRouter in ErrorBoundary so bootstrap throws surface a screen

* chore: strip PR-ref noise from review-followup comments

* fix(THU-578): reset isReady before chat session eviction on workspace switch

* fix(THU-553): dedupe ModePicker validation between blur and Continue

* fix(THU-552): force ctx.userId for invitedByUserId in pending memberships

* fix(THU-552): reject malformed emails on pending membership writes

* fix(THU-578): persist selectedAgent against session.workspaceId not thread

* fix(THU-556): hide chat-skills-bar when no edit perm and no pinned chips

* docs: refresh workspace-memberships handler docstring for permission-key model

* test(THU-578): seed workspace context in LinkPreviewWidget fallback tests

* fix(THU-553): reset isValidating on SET_URL to unstick Continue after stale blur

* fix(THU-553): reset stage to picker on stale-URL bail-out in handleContinue

* fix(THU-549): pass captured serverId to handleFullWipe after registry clear

* fix(THU-555): align pending-row gates + block admin escalation via invite

* refactor(THU-555): use isResolved for membership loading distinction

* refactor(THU-555): assign openRef in render body, drop useEffect

* fix(THU-555): widen duplicate-slug suffix to 8 chars for collision resistance

* docs(THU-555): refresh stale comments referencing removed manage_members guard

* fix(THU-555): mirror serverUrl ref synchronously in mode-picker onChange

* fix(THU-555): sync pending memberships to all workspace members

* fix(THU-555): require change_roles for membership PUT that changes existing role

* fix(THU-549): keep getAuthToken resolvable through signOut after registry clear

* fix(THU-555): add last-admin guard to membership PUT apply path

* fix(THU-555): gate pending-row Admin option on change_roles
raivieiraadriano92 added a commit that referenced this pull request Jun 16, 2026
…pending) (#974)

* feat(THU-555): add user_name/user_email to workspace_memberships

* feat(THU-555): enrich membership upload handler with user display info

* feat(THU-555): propagate user name/email changes to memberships

* feat(THU-555): mirror userName/userEmail on FE workspace_memberships

* feat(THU-555): reactive members + pending queries

* feat(THU-555): membership mutation DAL

* feat(THU-555): useWorkspacePermission hook

* feat(THU-555): RequireWorkspacePermission route wrapper

* feat(THU-555): Members page scaffold + route

* feat(THU-555): Members sidebar entry gated by manage_members

* feat(THU-555): render members table with role dropdown

* feat(THU-555): wire Remove member with confirmation

* feat(THU-555): use globe icon for General sidebar entry

* fix(THU-555): rename SCREAMING_SNAKE constant to camelCase

* feat(THU-593): gate member management on E2EE-enabled servers

* fix(chat-store): dedupe concurrent hydrateChatStore calls

* feat(THU-556): Settings → Workspace → Permissions page + resource-CRUD enforcement (#978)

* feat(THU-556): setWorkspacePermissionRequiredRole DAL

* feat(THU-556): RequireWorkspaceAdmin route guard

* refactor(THU-556): consolidate workspace permission keys into shared/workspaces.ts

* feat(THU-556): Permissions page (11 keys, labels only)

* feat(THU-556): Permissions sidebar entry

* feat(THU-556): mount Permissions route

* feat(THU-556): refine Permissions page copy and layout

* feat(THU-556): scope Permissions to agents/skills/models/mcps

* feat(THU-556): use Lock icon for Permissions sidebar entry

* feat(THU-556): permissionAllows + BE permission lookup helpers

* feat(THU-556): permission-key gating in workspace-scoped handler

* feat(THU-556): enforce add_agents/remove_agents permissions

* feat(THU-556): treat soft-delete PATCH as remove for permission gating

* feat(THU-556): enforce add_skills/remove_skills permissions

* feat(THU-556): enforce add_models/remove_models permissions

* feat(THU-556): enforce add_mcp_servers/remove_mcp_servers permissions

* feat(THU-556): gate edit + chat bypasses, add permission gating tests

* fix(workspaces): address review comments across stacked PRs (#988)

* fix(THU-549): clear auth token after signOut so server can revoke session

* fix(THU-549): clear registry before db-closing so reloaded tabs land at ModePicker

* fix(THU-549): guard LogoutModal handleLogout against rapid double-click

* fix(THU-549): friendlier ZodError for unset/invalid SERVER_ID

* fix(THU-549): remove Cloud URL dev-settings input (use ModePicker flow instead)

* fix(THU-552): preserve existing membership role on promote-on-insert

* fix(THU-552): delete pending row by (workspace_id, email) after promote-on-insert

* fix(THU-552): skip onCreated when create-workspace modal dismissed mid-flight

* fix(THU-554): preserve slug/icon on PUT when payload omits them

* fix(THU-554): reflect remote workspace updates into settings form

* fix(THU-554): append random suffix to duplicate workspace slug

* fix(THU-555): resolve workspace permission state when user has no membership

* fix(THU-555): gate member actions on granular permission keys

* fix(THU-551): expose URL workspace id immediately + collapse chat-id on workspace switch

* fix(THU-550): wrap BrowserRouter in ErrorBoundary so bootstrap throws surface a screen

* chore: strip PR-ref noise from review-followup comments

* fix(THU-578): reset isReady before chat session eviction on workspace switch

* fix(THU-553): dedupe ModePicker validation between blur and Continue

* fix(THU-552): force ctx.userId for invitedByUserId in pending memberships

* fix(THU-552): reject malformed emails on pending membership writes

* fix(THU-578): persist selectedAgent against session.workspaceId not thread

* fix(THU-556): hide chat-skills-bar when no edit perm and no pinned chips

* docs: refresh workspace-memberships handler docstring for permission-key model

* test(THU-578): seed workspace context in LinkPreviewWidget fallback tests

* fix(THU-553): reset isValidating on SET_URL to unstick Continue after stale blur

* fix(THU-553): reset stage to picker on stale-URL bail-out in handleContinue

* fix(THU-549): pass captured serverId to handleFullWipe after registry clear

* fix(THU-555): align pending-row gates + block admin escalation via invite

* refactor(THU-555): use isResolved for membership loading distinction

* refactor(THU-555): assign openRef in render body, drop useEffect

* fix(THU-555): widen duplicate-slug suffix to 8 chars for collision resistance

* docs(THU-555): refresh stale comments referencing removed manage_members guard

* fix(THU-555): mirror serverUrl ref synchronously in mode-picker onChange

* fix(THU-555): sync pending memberships to all workspace members

* fix(THU-555): require change_roles for membership PUT that changes existing role

* fix(THU-549): keep getAuthToken resolvable through signOut after registry clear

* fix(THU-555): add last-admin guard to membership PUT apply path

* fix(THU-555): gate pending-row Admin option on change_roles
raivieiraadriano92 added a commit that referenced this pull request Jun 16, 2026
…plicate) (#971)

* fix(THU-554): align PageHeader actions with content padding

* feat(THU-554): allow personal workspace rename via upload handler

* feat(THU-554): updateWorkspaceName DAL, default name, createdAt stamps

* feat(THU-554): useActiveWorkspaceMembership hook

* feat(THU-554): split settings sidebar into Account Settings and Workspace groups

* feat(THU-554): Workspace Settings page with autosave rename

* feat(THU-554): add slug + icon columns to workspaces

* feat(THU-554): workspace slug input with auto-derive from name

* feat(THU-554): workspace icon picker (emoji or image upload)

* feat(THU-554): render workspace icon in the sidebar selector

* feat(THU-554): share WorkspaceFormFields between settings and create modal

* feat(THU-554): duplicate workspace action

* feat(THU-555): Settings → Workspace → Members (add / remove / role / pending) (#974)

* feat(THU-555): add user_name/user_email to workspace_memberships

* feat(THU-555): enrich membership upload handler with user display info

* feat(THU-555): propagate user name/email changes to memberships

* feat(THU-555): mirror userName/userEmail on FE workspace_memberships

* feat(THU-555): reactive members + pending queries

* feat(THU-555): membership mutation DAL

* feat(THU-555): useWorkspacePermission hook

* feat(THU-555): RequireWorkspacePermission route wrapper

* feat(THU-555): Members page scaffold + route

* feat(THU-555): Members sidebar entry gated by manage_members

* feat(THU-555): render members table with role dropdown

* feat(THU-555): wire Remove member with confirmation

* feat(THU-555): use globe icon for General sidebar entry

* fix(THU-555): rename SCREAMING_SNAKE constant to camelCase

* feat(THU-593): gate member management on E2EE-enabled servers

* fix(chat-store): dedupe concurrent hydrateChatStore calls

* feat(THU-556): Settings → Workspace → Permissions page + resource-CRUD enforcement (#978)

* feat(THU-556): setWorkspacePermissionRequiredRole DAL

* feat(THU-556): RequireWorkspaceAdmin route guard

* refactor(THU-556): consolidate workspace permission keys into shared/workspaces.ts

* feat(THU-556): Permissions page (11 keys, labels only)

* feat(THU-556): Permissions sidebar entry

* feat(THU-556): mount Permissions route

* feat(THU-556): refine Permissions page copy and layout

* feat(THU-556): scope Permissions to agents/skills/models/mcps

* feat(THU-556): use Lock icon for Permissions sidebar entry

* feat(THU-556): permissionAllows + BE permission lookup helpers

* feat(THU-556): permission-key gating in workspace-scoped handler

* feat(THU-556): enforce add_agents/remove_agents permissions

* feat(THU-556): treat soft-delete PATCH as remove for permission gating

* feat(THU-556): enforce add_skills/remove_skills permissions

* feat(THU-556): enforce add_models/remove_models permissions

* feat(THU-556): enforce add_mcp_servers/remove_mcp_servers permissions

* feat(THU-556): gate edit + chat bypasses, add permission gating tests

* fix(workspaces): address review comments across stacked PRs (#988)

* fix(THU-549): clear auth token after signOut so server can revoke session

* fix(THU-549): clear registry before db-closing so reloaded tabs land at ModePicker

* fix(THU-549): guard LogoutModal handleLogout against rapid double-click

* fix(THU-549): friendlier ZodError for unset/invalid SERVER_ID

* fix(THU-549): remove Cloud URL dev-settings input (use ModePicker flow instead)

* fix(THU-552): preserve existing membership role on promote-on-insert

* fix(THU-552): delete pending row by (workspace_id, email) after promote-on-insert

* fix(THU-552): skip onCreated when create-workspace modal dismissed mid-flight

* fix(THU-554): preserve slug/icon on PUT when payload omits them

* fix(THU-554): reflect remote workspace updates into settings form

* fix(THU-554): append random suffix to duplicate workspace slug

* fix(THU-555): resolve workspace permission state when user has no membership

* fix(THU-555): gate member actions on granular permission keys

* fix(THU-551): expose URL workspace id immediately + collapse chat-id on workspace switch

* fix(THU-550): wrap BrowserRouter in ErrorBoundary so bootstrap throws surface a screen

* chore: strip PR-ref noise from review-followup comments

* fix(THU-578): reset isReady before chat session eviction on workspace switch

* fix(THU-553): dedupe ModePicker validation between blur and Continue

* fix(THU-552): force ctx.userId for invitedByUserId in pending memberships

* fix(THU-552): reject malformed emails on pending membership writes

* fix(THU-578): persist selectedAgent against session.workspaceId not thread

* fix(THU-556): hide chat-skills-bar when no edit perm and no pinned chips

* docs: refresh workspace-memberships handler docstring for permission-key model

* test(THU-578): seed workspace context in LinkPreviewWidget fallback tests

* fix(THU-553): reset isValidating on SET_URL to unstick Continue after stale blur

* fix(THU-553): reset stage to picker on stale-URL bail-out in handleContinue

* fix(THU-549): pass captured serverId to handleFullWipe after registry clear

* fix(THU-555): align pending-row gates + block admin escalation via invite

* refactor(THU-555): use isResolved for membership loading distinction

* refactor(THU-555): assign openRef in render body, drop useEffect

* fix(THU-555): widen duplicate-slug suffix to 8 chars for collision resistance

* docs(THU-555): refresh stale comments referencing removed manage_members guard

* fix(THU-555): mirror serverUrl ref synchronously in mode-picker onChange

* fix(THU-555): sync pending memberships to all workspace members

* fix(THU-555): require change_roles for membership PUT that changes existing role

* fix(THU-549): keep getAuthToken resolvable through signOut after registry clear

* fix(THU-555): add last-admin guard to membership PUT apply path

* fix(THU-555): gate pending-row Admin option on change_roles
raivieiraadriano92 added a commit that referenced this pull request Jun 16, 2026
* feat(THU-552): promote pending membership to active on email match

* feat(THU-552): createSharedWorkspace DAL function

* feat(THU-552): useCanCreateWorkspace hook

* feat(THU-552): EmailChipInput component

* feat(THU-552): CreateWorkspaceModal component

* feat(THU-552): Create workspace button in sidebar selector footer

* feat(THU-552): seed defaults into newly-created shared workspace

* fix(THU-552): scope cleanupRemovedDefaults to a workspace

* feat(THU-552): split create + invite into two modals, single Create CTA

* feat(THU-552): workspace selector design polish

* fix(THU-552): rename SCREAMING_SNAKE constants to camelCase

* feat(THU-554): Settings → Workspace → General (rename, slug, icon, duplicate) (#971)

* fix(THU-554): align PageHeader actions with content padding

* feat(THU-554): allow personal workspace rename via upload handler

* feat(THU-554): updateWorkspaceName DAL, default name, createdAt stamps

* feat(THU-554): useActiveWorkspaceMembership hook

* feat(THU-554): split settings sidebar into Account Settings and Workspace groups

* feat(THU-554): Workspace Settings page with autosave rename

* feat(THU-554): add slug + icon columns to workspaces

* feat(THU-554): workspace slug input with auto-derive from name

* feat(THU-554): workspace icon picker (emoji or image upload)

* feat(THU-554): render workspace icon in the sidebar selector

* feat(THU-554): share WorkspaceFormFields between settings and create modal

* feat(THU-554): duplicate workspace action

* feat(THU-555): Settings → Workspace → Members (add / remove / role / pending) (#974)

* feat(THU-555): add user_name/user_email to workspace_memberships

* feat(THU-555): enrich membership upload handler with user display info

* feat(THU-555): propagate user name/email changes to memberships

* feat(THU-555): mirror userName/userEmail on FE workspace_memberships

* feat(THU-555): reactive members + pending queries

* feat(THU-555): membership mutation DAL

* feat(THU-555): useWorkspacePermission hook

* feat(THU-555): RequireWorkspacePermission route wrapper

* feat(THU-555): Members page scaffold + route

* feat(THU-555): Members sidebar entry gated by manage_members

* feat(THU-555): render members table with role dropdown

* feat(THU-555): wire Remove member with confirmation

* feat(THU-555): use globe icon for General sidebar entry

* fix(THU-555): rename SCREAMING_SNAKE constant to camelCase

* feat(THU-593): gate member management on E2EE-enabled servers

* fix(chat-store): dedupe concurrent hydrateChatStore calls

* feat(THU-556): Settings → Workspace → Permissions page + resource-CRUD enforcement (#978)

* feat(THU-556): setWorkspacePermissionRequiredRole DAL

* feat(THU-556): RequireWorkspaceAdmin route guard

* refactor(THU-556): consolidate workspace permission keys into shared/workspaces.ts

* feat(THU-556): Permissions page (11 keys, labels only)

* feat(THU-556): Permissions sidebar entry

* feat(THU-556): mount Permissions route

* feat(THU-556): refine Permissions page copy and layout

* feat(THU-556): scope Permissions to agents/skills/models/mcps

* feat(THU-556): use Lock icon for Permissions sidebar entry

* feat(THU-556): permissionAllows + BE permission lookup helpers

* feat(THU-556): permission-key gating in workspace-scoped handler

* feat(THU-556): enforce add_agents/remove_agents permissions

* feat(THU-556): treat soft-delete PATCH as remove for permission gating

* feat(THU-556): enforce add_skills/remove_skills permissions

* feat(THU-556): enforce add_models/remove_models permissions

* feat(THU-556): enforce add_mcp_servers/remove_mcp_servers permissions

* feat(THU-556): gate edit + chat bypasses, add permission gating tests

* fix(workspaces): address review comments across stacked PRs (#988)

* fix(THU-549): clear auth token after signOut so server can revoke session

* fix(THU-549): clear registry before db-closing so reloaded tabs land at ModePicker

* fix(THU-549): guard LogoutModal handleLogout against rapid double-click

* fix(THU-549): friendlier ZodError for unset/invalid SERVER_ID

* fix(THU-549): remove Cloud URL dev-settings input (use ModePicker flow instead)

* fix(THU-552): preserve existing membership role on promote-on-insert

* fix(THU-552): delete pending row by (workspace_id, email) after promote-on-insert

* fix(THU-552): skip onCreated when create-workspace modal dismissed mid-flight

* fix(THU-554): preserve slug/icon on PUT when payload omits them

* fix(THU-554): reflect remote workspace updates into settings form

* fix(THU-554): append random suffix to duplicate workspace slug

* fix(THU-555): resolve workspace permission state when user has no membership

* fix(THU-555): gate member actions on granular permission keys

* fix(THU-551): expose URL workspace id immediately + collapse chat-id on workspace switch

* fix(THU-550): wrap BrowserRouter in ErrorBoundary so bootstrap throws surface a screen

* chore: strip PR-ref noise from review-followup comments

* fix(THU-578): reset isReady before chat session eviction on workspace switch

* fix(THU-553): dedupe ModePicker validation between blur and Continue

* fix(THU-552): force ctx.userId for invitedByUserId in pending memberships

* fix(THU-552): reject malformed emails on pending membership writes

* fix(THU-578): persist selectedAgent against session.workspaceId not thread

* fix(THU-556): hide chat-skills-bar when no edit perm and no pinned chips

* docs: refresh workspace-memberships handler docstring for permission-key model

* test(THU-578): seed workspace context in LinkPreviewWidget fallback tests

* fix(THU-553): reset isValidating on SET_URL to unstick Continue after stale blur

* fix(THU-553): reset stage to picker on stale-URL bail-out in handleContinue

* fix(THU-549): pass captured serverId to handleFullWipe after registry clear

* fix(THU-555): align pending-row gates + block admin escalation via invite

* refactor(THU-555): use isResolved for membership loading distinction

* refactor(THU-555): assign openRef in render body, drop useEffect

* fix(THU-555): widen duplicate-slug suffix to 8 chars for collision resistance

* docs(THU-555): refresh stale comments referencing removed manage_members guard

* fix(THU-555): mirror serverUrl ref synchronously in mode-picker onChange

* fix(THU-555): sync pending memberships to all workspace members

* fix(THU-555): require change_roles for membership PUT that changes existing role

* fix(THU-549): keep getAuthToken resolvable through signOut after registry clear

* fix(THU-555): add last-admin guard to membership PUT apply path

* fix(THU-555): gate pending-row Admin option on change_roles
raivieiraadriano92 added a commit that referenced this pull request Jun 16, 2026
…pace (#961)

* feat(THU-551): reactive active-workspace + URL helpers

* feat(THU-551): useWorkspacesQuery hook + DAL test

* feat(THU-551): WorkspaceMembershipGate + live membership query

* feat(THU-551): mount workspace routes under /w/:workspaceId prefix

* feat(THU-551): workspace selector in sidebar header

* feat(THU-551): thread workspace prefix through navigation call sites

* feat(THU-552): create workspace modal — name + invite by email (#965)

* feat(THU-552): promote pending membership to active on email match

* feat(THU-552): createSharedWorkspace DAL function

* feat(THU-552): useCanCreateWorkspace hook

* feat(THU-552): EmailChipInput component

* feat(THU-552): CreateWorkspaceModal component

* feat(THU-552): Create workspace button in sidebar selector footer

* feat(THU-552): seed defaults into newly-created shared workspace

* fix(THU-552): scope cleanupRemovedDefaults to a workspace

* feat(THU-552): split create + invite into two modals, single Create CTA

* feat(THU-552): workspace selector design polish

* fix(THU-552): rename SCREAMING_SNAKE constants to camelCase

* feat(THU-554): Settings → Workspace → General (rename, slug, icon, duplicate) (#971)

* fix(THU-554): align PageHeader actions with content padding

* feat(THU-554): allow personal workspace rename via upload handler

* feat(THU-554): updateWorkspaceName DAL, default name, createdAt stamps

* feat(THU-554): useActiveWorkspaceMembership hook

* feat(THU-554): split settings sidebar into Account Settings and Workspace groups

* feat(THU-554): Workspace Settings page with autosave rename

* feat(THU-554): add slug + icon columns to workspaces

* feat(THU-554): workspace slug input with auto-derive from name

* feat(THU-554): workspace icon picker (emoji or image upload)

* feat(THU-554): render workspace icon in the sidebar selector

* feat(THU-554): share WorkspaceFormFields between settings and create modal

* feat(THU-554): duplicate workspace action

* feat(THU-555): Settings → Workspace → Members (add / remove / role / pending) (#974)

* feat(THU-555): add user_name/user_email to workspace_memberships

* feat(THU-555): enrich membership upload handler with user display info

* feat(THU-555): propagate user name/email changes to memberships

* feat(THU-555): mirror userName/userEmail on FE workspace_memberships

* feat(THU-555): reactive members + pending queries

* feat(THU-555): membership mutation DAL

* feat(THU-555): useWorkspacePermission hook

* feat(THU-555): RequireWorkspacePermission route wrapper

* feat(THU-555): Members page scaffold + route

* feat(THU-555): Members sidebar entry gated by manage_members

* feat(THU-555): render members table with role dropdown

* feat(THU-555): wire Remove member with confirmation

* feat(THU-555): use globe icon for General sidebar entry

* fix(THU-555): rename SCREAMING_SNAKE constant to camelCase

* feat(THU-593): gate member management on E2EE-enabled servers

* fix(chat-store): dedupe concurrent hydrateChatStore calls

* feat(THU-556): Settings → Workspace → Permissions page + resource-CRUD enforcement (#978)

* feat(THU-556): setWorkspacePermissionRequiredRole DAL

* feat(THU-556): RequireWorkspaceAdmin route guard

* refactor(THU-556): consolidate workspace permission keys into shared/workspaces.ts

* feat(THU-556): Permissions page (11 keys, labels only)

* feat(THU-556): Permissions sidebar entry

* feat(THU-556): mount Permissions route

* feat(THU-556): refine Permissions page copy and layout

* feat(THU-556): scope Permissions to agents/skills/models/mcps

* feat(THU-556): use Lock icon for Permissions sidebar entry

* feat(THU-556): permissionAllows + BE permission lookup helpers

* feat(THU-556): permission-key gating in workspace-scoped handler

* feat(THU-556): enforce add_agents/remove_agents permissions

* feat(THU-556): treat soft-delete PATCH as remove for permission gating

* feat(THU-556): enforce add_skills/remove_skills permissions

* feat(THU-556): enforce add_models/remove_models permissions

* feat(THU-556): enforce add_mcp_servers/remove_mcp_servers permissions

* feat(THU-556): gate edit + chat bypasses, add permission gating tests

* fix(workspaces): address review comments across stacked PRs (#988)

* fix(THU-549): clear auth token after signOut so server can revoke session

* fix(THU-549): clear registry before db-closing so reloaded tabs land at ModePicker

* fix(THU-549): guard LogoutModal handleLogout against rapid double-click

* fix(THU-549): friendlier ZodError for unset/invalid SERVER_ID

* fix(THU-549): remove Cloud URL dev-settings input (use ModePicker flow instead)

* fix(THU-552): preserve existing membership role on promote-on-insert

* fix(THU-552): delete pending row by (workspace_id, email) after promote-on-insert

* fix(THU-552): skip onCreated when create-workspace modal dismissed mid-flight

* fix(THU-554): preserve slug/icon on PUT when payload omits them

* fix(THU-554): reflect remote workspace updates into settings form

* fix(THU-554): append random suffix to duplicate workspace slug

* fix(THU-555): resolve workspace permission state when user has no membership

* fix(THU-555): gate member actions on granular permission keys

* fix(THU-551): expose URL workspace id immediately + collapse chat-id on workspace switch

* fix(THU-550): wrap BrowserRouter in ErrorBoundary so bootstrap throws surface a screen

* chore: strip PR-ref noise from review-followup comments

* fix(THU-578): reset isReady before chat session eviction on workspace switch

* fix(THU-553): dedupe ModePicker validation between blur and Continue

* fix(THU-552): force ctx.userId for invitedByUserId in pending memberships

* fix(THU-552): reject malformed emails on pending membership writes

* fix(THU-578): persist selectedAgent against session.workspaceId not thread

* fix(THU-556): hide chat-skills-bar when no edit perm and no pinned chips

* docs: refresh workspace-memberships handler docstring for permission-key model

* test(THU-578): seed workspace context in LinkPreviewWidget fallback tests

* fix(THU-553): reset isValidating on SET_URL to unstick Continue after stale blur

* fix(THU-553): reset stage to picker on stale-URL bail-out in handleContinue

* fix(THU-549): pass captured serverId to handleFullWipe after registry clear

* fix(THU-555): align pending-row gates + block admin escalation via invite

* refactor(THU-555): use isResolved for membership loading distinction

* refactor(THU-555): assign openRef in render body, drop useEffect

* fix(THU-555): widen duplicate-slug suffix to 8 chars for collision resistance

* docs(THU-555): refresh stale comments referencing removed manage_members guard

* fix(THU-555): mirror serverUrl ref synchronously in mode-picker onChange

* fix(THU-555): sync pending memberships to all workspace members

* fix(THU-555): require change_roles for membership PUT that changes existing role

* fix(THU-549): keep getAuthToken resolvable through signOut after registry clear

* fix(THU-555): add last-admin guard to membership PUT apply path

* fix(THU-555): gate pending-row Admin option on change_roles
raivieiraadriano92 added a commit that referenced this pull request Jun 16, 2026
…vity (#958)

* feat(THU-553): polish mode-picker UI — layout, selection state, and connectivity

* fix(THU-553): DI for mode-picker, drop mock.module('@/lib/http') leakage

* feat(THU-551): workspace selector in sidebar + URL-based active workspace (#961)

* feat(THU-551): reactive active-workspace + URL helpers

* feat(THU-551): useWorkspacesQuery hook + DAL test

* feat(THU-551): WorkspaceMembershipGate + live membership query

* feat(THU-551): mount workspace routes under /w/:workspaceId prefix

* feat(THU-551): workspace selector in sidebar header

* feat(THU-551): thread workspace prefix through navigation call sites

* feat(THU-552): create workspace modal — name + invite by email (#965)

* feat(THU-552): promote pending membership to active on email match

* feat(THU-552): createSharedWorkspace DAL function

* feat(THU-552): useCanCreateWorkspace hook

* feat(THU-552): EmailChipInput component

* feat(THU-552): CreateWorkspaceModal component

* feat(THU-552): Create workspace button in sidebar selector footer

* feat(THU-552): seed defaults into newly-created shared workspace

* fix(THU-552): scope cleanupRemovedDefaults to a workspace

* feat(THU-552): split create + invite into two modals, single Create CTA

* feat(THU-552): workspace selector design polish

* fix(THU-552): rename SCREAMING_SNAKE constants to camelCase

* feat(THU-554): Settings → Workspace → General (rename, slug, icon, duplicate) (#971)

* fix(THU-554): align PageHeader actions with content padding

* feat(THU-554): allow personal workspace rename via upload handler

* feat(THU-554): updateWorkspaceName DAL, default name, createdAt stamps

* feat(THU-554): useActiveWorkspaceMembership hook

* feat(THU-554): split settings sidebar into Account Settings and Workspace groups

* feat(THU-554): Workspace Settings page with autosave rename

* feat(THU-554): add slug + icon columns to workspaces

* feat(THU-554): workspace slug input with auto-derive from name

* feat(THU-554): workspace icon picker (emoji or image upload)

* feat(THU-554): render workspace icon in the sidebar selector

* feat(THU-554): share WorkspaceFormFields between settings and create modal

* feat(THU-554): duplicate workspace action

* feat(THU-555): Settings → Workspace → Members (add / remove / role / pending) (#974)

* feat(THU-555): add user_name/user_email to workspace_memberships

* feat(THU-555): enrich membership upload handler with user display info

* feat(THU-555): propagate user name/email changes to memberships

* feat(THU-555): mirror userName/userEmail on FE workspace_memberships

* feat(THU-555): reactive members + pending queries

* feat(THU-555): membership mutation DAL

* feat(THU-555): useWorkspacePermission hook

* feat(THU-555): RequireWorkspacePermission route wrapper

* feat(THU-555): Members page scaffold + route

* feat(THU-555): Members sidebar entry gated by manage_members

* feat(THU-555): render members table with role dropdown

* feat(THU-555): wire Remove member with confirmation

* feat(THU-555): use globe icon for General sidebar entry

* fix(THU-555): rename SCREAMING_SNAKE constant to camelCase

* feat(THU-593): gate member management on E2EE-enabled servers

* fix(chat-store): dedupe concurrent hydrateChatStore calls

* feat(THU-556): Settings → Workspace → Permissions page + resource-CRUD enforcement (#978)

* feat(THU-556): setWorkspacePermissionRequiredRole DAL

* feat(THU-556): RequireWorkspaceAdmin route guard

* refactor(THU-556): consolidate workspace permission keys into shared/workspaces.ts

* feat(THU-556): Permissions page (11 keys, labels only)

* feat(THU-556): Permissions sidebar entry

* feat(THU-556): mount Permissions route

* feat(THU-556): refine Permissions page copy and layout

* feat(THU-556): scope Permissions to agents/skills/models/mcps

* feat(THU-556): use Lock icon for Permissions sidebar entry

* feat(THU-556): permissionAllows + BE permission lookup helpers

* feat(THU-556): permission-key gating in workspace-scoped handler

* feat(THU-556): enforce add_agents/remove_agents permissions

* feat(THU-556): treat soft-delete PATCH as remove for permission gating

* feat(THU-556): enforce add_skills/remove_skills permissions

* feat(THU-556): enforce add_models/remove_models permissions

* feat(THU-556): enforce add_mcp_servers/remove_mcp_servers permissions

* feat(THU-556): gate edit + chat bypasses, add permission gating tests

* fix(workspaces): address review comments across stacked PRs (#988)

* fix(THU-549): clear auth token after signOut so server can revoke session

* fix(THU-549): clear registry before db-closing so reloaded tabs land at ModePicker

* fix(THU-549): guard LogoutModal handleLogout against rapid double-click

* fix(THU-549): friendlier ZodError for unset/invalid SERVER_ID

* fix(THU-549): remove Cloud URL dev-settings input (use ModePicker flow instead)

* fix(THU-552): preserve existing membership role on promote-on-insert

* fix(THU-552): delete pending row by (workspace_id, email) after promote-on-insert

* fix(THU-552): skip onCreated when create-workspace modal dismissed mid-flight

* fix(THU-554): preserve slug/icon on PUT when payload omits them

* fix(THU-554): reflect remote workspace updates into settings form

* fix(THU-554): append random suffix to duplicate workspace slug

* fix(THU-555): resolve workspace permission state when user has no membership

* fix(THU-555): gate member actions on granular permission keys

* fix(THU-551): expose URL workspace id immediately + collapse chat-id on workspace switch

* fix(THU-550): wrap BrowserRouter in ErrorBoundary so bootstrap throws surface a screen

* chore: strip PR-ref noise from review-followup comments

* fix(THU-578): reset isReady before chat session eviction on workspace switch

* fix(THU-553): dedupe ModePicker validation between blur and Continue

* fix(THU-552): force ctx.userId for invitedByUserId in pending memberships

* fix(THU-552): reject malformed emails on pending membership writes

* fix(THU-578): persist selectedAgent against session.workspaceId not thread

* fix(THU-556): hide chat-skills-bar when no edit perm and no pinned chips

* docs: refresh workspace-memberships handler docstring for permission-key model

* test(THU-578): seed workspace context in LinkPreviewWidget fallback tests

* fix(THU-553): reset isValidating on SET_URL to unstick Continue after stale blur

* fix(THU-553): reset stage to picker on stale-URL bail-out in handleContinue

* fix(THU-549): pass captured serverId to handleFullWipe after registry clear

* fix(THU-555): align pending-row gates + block admin escalation via invite

* refactor(THU-555): use isResolved for membership loading distinction

* refactor(THU-555): assign openRef in render body, drop useEffect

* fix(THU-555): widen duplicate-slug suffix to 8 chars for collision resistance

* docs(THU-555): refresh stale comments referencing removed manage_members guard

* fix(THU-555): mirror serverUrl ref synchronously in mode-picker onChange

* fix(THU-555): sync pending memberships to all workspace members

* fix(THU-555): require change_roles for membership PUT that changes existing role

* fix(THU-549): keep getAuthToken resolvable through signOut after registry clear

* fix(THU-555): add last-admin guard to membership PUT apply path

* fix(THU-555): gate pending-row Admin option on change_roles
raivieiraadriano92 added a commit that referenced this pull request Jun 16, 2026
* feat(THU-578): scope all FE DAL methods to active workspaceId

* fix(THU-578): seed trust domain in eval CLI + scope model-profile upsert by workspace

* fix(THU-578): retry hydration when workspaceId resolves after WorkspaceGate

* fix(THU-578): evict stale chat session on workspace switch

* fix(THU-578): scope prompt join in getTriggerPromptForThread to active workspace

* fix(THU-578): fix random test failures from workspaceId resolution races

* feat(THU-553): mode picker UI — layout, selection state, and connectivity (#958)

* feat(THU-553): polish mode-picker UI — layout, selection state, and connectivity

* fix(THU-553): DI for mode-picker, drop mock.module('@/lib/http') leakage

* feat(THU-551): workspace selector in sidebar + URL-based active workspace (#961)

* feat(THU-551): reactive active-workspace + URL helpers

* feat(THU-551): useWorkspacesQuery hook + DAL test

* feat(THU-551): WorkspaceMembershipGate + live membership query

* feat(THU-551): mount workspace routes under /w/:workspaceId prefix

* feat(THU-551): workspace selector in sidebar header

* feat(THU-551): thread workspace prefix through navigation call sites

* feat(THU-552): create workspace modal — name + invite by email (#965)

* feat(THU-552): promote pending membership to active on email match

* feat(THU-552): createSharedWorkspace DAL function

* feat(THU-552): useCanCreateWorkspace hook

* feat(THU-552): EmailChipInput component

* feat(THU-552): CreateWorkspaceModal component

* feat(THU-552): Create workspace button in sidebar selector footer

* feat(THU-552): seed defaults into newly-created shared workspace

* fix(THU-552): scope cleanupRemovedDefaults to a workspace

* feat(THU-552): split create + invite into two modals, single Create CTA

* feat(THU-552): workspace selector design polish

* fix(THU-552): rename SCREAMING_SNAKE constants to camelCase

* feat(THU-554): Settings → Workspace → General (rename, slug, icon, duplicate) (#971)

* fix(THU-554): align PageHeader actions with content padding

* feat(THU-554): allow personal workspace rename via upload handler

* feat(THU-554): updateWorkspaceName DAL, default name, createdAt stamps

* feat(THU-554): useActiveWorkspaceMembership hook

* feat(THU-554): split settings sidebar into Account Settings and Workspace groups

* feat(THU-554): Workspace Settings page with autosave rename

* feat(THU-554): add slug + icon columns to workspaces

* feat(THU-554): workspace slug input with auto-derive from name

* feat(THU-554): workspace icon picker (emoji or image upload)

* feat(THU-554): render workspace icon in the sidebar selector

* feat(THU-554): share WorkspaceFormFields between settings and create modal

* feat(THU-554): duplicate workspace action

* feat(THU-555): Settings → Workspace → Members (add / remove / role / pending) (#974)

* feat(THU-555): add user_name/user_email to workspace_memberships

* feat(THU-555): enrich membership upload handler with user display info

* feat(THU-555): propagate user name/email changes to memberships

* feat(THU-555): mirror userName/userEmail on FE workspace_memberships

* feat(THU-555): reactive members + pending queries

* feat(THU-555): membership mutation DAL

* feat(THU-555): useWorkspacePermission hook

* feat(THU-555): RequireWorkspacePermission route wrapper

* feat(THU-555): Members page scaffold + route

* feat(THU-555): Members sidebar entry gated by manage_members

* feat(THU-555): render members table with role dropdown

* feat(THU-555): wire Remove member with confirmation

* feat(THU-555): use globe icon for General sidebar entry

* fix(THU-555): rename SCREAMING_SNAKE constant to camelCase

* feat(THU-593): gate member management on E2EE-enabled servers

* fix(chat-store): dedupe concurrent hydrateChatStore calls

* feat(THU-556): Settings → Workspace → Permissions page + resource-CRUD enforcement (#978)

* feat(THU-556): setWorkspacePermissionRequiredRole DAL

* feat(THU-556): RequireWorkspaceAdmin route guard

* refactor(THU-556): consolidate workspace permission keys into shared/workspaces.ts

* feat(THU-556): Permissions page (11 keys, labels only)

* feat(THU-556): Permissions sidebar entry

* feat(THU-556): mount Permissions route

* feat(THU-556): refine Permissions page copy and layout

* feat(THU-556): scope Permissions to agents/skills/models/mcps

* feat(THU-556): use Lock icon for Permissions sidebar entry

* feat(THU-556): permissionAllows + BE permission lookup helpers

* feat(THU-556): permission-key gating in workspace-scoped handler

* feat(THU-556): enforce add_agents/remove_agents permissions

* feat(THU-556): treat soft-delete PATCH as remove for permission gating

* feat(THU-556): enforce add_skills/remove_skills permissions

* feat(THU-556): enforce add_models/remove_models permissions

* feat(THU-556): enforce add_mcp_servers/remove_mcp_servers permissions

* feat(THU-556): gate edit + chat bypasses, add permission gating tests

* fix(workspaces): address review comments across stacked PRs (#988)

* fix(THU-549): clear auth token after signOut so server can revoke session

* fix(THU-549): clear registry before db-closing so reloaded tabs land at ModePicker

* fix(THU-549): guard LogoutModal handleLogout against rapid double-click

* fix(THU-549): friendlier ZodError for unset/invalid SERVER_ID

* fix(THU-549): remove Cloud URL dev-settings input (use ModePicker flow instead)

* fix(THU-552): preserve existing membership role on promote-on-insert

* fix(THU-552): delete pending row by (workspace_id, email) after promote-on-insert

* fix(THU-552): skip onCreated when create-workspace modal dismissed mid-flight

* fix(THU-554): preserve slug/icon on PUT when payload omits them

* fix(THU-554): reflect remote workspace updates into settings form

* fix(THU-554): append random suffix to duplicate workspace slug

* fix(THU-555): resolve workspace permission state when user has no membership

* fix(THU-555): gate member actions on granular permission keys

* fix(THU-551): expose URL workspace id immediately + collapse chat-id on workspace switch

* fix(THU-550): wrap BrowserRouter in ErrorBoundary so bootstrap throws surface a screen

* chore: strip PR-ref noise from review-followup comments

* fix(THU-578): reset isReady before chat session eviction on workspace switch

* fix(THU-553): dedupe ModePicker validation between blur and Continue

* fix(THU-552): force ctx.userId for invitedByUserId in pending memberships

* fix(THU-552): reject malformed emails on pending membership writes

* fix(THU-578): persist selectedAgent against session.workspaceId not thread

* fix(THU-556): hide chat-skills-bar when no edit perm and no pinned chips

* docs: refresh workspace-memberships handler docstring for permission-key model

* test(THU-578): seed workspace context in LinkPreviewWidget fallback tests

* fix(THU-553): reset isValidating on SET_URL to unstick Continue after stale blur

* fix(THU-553): reset stage to picker on stale-URL bail-out in handleContinue

* fix(THU-549): pass captured serverId to handleFullWipe after registry clear

* fix(THU-555): align pending-row gates + block admin escalation via invite

* refactor(THU-555): use isResolved for membership loading distinction

* refactor(THU-555): assign openRef in render body, drop useEffect

* fix(THU-555): widen duplicate-slug suffix to 8 chars for collision resistance

* docs(THU-555): refresh stale comments referencing removed manage_members guard

* fix(THU-555): mirror serverUrl ref synchronously in mode-picker onChange

* fix(THU-555): sync pending memberships to all workspace members

* fix(THU-555): require change_roles for membership PUT that changes existing role

* fix(THU-549): keep getAuthToken resolvable through signOut after registry clear

* fix(THU-555): add last-admin guard to membership PUT apply path

* fix(THU-555): gate pending-row Admin option on change_roles
raivieiraadriano92 added a commit that referenced this pull request Jun 16, 2026
* feat(THU-550): BE workspaces tables + post-create bootstrap hook

* feat(THU-550): PowerSync upload handler factory + workspace handlers

* feat(THU-550): workspace_id on scoped tables + sync rules + ws handler

* feat(THU-550): encrypt workspaces.name

* feat(THU-550): shared deterministic personal-workspace id helpers

* feat(THU-550): BE — FE-owned personal workspace creation

* feat(THU-550): FE — workspace-aware DAL + post-auth bootstrap + gate

* fix(THU-550): log permanently rejected upload ops

* fix(THU-550): pin workspace_id in PATCH/DELETE WHERE clause

* fix(THU-550): scope reconcile-defaults lookups and updates by workspace_id

* fix(THU-550): force personal workspace name + shared workspace admin bootstrap

* fix(THU-550): throw bootstrap error + update stale 400 upload test assertions

* fix(THU-550): add workspaceId to model profile defaults + fix getSystemTinfoilClient cloud URL

* fix(THU-550): restore BE test isolation broken by upload handler transactions

* ci(THU-550): install root deps before BE type-check (shared/ uses uuid)

* feat(THU-578): scope all FE DAL methods to active workspaceId (#951)

* feat(THU-578): scope all FE DAL methods to active workspaceId

* fix(THU-578): seed trust domain in eval CLI + scope model-profile upsert by workspace

* fix(THU-578): retry hydration when workspaceId resolves after WorkspaceGate

* fix(THU-578): evict stale chat session on workspace switch

* fix(THU-578): scope prompt join in getTriggerPromptForThread to active workspace

* fix(THU-578): fix random test failures from workspaceId resolution races

* feat(THU-553): mode picker UI — layout, selection state, and connectivity (#958)

* feat(THU-553): polish mode-picker UI — layout, selection state, and connectivity

* fix(THU-553): DI for mode-picker, drop mock.module('@/lib/http') leakage

* feat(THU-551): workspace selector in sidebar + URL-based active workspace (#961)

* feat(THU-551): reactive active-workspace + URL helpers

* feat(THU-551): useWorkspacesQuery hook + DAL test

* feat(THU-551): WorkspaceMembershipGate + live membership query

* feat(THU-551): mount workspace routes under /w/:workspaceId prefix

* feat(THU-551): workspace selector in sidebar header

* feat(THU-551): thread workspace prefix through navigation call sites

* feat(THU-552): create workspace modal — name + invite by email (#965)

* feat(THU-552): promote pending membership to active on email match

* feat(THU-552): createSharedWorkspace DAL function

* feat(THU-552): useCanCreateWorkspace hook

* feat(THU-552): EmailChipInput component

* feat(THU-552): CreateWorkspaceModal component

* feat(THU-552): Create workspace button in sidebar selector footer

* feat(THU-552): seed defaults into newly-created shared workspace

* fix(THU-552): scope cleanupRemovedDefaults to a workspace

* feat(THU-552): split create + invite into two modals, single Create CTA

* feat(THU-552): workspace selector design polish

* fix(THU-552): rename SCREAMING_SNAKE constants to camelCase

* feat(THU-554): Settings → Workspace → General (rename, slug, icon, duplicate) (#971)

* fix(THU-554): align PageHeader actions with content padding

* feat(THU-554): allow personal workspace rename via upload handler

* feat(THU-554): updateWorkspaceName DAL, default name, createdAt stamps

* feat(THU-554): useActiveWorkspaceMembership hook

* feat(THU-554): split settings sidebar into Account Settings and Workspace groups

* feat(THU-554): Workspace Settings page with autosave rename

* feat(THU-554): add slug + icon columns to workspaces

* feat(THU-554): workspace slug input with auto-derive from name

* feat(THU-554): workspace icon picker (emoji or image upload)

* feat(THU-554): render workspace icon in the sidebar selector

* feat(THU-554): share WorkspaceFormFields between settings and create modal

* feat(THU-554): duplicate workspace action

* feat(THU-555): Settings → Workspace → Members (add / remove / role / pending) (#974)

* feat(THU-555): add user_name/user_email to workspace_memberships

* feat(THU-555): enrich membership upload handler with user display info

* feat(THU-555): propagate user name/email changes to memberships

* feat(THU-555): mirror userName/userEmail on FE workspace_memberships

* feat(THU-555): reactive members + pending queries

* feat(THU-555): membership mutation DAL

* feat(THU-555): useWorkspacePermission hook

* feat(THU-555): RequireWorkspacePermission route wrapper

* feat(THU-555): Members page scaffold + route

* feat(THU-555): Members sidebar entry gated by manage_members

* feat(THU-555): render members table with role dropdown

* feat(THU-555): wire Remove member with confirmation

* feat(THU-555): use globe icon for General sidebar entry

* fix(THU-555): rename SCREAMING_SNAKE constant to camelCase

* feat(THU-593): gate member management on E2EE-enabled servers

* fix(chat-store): dedupe concurrent hydrateChatStore calls

* feat(THU-556): Settings → Workspace → Permissions page + resource-CRUD enforcement (#978)

* feat(THU-556): setWorkspacePermissionRequiredRole DAL

* feat(THU-556): RequireWorkspaceAdmin route guard

* refactor(THU-556): consolidate workspace permission keys into shared/workspaces.ts

* feat(THU-556): Permissions page (11 keys, labels only)

* feat(THU-556): Permissions sidebar entry

* feat(THU-556): mount Permissions route

* feat(THU-556): refine Permissions page copy and layout

* feat(THU-556): scope Permissions to agents/skills/models/mcps

* feat(THU-556): use Lock icon for Permissions sidebar entry

* feat(THU-556): permissionAllows + BE permission lookup helpers

* feat(THU-556): permission-key gating in workspace-scoped handler

* feat(THU-556): enforce add_agents/remove_agents permissions

* feat(THU-556): treat soft-delete PATCH as remove for permission gating

* feat(THU-556): enforce add_skills/remove_skills permissions

* feat(THU-556): enforce add_models/remove_models permissions

* feat(THU-556): enforce add_mcp_servers/remove_mcp_servers permissions

* feat(THU-556): gate edit + chat bypasses, add permission gating tests

* fix(workspaces): address review comments across stacked PRs (#988)

* fix(THU-549): clear auth token after signOut so server can revoke session

* fix(THU-549): clear registry before db-closing so reloaded tabs land at ModePicker

* fix(THU-549): guard LogoutModal handleLogout against rapid double-click

* fix(THU-549): friendlier ZodError for unset/invalid SERVER_ID

* fix(THU-549): remove Cloud URL dev-settings input (use ModePicker flow instead)

* fix(THU-552): preserve existing membership role on promote-on-insert

* fix(THU-552): delete pending row by (workspace_id, email) after promote-on-insert

* fix(THU-552): skip onCreated when create-workspace modal dismissed mid-flight

* fix(THU-554): preserve slug/icon on PUT when payload omits them

* fix(THU-554): reflect remote workspace updates into settings form

* fix(THU-554): append random suffix to duplicate workspace slug

* fix(THU-555): resolve workspace permission state when user has no membership

* fix(THU-555): gate member actions on granular permission keys

* fix(THU-551): expose URL workspace id immediately + collapse chat-id on workspace switch

* fix(THU-550): wrap BrowserRouter in ErrorBoundary so bootstrap throws surface a screen

* chore: strip PR-ref noise from review-followup comments

* fix(THU-578): reset isReady before chat session eviction on workspace switch

* fix(THU-553): dedupe ModePicker validation between blur and Continue

* fix(THU-552): force ctx.userId for invitedByUserId in pending memberships

* fix(THU-552): reject malformed emails on pending membership writes

* fix(THU-578): persist selectedAgent against session.workspaceId not thread

* fix(THU-556): hide chat-skills-bar when no edit perm and no pinned chips

* docs: refresh workspace-memberships handler docstring for permission-key model

* test(THU-578): seed workspace context in LinkPreviewWidget fallback tests

* fix(THU-553): reset isValidating on SET_URL to unstick Continue after stale blur

* fix(THU-553): reset stage to picker on stale-URL bail-out in handleContinue

* fix(THU-549): pass captured serverId to handleFullWipe after registry clear

* fix(THU-555): align pending-row gates + block admin escalation via invite

* refactor(THU-555): use isResolved for membership loading distinction

* refactor(THU-555): assign openRef in render body, drop useEffect

* fix(THU-555): widen duplicate-slug suffix to 8 chars for collision resistance

* docs(THU-555): refresh stale comments referencing removed manage_members guard

* fix(THU-555): mirror serverUrl ref synchronously in mode-picker onChange

* fix(THU-555): sync pending memberships to all workspace members

* fix(THU-555): require change_roles for membership PUT that changes existing role

* fix(THU-549): keep getAuthToken resolvable through signOut after registry clear

* fix(THU-555): add last-admin guard to membership PUT apply path

* fix(THU-555): gate pending-row Admin option on change_roles
raivieiraadriano92 added a commit that referenced this pull request Jun 16, 2026
* feat(THU-549): serverId + extended /v1/config payload

* feat(THU-549): trust-domain registry + namespaced auth/device/keys

* feat(THU-549): boot env vars + trust-domain decision tree

* feat(THU-549): scope DB filename by trust domain + lifecycle broadcast

* feat(THU-549): logout = unconditional wipe

* feat(THU-549): scope cloud URL by active server

* refactor(THU-549): collapse registry actions into activateServer/activateStandalone

* feat(THU-549): persist per-server session on ServerEntry + wire mirror

* refactor(THU-549): drop unused db-reopened event + subscribeDbLifecycle

* refactor(THU-549): extract shared sync-worker-name format

* fix(THU-549): pin SERVER_ID for e2e backends

* fix(THU-549): address PR review — timeout, Tauri OPFS, logout loading state, test SERVER_ID, auth-token let

* fix(THU-549): wipe IDB databases on logout + fix SSO redirect race + delete E2EE keys IDB

* fix(THU-549): use getActiveCloudUrl in getSystemTinfoilClient

* fix(THU-550): fix BE/FE tests broken by THU-549 trust-domain rebase

* feat(THU-550): Workspaces data layer (#944)

* feat(THU-550): BE workspaces tables + post-create bootstrap hook

* feat(THU-550): PowerSync upload handler factory + workspace handlers

* feat(THU-550): workspace_id on scoped tables + sync rules + ws handler

* feat(THU-550): encrypt workspaces.name

* feat(THU-550): shared deterministic personal-workspace id helpers

* feat(THU-550): BE — FE-owned personal workspace creation

* feat(THU-550): FE — workspace-aware DAL + post-auth bootstrap + gate

* fix(THU-550): log permanently rejected upload ops

* fix(THU-550): pin workspace_id in PATCH/DELETE WHERE clause

* fix(THU-550): scope reconcile-defaults lookups and updates by workspace_id

* fix(THU-550): force personal workspace name + shared workspace admin bootstrap

* fix(THU-550): throw bootstrap error + update stale 400 upload test assertions

* fix(THU-550): add workspaceId to model profile defaults + fix getSystemTinfoilClient cloud URL

* fix(THU-550): restore BE test isolation broken by upload handler transactions

* ci(THU-550): install root deps before BE type-check (shared/ uses uuid)

* feat(THU-578): scope all FE DAL methods to active workspaceId (#951)

* feat(THU-578): scope all FE DAL methods to active workspaceId

* fix(THU-578): seed trust domain in eval CLI + scope model-profile upsert by workspace

* fix(THU-578): retry hydration when workspaceId resolves after WorkspaceGate

* fix(THU-578): evict stale chat session on workspace switch

* fix(THU-578): scope prompt join in getTriggerPromptForThread to active workspace

* fix(THU-578): fix random test failures from workspaceId resolution races

* feat(THU-553): mode picker UI — layout, selection state, and connectivity (#958)

* feat(THU-553): polish mode-picker UI — layout, selection state, and connectivity

* fix(THU-553): DI for mode-picker, drop mock.module('@/lib/http') leakage

* feat(THU-551): workspace selector in sidebar + URL-based active workspace (#961)

* feat(THU-551): reactive active-workspace + URL helpers

* feat(THU-551): useWorkspacesQuery hook + DAL test

* feat(THU-551): WorkspaceMembershipGate + live membership query

* feat(THU-551): mount workspace routes under /w/:workspaceId prefix

* feat(THU-551): workspace selector in sidebar header

* feat(THU-551): thread workspace prefix through navigation call sites

* feat(THU-552): create workspace modal — name + invite by email (#965)

* feat(THU-552): promote pending membership to active on email match

* feat(THU-552): createSharedWorkspace DAL function

* feat(THU-552): useCanCreateWorkspace hook

* feat(THU-552): EmailChipInput component

* feat(THU-552): CreateWorkspaceModal component

* feat(THU-552): Create workspace button in sidebar selector footer

* feat(THU-552): seed defaults into newly-created shared workspace

* fix(THU-552): scope cleanupRemovedDefaults to a workspace

* feat(THU-552): split create + invite into two modals, single Create CTA

* feat(THU-552): workspace selector design polish

* fix(THU-552): rename SCREAMING_SNAKE constants to camelCase

* feat(THU-554): Settings → Workspace → General (rename, slug, icon, duplicate) (#971)

* fix(THU-554): align PageHeader actions with content padding

* feat(THU-554): allow personal workspace rename via upload handler

* feat(THU-554): updateWorkspaceName DAL, default name, createdAt stamps

* feat(THU-554): useActiveWorkspaceMembership hook

* feat(THU-554): split settings sidebar into Account Settings and Workspace groups

* feat(THU-554): Workspace Settings page with autosave rename

* feat(THU-554): add slug + icon columns to workspaces

* feat(THU-554): workspace slug input with auto-derive from name

* feat(THU-554): workspace icon picker (emoji or image upload)

* feat(THU-554): render workspace icon in the sidebar selector

* feat(THU-554): share WorkspaceFormFields between settings and create modal

* feat(THU-554): duplicate workspace action

* feat(THU-555): Settings → Workspace → Members (add / remove / role / pending) (#974)

* feat(THU-555): add user_name/user_email to workspace_memberships

* feat(THU-555): enrich membership upload handler with user display info

* feat(THU-555): propagate user name/email changes to memberships

* feat(THU-555): mirror userName/userEmail on FE workspace_memberships

* feat(THU-555): reactive members + pending queries

* feat(THU-555): membership mutation DAL

* feat(THU-555): useWorkspacePermission hook

* feat(THU-555): RequireWorkspacePermission route wrapper

* feat(THU-555): Members page scaffold + route

* feat(THU-555): Members sidebar entry gated by manage_members

* feat(THU-555): render members table with role dropdown

* feat(THU-555): wire Remove member with confirmation

* feat(THU-555): use globe icon for General sidebar entry

* fix(THU-555): rename SCREAMING_SNAKE constant to camelCase

* feat(THU-593): gate member management on E2EE-enabled servers

* fix(chat-store): dedupe concurrent hydrateChatStore calls

* feat(THU-556): Settings → Workspace → Permissions page + resource-CRUD enforcement (#978)

* feat(THU-556): setWorkspacePermissionRequiredRole DAL

* feat(THU-556): RequireWorkspaceAdmin route guard

* refactor(THU-556): consolidate workspace permission keys into shared/workspaces.ts

* feat(THU-556): Permissions page (11 keys, labels only)

* feat(THU-556): Permissions sidebar entry

* feat(THU-556): mount Permissions route

* feat(THU-556): refine Permissions page copy and layout

* feat(THU-556): scope Permissions to agents/skills/models/mcps

* feat(THU-556): use Lock icon for Permissions sidebar entry

* feat(THU-556): permissionAllows + BE permission lookup helpers

* feat(THU-556): permission-key gating in workspace-scoped handler

* feat(THU-556): enforce add_agents/remove_agents permissions

* feat(THU-556): treat soft-delete PATCH as remove for permission gating

* feat(THU-556): enforce add_skills/remove_skills permissions

* feat(THU-556): enforce add_models/remove_models permissions

* feat(THU-556): enforce add_mcp_servers/remove_mcp_servers permissions

* feat(THU-556): gate edit + chat bypasses, add permission gating tests

* fix(workspaces): address review comments across stacked PRs (#988)

* fix(THU-549): clear auth token after signOut so server can revoke session

* fix(THU-549): clear registry before db-closing so reloaded tabs land at ModePicker

* fix(THU-549): guard LogoutModal handleLogout against rapid double-click

* fix(THU-549): friendlier ZodError for unset/invalid SERVER_ID

* fix(THU-549): remove Cloud URL dev-settings input (use ModePicker flow instead)

* fix(THU-552): preserve existing membership role on promote-on-insert

* fix(THU-552): delete pending row by (workspace_id, email) after promote-on-insert

* fix(THU-552): skip onCreated when create-workspace modal dismissed mid-flight

* fix(THU-554): preserve slug/icon on PUT when payload omits them

* fix(THU-554): reflect remote workspace updates into settings form

* fix(THU-554): append random suffix to duplicate workspace slug

* fix(THU-555): resolve workspace permission state when user has no membership

* fix(THU-555): gate member actions on granular permission keys

* fix(THU-551): expose URL workspace id immediately + collapse chat-id on workspace switch

* fix(THU-550): wrap BrowserRouter in ErrorBoundary so bootstrap throws surface a screen

* chore: strip PR-ref noise from review-followup comments

* fix(THU-578): reset isReady before chat session eviction on workspace switch

* fix(THU-553): dedupe ModePicker validation between blur and Continue

* fix(THU-552): force ctx.userId for invitedByUserId in pending memberships

* fix(THU-552): reject malformed emails on pending membership writes

* fix(THU-578): persist selectedAgent against session.workspaceId not thread

* fix(THU-556): hide chat-skills-bar when no edit perm and no pinned chips

* docs: refresh workspace-memberships handler docstring for permission-key model

* test(THU-578): seed workspace context in LinkPreviewWidget fallback tests

* fix(THU-553): reset isValidating on SET_URL to unstick Continue after stale blur

* fix(THU-553): reset stage to picker on stale-URL bail-out in handleContinue

* fix(THU-549): pass captured serverId to handleFullWipe after registry clear

* fix(THU-555): align pending-row gates + block admin escalation via invite

* refactor(THU-555): use isResolved for membership loading distinction

* refactor(THU-555): assign openRef in render body, drop useEffect

* fix(THU-555): widen duplicate-slug suffix to 8 chars for collision resistance

* docs(THU-555): refresh stale comments referencing removed manage_members guard

* fix(THU-555): mirror serverUrl ref synchronously in mode-picker onChange

* fix(THU-555): sync pending memberships to all workspace members

* fix(THU-555): require change_roles for membership PUT that changes existing role

* fix(THU-549): keep getAuthToken resolvable through signOut after registry clear

* fix(THU-555): add last-admin guard to membership PUT apply path

* fix(THU-555): gate pending-row Admin option on change_roles
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants