Skip to content

improvement(workspaces): auto-add without invite if part of organization#5132

Merged
icecrasher321 merged 4 commits into
stagingfrom
feat/auto-add-ws-member
Jun 18, 2026
Merged

improvement(workspaces): auto-add without invite if part of organization#5132
icecrasher321 merged 4 commits into
stagingfrom
feat/auto-add-ws-member

Conversation

@icecrasher321

Copy link
Copy Markdown
Collaborator

Summary

Auto-add into workspace without invite acceptance if part of organization. Email still sent to let them know they've been added.

Type of Change

  • Other: UX Improvement

Testing

Tested manually

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel

vercel Bot commented Jun 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jun 18, 2026 10:35pm

Request Review

@cursor

cursor Bot commented Jun 18, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes who gets workspace permissions and how org batch invites behave (seats, partial 207 responses), but grants are idempotent and covered by new unit/route tests.

Overview
Existing organization members who are invited to org workspaces they do not yet belong to are now granted access immediately via grantWorkspaceAccessDirectly, instead of receiving pending workspace invitations. New emails still get organization invitations (acceptance still joins the org and uses seats where applicable).

The org batch invite API and workspace batch invite path return separate buckets: directlyAdded / directlyAddedCount vs invitationsSent / successful, with HTTP 207 for partial failures and clearer success messaging. Direct adds trigger audit (member.added), telemetry, optional “you’ve been added” email (WorkspaceAddedEmail), env credential sync, and cancellation of redundant pending workspace invites for that email.

Invite modals show toasts for “members added” vs “invites sent” and keep failed addresses for retry; org roster UI adds Remove from workspace for non–org-admin members. Client hooks treat partial org invite responses as success and invalidate workspace member/permission caches after direct adds.

Reviewed by Cursor Bugbot for commit 588701b. Configure here.

Comment thread apps/sim/lib/core/config/env-flags.ts Outdated
Comment thread apps/sim/app/api/workspaces/invitations/batch/route.ts Outdated
Comment thread apps/sim/lib/invitations/direct-grant.ts Outdated
Comment thread apps/sim/lib/invitations/send.ts Outdated
@greptile-apps

greptile-apps Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR introduces a "direct add" flow for workspace invitations: when the invitee is already a member of the workspace's organization, they are granted access immediately (no pending invitation, no acceptance step) and receive a lightweight "you've been added" notification email instead of an invite link.

  • direct-grant.ts — new idempotent core function that inserts a permission row in a transaction, then fires side-effects (env-var sync, telemetry, audit, "added" email) outside the transaction with appropriate isolation.
  • workspace-invitations.ts / route.ts — both call paths updated to route same-org registered users through the direct-grant path; new instantAdd/outcome fields distinguish direct additions from pending invitations in the batch response.
  • UI — org invite modal and batch invite modal both show granular toast feedback splitting "N members added" from "N invites sent".

Confidence Score: 5/5

Safe to merge — the direct-grant path is properly gated behind org-membership verification and uses an idempotent DB transaction; no auth boundaries are weakened.

The core permission insert is wrapped in a transaction with onConflictDoNothing so concurrent invites are handled cleanly. Seat accounting only counts org invitations, not direct grants, which is correct. Tests cover the main paths including the race-condition case. The open items (captureServerEvent not in try-catch, premature getUserOrganization call, id field overloading) are minor and do not affect correctness of access control.

apps/sim/lib/invitations/direct-grant.ts — captureServerEvent placement; apps/sim/lib/invitations/workspace-invitations.ts — early getUserOrganization call and id field semantics on the direct-grant return.

Important Files Changed

Filename Overview
apps/sim/lib/invitations/direct-grant.ts New core function that grants workspace access directly (no invite/acceptance). DB transaction is idempotent with onConflictDoNothing; post-commit side effects (env sync, telemetry, audit, email) are appropriately isolated except captureServerEvent which is not try-caught.
apps/sim/lib/invitations/workspace-invitations.ts Routes same-org registered users through grantWorkspaceAccessDirectly; getUserOrganization is now fetched before the existingPermission guard, adding an unnecessary DB call on rejections.
apps/sim/app/api/organizations/[id]/invitations/route.ts Splits the invitation loop into org invites (new emails) and direct grants (existing members); seat counting correctly uses only org invitations; audit, response shape, and partial-failure handling all look correct.
apps/sim/app/api/workspaces/invitations/batch/route.ts Correctly gates the added/successful buckets on instantAdd and outcome flags; cache invalidation for workspace permissions and members added on success.
apps/sim/components/emails/invitations/workspace-added-email.tsx New lightweight 'you've been added' email template; uses brand config correctly, no acceptance link needed.
apps/sim/lib/invitations/direct-grant.test.ts Comprehensive unit tests for grantWorkspaceAccessDirectly covering insert, concurrent-insert race, no-op, env-sync, supersede, and notify=false paths.
apps/sim/lib/invitations/workspace-invitations.test.ts New test file covering createWorkspaceInvitation for same-org direct grant, existing member rejection, external member, no-org member, and new email paths.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Invite email submitted] --> B{Registered user?}
    B -- No --> C[Create pending org/workspace invitation\n+ send invite email]
    B -- Yes --> D{Already has\nworkspace permission?}
    D -- Yes --> E[Reject: already has access]
    D -- No --> F{Same org as workspace?}
    F -- No --> G[Create pending workspace invitation\nmembershipIntent=external + send invite email]
    F -- Yes --> H[grantWorkspaceAccessDirectly\nin DB transaction]
    H --> I{outcome}
    I -- added --> J[Supersede pending invites\nSync env credentials\nAudit MEMBER_ADDED\nSend workspace-added email]
    I -- unchanged\nrace condition --> K[No-op, return unchanged]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[Invite email submitted] --> B{Registered user?}
    B -- No --> C[Create pending org/workspace invitation\n+ send invite email]
    B -- Yes --> D{Already has\nworkspace permission?}
    D -- Yes --> E[Reject: already has access]
    D -- No --> F{Same org as workspace?}
    F -- No --> G[Create pending workspace invitation\nmembershipIntent=external + send invite email]
    F -- Yes --> H[grantWorkspaceAccessDirectly\nin DB transaction]
    H --> I{outcome}
    I -- added --> J[Supersede pending invites\nSync env credentials\nAudit MEMBER_ADDED\nSend workspace-added email]
    I -- unchanged\nrace condition --> K[No-op, return unchanged]
Loading

Reviews (2): Last reviewed commit: "address comments" | Re-trigger Greptile

Comment thread apps/sim/lib/core/config/env-flags.ts Outdated
Comment thread apps/sim/lib/invitations/send.ts Outdated
Comment thread apps/sim/lib/invitations/workspace-invitations.ts
@icecrasher321

Copy link
Copy Markdown
Collaborator Author

@greptile

@icecrasher321

Copy link
Copy Markdown
Collaborator Author

bugbot run

@icecrasher321 icecrasher321 changed the title feat(workspaces): auto-add without invite if part of organization improvement(workspaces): auto-add without invite if part of organization Jun 18, 2026
Comment thread apps/sim/app/api/organizations/[id]/invitations/route.ts Outdated
@icecrasher321

Copy link
Copy Markdown
Collaborator Author

bugbot run

@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.

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit 588701b. Configure here.

Comment thread apps/sim/app/api/organizations/[id]/invitations/route.ts
@icecrasher321 icecrasher321 merged commit 267e49c into staging Jun 18, 2026
16 checks passed
@waleedlatif1 waleedlatif1 deleted the feat/auto-add-ws-member branch June 18, 2026 23:20
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.

1 participant