Repository navigation
chore: sync dev with staging (2026-11-03, project bootstrap) - #9612
Conversation
* docs(library): update best-zapier-alternatives * Pi Babysit: address PR #9592 feedback --------- Co-authored-by: Sim Pi Agent <pi@sim.ai>
…he files it already wrote (#9596) * improvement(sandbox): return a timed-out workbench run's output and the files it already wrote A run_code call killed at its own timeout returned an empty stdout and only "timed out after Nms", so models reran long jobs from scratch. E2B's SDK throws a stream-less TimeoutError, which dropped the output the stream callbacks had already observed. - E2B adapter: a timed-out command returns the stdout/stderr its callbacks observed - Workbench runs (session sandboxes) that hit their own timeout throw a timeout abort carrying the output tail (64 KB) and the files already in the call's output directory; every existing timeout check still accepts it - The route returns that output in stdout with a 408 and says the workbench and its files persist, so the next call can rerun only the remaining work - Output goes through the same session provenance check and secret-name reporting as any other result; one-shot sandboxes and billing are unchanged * fix(sandbox): keep a cancellation during the timed-out output listing a cancellation
…launch (#9606) Every warm workbench call ran a separate CLI integrity check command (ownership write, launch, stop receipt, settle) before the user's command, about a third of a warm call's fixed cost. - The session supervisor's PID 1 now runs the same check as the ordinary user, after recording itself and before starting the command; a mismatch starts nothing and is reported on the stop receipt the call already fetches - A refused launch is repaired under the session lock and relaunched once; a second refusal fails the call - The check runs with an isolated interpreter instead of the user's login shell, never follows links or blocks on non-regular files, and treats an unreadable artifact as stale so repair overwrites it - A reused machine runs no separate check command; a new machine still installs the CLI first - Gated live test against real E2B covers refusal, repair, hooks, FIFOs, Stop during a slow check, and the warm-call cost
…up members, and preview member removal (#9600)
…rganization, even after leaving (#9601)
* fix(organizations): let admins rename their organization * fix(organizations): keep the rename draft until it is saved or cancelled
* fix(credentials): let members leave a shared credential * fix(credentials): type the stale keys list * fix(credentials): match the last-admin guard to the server
… operation and let Chat rename workspaces (#9607) * improvement(workspaces): move workspace updates behind an application operation and let Chat rename workspaces PATCH/PUT /api/workspaces/[id] now run through defineInternalJsonRoute and the session-only workspaces.update use case, keeping the legacy 403/400/500 bodies, audit changes and response shape. Chat gets a workspace/general settings section backed by workspaces.identity.read and workspaces.identity.update, which admit only name and logo; billing payer and the personal-key policy stay session-only. * fix(workspaces): recheck active state atomically on update The workspace UPDATE now carries the not-archived predicate, so an archival that commits after the use case's reads leaves nothing to write and answers the same 404 as a missing workspace, on both the session route and Chat's identity update. The integration suite holds the row lock, archives the row while the update waits, and asserts the 404 and the unchanged name; the archived-fixture refusal now checks that fixture's own row.
… out other devices (#9595) * fix(organizations): let members leave their organization; remove sign out other devices * fix(organizations): hide Leave when the caller's role is unknown * fix(organizations): offer Leave only from an authoritative role * fix(organizations): use only a freshly fetched summary role for Leave * fix(organizations): refetch the summary role on mount
…ess connector (#9602) * fix(knowledge): let members connect their own account to a member-access connector * fix(knowledge): keep watching member enrollment until it connects
…tic card above the composer (#9608) * improvement(billing): open a Chat plan popup on send instead of a static card above the composer * improvement(billing): name the plan upgrade link input
…9597) * fix(sim-links): hand off a departing member's links to the successor * fix(sim-links): repair leftover links on account deletion * fix(sim-links): lock leftover-link organizations before deletion barriers * fix(sim-links): hold the creator's membership while creating a link
* improvement(chat): let Chat review organization access requests Organization admins can list, preview, approve and deny access requests from an organization conversation through the settings tool's new requests section. The admin list, preview and resolve operations now admit organization-delegated Copilot principals under the sim:settings audience; role, audit, outbox notice and fingerprint checks are unchanged. Request settings stay session-only. * fix(chat): default access request list to pending The settings tool parses list input before calling the reader, so an omitted status reached it as undefined and listed every status. The list schema now defaults status to pending, matching get; explicit statuses still apply.
…nd keep the renamed name visible (#9609) * fix(organizations): refuse self-leave for directory-managed members and keep the renamed name visible * fix(organizations): refuse transfer-and-leave for directory-managed owners * fix(organizations): transfer ownership for directory-managed owners and explain why they stay
…te them (#9611) * fix(db): bootstrap Projects for development push databases that predate them A push-managed database with workspaces but no Project schema could never pass the schema-push guard: push cannot backfill workspace.project_id, and the guard refuses every unenforced Project state. Under SIM_DEV_DB_PUSH=1 the pre-push reconciler now replays migrations 0394 and 0406 (the foundation commits atomically with the rollout marker) and runs the registered 0032_project_membership script migration on its single session, recording it as the migration runner does. An interrupted bootstrap resumes; every other unenforced state still refuses with the same message. The pinned drizzle-kit patch also adds unique constraints before foreign keys, so a composite foreign key on a new unique constraint of an existing table no longer fails, and in the noninteractive push policy it answers the unique-constraint truncate prompt with its non-destructive choice. * fix(db): push the schema before the Project migration reads it The bootstrap ran 0032_project_membership before Drizzle pushed anything, so a development database from before a column the migration reads (such as the fork parent) failed after committing the expansion, on every retry. --prepare now only replays the expansion. push.ts then runs one Drizzle push with workspace.project_id still nullable (SIM_DB_PUSH_PROJECT_BOOTSTRAP=1), runs the migration through reconcile-project-membership.ts --bootstrap, and finishes with the ordinary push. The connector and rollout marker are kept out of Drizzle's diff until the migration retires them. A fresh bootstrap also requires that no public table carries a project_id column. * fix(db): pin the Project bootstrap to the public schema The replayed migration SQL and the script runner name tables unqualified, so a connection search path that lists another schema first created the Project foundation outside public. The refusal snapshot now also covers folders and Projects.
…h-staging-2026-11-03b
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
[High impact] The PR appears safe to merge; no actionable issues were found. SummarySyncs
No actionable issues were found. This review checked source and tests; it did not run the test suites.
|
There was a problem hiding this comment.
15 issues found across 110 files
Confidence score: 3/5
- In
membership.ts, last-workspace self-removal skips the SCIM guard because the caller omits the actor ID, allowing directory-managed members to remove their organization membership. Pass the actor identity on self-removals. - In
update.ts, a concurrent admin demotion can leave the billed account without an admin because the check happens outside the transaction. Revalidate the candidate inside the transaction. - In
session-cli.ts, a directory at a CLI artifact path is treated as a verification failure, soensureSessionCliskips repair. Check the file type before wrapping the descriptor withos.fdopen. - In
credentials.ts, a committed leave can leave fresh credential caches showing the user still has access if the DELETE response is lost. Invalidate those caches fromonSettled.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/sim/app/workspace/[workspaceId]/skills/[skillId]/components/skill-editors-card.tsx">
<violation number="1" location="apps/sim/app/workspace/[workspaceId]/skills/[skillId]/components/skill-editors-card.tsx:55">
P3: The disabled Remove action does not explain why workspace admins cannot be removed. Pass `skillEditorLockReason(editor.isWorkspaceAdmin)` as `disabledReason` so the shared row shows the inheritance explanation on hover.</violation>
</file>
<file name="apps/sim/stores/modals/chat-plan/store.ts">
<violation number="1" location="apps/sim/stores/modals/chat-plan/store.ts:15">
P2: This singleton can leave the paid-plan dialog open after navigating away from the workspace that triggered it, including on organization routes where no chat send occurred. Clear the transient modal state on route/workspace changes or scope it to the initiating workspace.</violation>
</file>
<file name="apps/sim/lib/sim-links/application/create-membership.integration.ts">
<violation number="1" location="apps/sim/lib/sim-links/application/create-membership.integration.ts:83">
P2: This test does not exercise the required `FOR SHARE` lifetime: it removes the member before `createSimLink` starts its insert transaction, so a membership recheck without the lock would still pass. Add synchronization inside the creation transaction, or observe that removal blocks until creation commits, to cover the concurrent-removal case.
(Based on your team's feedback about rechecking membership and holding the member row through commit.)</violation>
</file>
<file name="apps/sim/lib/billing/organizations/membership.ts">
<violation number="1" location="apps/sim/lib/billing/organizations/membership.ts:1393">
P2: The last-workspace self-removal path bypasses this SCIM guard because its caller omits `actorUserId`; pass the actor identity for self-removals so directory-managed members cannot delete their organization membership through workspace removal.</violation>
</file>
<file name="apps/sim/app/o/[organizationId]/components/organization-sidebar/components/organization-header/organization-header.tsx">
<violation number="1" location="apps/sim/app/o/[organizationId]/components/organization-sidebar/components/organization-header/organization-header.tsx:105">
P2: The close guard leaves `isMenuOpen` true, and `stopRenaming` never closes it. After Enter, Escape, or a successful blur, the organization menu remains open; close it when stopping the edit.</violation>
<violation number="2" location="apps/sim/app/o/[organizationId]/components/organization-sidebar/components/organization-header/organization-header.tsx:158">
P2: The controlled dropdown drops its close request while the rename input is mounted, but never replays it after blur or save. Clicking outside during rename therefore leaves the organization menu open, including for unchanged or successfully saved names. Record the pending close request and apply it after blur/save while keeping the menu open for failed saves.</violation>
</file>
<file name="apps/sim/app/workspace/[workspaceId]/components/credential-detail/components/credential-members-section.tsx">
<violation number="1" location="apps/sim/app/workspace/[workspaceId]/components/credential-detail/components/credential-members-section.tsx:103">
P2: While the session query is pending, this treats an admin's own row as another member and renders `Remove` instead of `Leave`. Gate both action branches on a resolved viewer ID; otherwise clicking during that window revokes the admin's membership without the leave confirmation.</violation>
</file>
<file name="apps/sim/lib/settings/sections.ts">
<violation number="1" location="apps/sim/lib/settings/sections.ts:58">
P2: This section is advertised by `settings list` and `settings open` even when Sim links are disabled for the organization. Gate discovery/open on the Sim links rollout flag (and membership) before exposing this section, rather than relying only on the later adapter operations to reject access.</violation>
<violation number="2" location="apps/sim/lib/settings/sections.ts:110">
P2: `settings open` cannot direct the user to the workspace name and logo page because this entry omits `uiSection`, so it returns neither `setupUrl` nor `userSetup`. Set `uiSection: 'general'` so the existing adapter and canonical settings route are exposed together.</violation>
</file>
<file name="apps/sim/lib/execution/remote-sandbox/session-cli.ts">
<violation number="1" location="apps/sim/lib/execution/remote-sandbox/session-cli.ts:23">
P2: A directory at either CLI artifact path is reported as verification failure instead of stale, because `os.fdopen` raises before the non-regular-file check; `ensureSessionCli` then skips repair. Inspect the descriptor with `fstat` before calling `fdopen` so directories exit 10 like other non-regular artifacts.</violation>
</file>
<file name="apps/sim/hooks/queries/credentials.ts">
<violation number="1" location="apps/sim/hooks/queries/credentials.ts:327">
P2: A committed leave can appear to fail when the DELETE response is lost, but `onSuccess` then leaves fresh credential caches showing the user still has access. Invalidate these caches from `onSettled` instead.</violation>
</file>
<file name="apps/sim/lib/mothership/tools/server/settings-operations.ts">
<violation number="1" location="apps/sim/lib/mothership/tools/server/settings-operations.ts:380">
P2: `bulk_add_members` rebuilds the v2 schema from `.shape`, dropping its cross-field validation; `describe` therefore advertises combinations that the operation rejects. Reuse the complete schema and extend it with `groupId`.</violation>
</file>
<file name="apps/sim/lib/workspaces/update.ts">
<violation number="1" location="apps/sim/lib/workspaces/update.ts:58">
P2: `previous` is not read atomically with the update, so concurrent same-field edits produce incorrect audit “from” values. Read and update under one row lock/transaction, or use a version predicate and derive the audit source from the locked row.</violation>
<violation number="2" location="apps/sim/lib/workspaces/update.ts:83">
P2: The billed-account admin check is outside the transaction, so a concurrent demotion can commit before the payer transfer and leave a non-admin billed account. Revalidate the candidate under the transaction’s mutation locks and abort when the permission changed before committing the transfer.</violation>
</file>
<file name="apps/sim/app/workspace/[workspaceId]/settings/components/organization-accounts/organization-accounts.tsx">
<violation number="1" location="apps/sim/app/workspace/[workspaceId]/settings/components/organization-accounts/organization-accounts.tsx:108">
P3: A refetch failure with a cached empty result is shown as a successful empty state, leaving the user no retry control. Gate this empty state on `!accounts.isError` so the error branch remains reachable.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Turn on auto-fix | Re-trigger cubic
| * the send started (the composer, a queued message, a handoff from another surface), and the | ||
| * workspace shell renders the one modal. | ||
| */ | ||
| export const useChatPlanModalStore = create<ChatPlanModalState>()( |
There was a problem hiding this comment.
P2: This singleton can leave the paid-plan dialog open after navigating away from the workspace that triggered it, including on organization routes where no chat send occurred. Clear the transient modal state on route/workspace changes or scope it to the initiating workspace.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/sim/stores/modals/chat-plan/store.ts, line 15:
<comment>This singleton can leave the paid-plan dialog open after navigating away from the workspace that triggered it, including on organization routes where no chat send occurred. Clear the transient modal state on route/workspace changes or scope it to the initiating workspace.</comment>
<file context>
@@ -0,0 +1,24 @@
+ * the send started (the composer, a queued message, a handoff from another surface), and the
+ * workspace shell renders the one modal.
+ */
+export const useChatPlanModalStore = create<ChatPlanModalState>()(
+ devtools(
+ (set) => ({
</file context>
| await authorized.promise | ||
| expect( | ||
| ( | ||
| await removeUserFromOrganization({ |
There was a problem hiding this comment.
P2: This test does not exercise the required FOR SHARE lifetime: it removes the member before createSimLink starts its insert transaction, so a membership recheck without the lock would still pass. Add synchronization inside the creation transaction, or observe that removal blocks until creation commits, to cover the concurrent-removal case.
(Based on your team's feedback about rechecking membership and holding the member row through commit.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/sim/lib/sim-links/application/create-membership.integration.ts, line 83:
<comment>This test does not exercise the required `FOR SHARE` lifetime: it removes the member before `createSimLink` starts its insert transaction, so a membership recheck without the lock would still pass. Add synchronization inside the creation transaction, or observe that removal blocks until creation commits, to cover the concurrent-removal case.
(Based on your team's feedback about rechecking membership and holding the member row through commit.) </comment>
<file context>
@@ -0,0 +1,101 @@
+ await authorized.promise
+ expect(
+ (
+ await removeUserFromOrganization({
+ organizationId,
+ userId: creatorId,
</file context>
| if (actorUserId) | ||
| if (actorUserId) { | ||
| await requireMemberManagementAuthority(tx, organizationId, actorUserId, userId) | ||
| if (actorUserId === userId) |
There was a problem hiding this comment.
P2: The last-workspace self-removal path bypasses this SCIM guard because its caller omits actorUserId; pass the actor identity for self-removals so directory-managed members cannot delete their organization membership through workspace removal.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/sim/lib/billing/organizations/membership.ts, line 1393:
<comment>The last-workspace self-removal path bypasses this SCIM guard because its caller omits `actorUserId`; pass the actor identity for self-removals so directory-managed members cannot delete their organization membership through workspace removal.</comment>
<file context>
@@ -1384,8 +1388,11 @@ export async function removeUserFromOrganization(
- if (actorUserId)
+ if (actorUserId) {
await requireMemberManagementAuthority(tx, organizationId, actorUserId, userId)
+ if (actorUserId === userId)
+ await assertSelfLeaveNotScimManaged({ organizationId, userId, executor: tx })
+ }
</file context>
| const stopRenaming = () => { | ||
| setDraftName(null) | ||
| renameOrganization.reset() | ||
| } |
There was a problem hiding this comment.
P2: The close guard leaves isMenuOpen true, and stopRenaming never closes it. After Enter, Escape, or a successful blur, the organization menu remains open; close it when stopping the edit.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/sim/app/o/[organizationId]/components/organization-sidebar/components/organization-header/organization-header.tsx, line 105:
<comment>The close guard leaves `isMenuOpen` true, and `stopRenaming` never closes it. After Enter, Escape, or a successful blur, the organization menu remains open; close it when stopping the edit.</comment>
<file context>
@@ -85,13 +102,37 @@ export function OrganizationHeader({
}
const { memberCount } = organization
+ const stopRenaming = () => {
+ setDraftName(null)
+ renameOrganization.reset()
</file context>
| const stopRenaming = () => { | |
| setDraftName(null) | |
| renameOrganization.reset() | |
| } | |
| const stopRenaming = () => { | |
| setDraftName(null) | |
| setIsMenuOpen(false) | |
| renameOrganization.reset() | |
| } |
| const action: MemberRowAction | null = | ||
| member.userId === viewerId | ||
| ? { | ||
| label: 'Leave', | ||
| disabled: actionDisabled, | ||
| disabledReason: | ||
| lockReason ?? | ||
| (roleLocked ? 'Make someone else an admin before leaving' : null), | ||
| onSelect: () => setIsLeaveConfirmOpen(true), | ||
| } | ||
| : isAdmin | ||
| ? { | ||
| label: 'Remove', | ||
| disabled: actionDisabled, | ||
| onSelect: () => handleRemoveMember(member.userId), | ||
| } | ||
| : null |
There was a problem hiding this comment.
P2: While the session query is pending, this treats an admin's own row as another member and renders Remove instead of Leave. Gate both action branches on a resolved viewer ID; otherwise clicking during that window revokes the admin's membership without the leave confirmation.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/sim/app/workspace/[workspaceId]/components/credential-detail/components/credential-members-section.tsx, line 103:
<comment>While the session query is pending, this treats an admin's own row as another member and renders `Remove` instead of `Leave`. Gate both action branches on a resolved viewer ID; otherwise clicking during that window revokes the admin's membership without the leave confirmation.</comment>
<file context>
@@ -64,22 +99,56 @@ export function CredentialMembersSection({ credentialId, isAdmin }: CredentialMe
member.roleSource !== 'workspace-admin' &&
explicitAdminCount <= 1
+ const actionDisabled = roleLocked || lockReason !== null
+ const action: MemberRowAction | null =
+ member.userId === viewerId
+ ? {
</file context>
| const action: MemberRowAction | null = | |
| member.userId === viewerId | |
| ? { | |
| label: 'Leave', | |
| disabled: actionDisabled, | |
| disabledReason: | |
| lockReason ?? | |
| (roleLocked ? 'Make someone else an admin before leaving' : null), | |
| onSelect: () => setIsLeaveConfirmOpen(true), | |
| } | |
| : isAdmin | |
| ? { | |
| label: 'Remove', | |
| disabled: actionDisabled, | |
| onSelect: () => handleRemoveMember(member.userId), | |
| } | |
| : null | |
| const action: MemberRowAction | null = | |
| viewerId !== undefined && member.userId === viewerId | |
| ? { | |
| label: 'Leave', | |
| disabled: actionDisabled, | |
| disabledReason: | |
| lockReason ?? | |
| (roleLocked ? 'Make someone else an admin before leaving' : null), | |
| onSelect: () => setIsLeaveConfirmOpen(true), | |
| } | |
| : viewerId !== undefined && isAdmin | |
| ? { | |
| label: 'Remove', | |
| disabled: actionDisabled, | |
| onSelect: () => handleRemoveMember(member.userId), | |
| } | |
| : null |
| throw new OrchestrationError('validation', 'No updates provided') | ||
| } | ||
|
|
||
| const [previous] = await db |
There was a problem hiding this comment.
P2: previous is not read atomically with the update, so concurrent same-field edits produce incorrect audit “from” values. Read and update under one row lock/transaction, or use a version predicate and derive the audit source from the locked row.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/sim/lib/workspaces/update.ts, line 58:
<comment>`previous` is not read atomically with the update, so concurrent same-field edits produce incorrect audit “from” values. Read and update under one row lock/transaction, or use a version predicate and derive the audit source from the locked row.</comment>
<file context>
@@ -0,0 +1,128 @@
+ throw new OrchestrationError('validation', 'No updates provided')
+ }
+
+ const [previous] = await db
+ .select()
+ .from(workspace)
</file context>
| 'Personal workspaces are always billed to their owner and cannot change billed account.' | ||
| ) | ||
| } | ||
| const candidatePermission = await getEffectiveWorkspacePermission(billedAccountUserId, previous) |
There was a problem hiding this comment.
P2: The billed-account admin check is outside the transaction, so a concurrent demotion can commit before the payer transfer and leave a non-admin billed account. Revalidate the candidate under the transaction’s mutation locks and abort when the permission changed before committing the transfer.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/sim/lib/workspaces/update.ts, line 83:
<comment>The billed-account admin check is outside the transaction, so a concurrent demotion can commit before the payer transfer and leave a non-admin billed account. Revalidate the candidate under the transaction’s mutation locks and abort when the permission changed before committing the transfer.</comment>
<file context>
@@ -0,0 +1,128 @@
+ 'Personal workspaces are always billed to their owner and cannot change billed account.'
+ )
+ }
+ const candidatePermission = await getEffectiveWorkspacePermission(billedAccountUserId, previous)
+ if (candidatePermission !== 'admin') {
+ throw new OrchestrationError('validation', 'Billed account must be a workspace admin')
</file context>
| open={isMenuOpen} | ||
| onOpenChange={(open) => { | ||
| // The input's blur saves the draft; closing now would unmount it and hide a failure. | ||
| if (!open && draftName !== null) return |
There was a problem hiding this comment.
P2: The controlled dropdown drops its close request while the rename input is mounted, but never replays it after blur or save. Clicking outside during rename therefore leaves the organization menu open, including for unchanged or successfully saved names. Record the pending close request and apply it after blur/save while keeping the menu open for failed saves.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/sim/app/o/[organizationId]/components/organization-sidebar/components/organization-header/organization-header.tsx, line 158:
<comment>The controlled dropdown drops its close request while the rename input is mounted, but never replays it after blur or save. Clicking outside during rename therefore leaves the organization menu open, including for unchanged or successfully saved names. Record the pending close request and apply it after blur/save while keeping the menu open for failed saves.</comment>
<file context>
@@ -110,25 +151,34 @@ export function OrganizationHeader({
+ open={isMenuOpen}
+ onOpenChange={(open) => {
+ // The input's blur saves the draft; closing now would unmount it and hide a failure.
+ if (!open && draftName !== null) return
+ setIsMenuOpen(open)
+ }}
</file context>
| canEdit | ||
| ? { | ||
| label: 'Remove', | ||
| disabled: editor.isWorkspaceAdmin, |
There was a problem hiding this comment.
P3: The disabled Remove action does not explain why workspace admins cannot be removed. Pass skillEditorLockReason(editor.isWorkspaceAdmin) as disabledReason so the shared row shows the inheritance explanation on hover.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/sim/app/workspace/[workspaceId]/skills/[skillId]/components/skill-editors-card.tsx, line 55:
<comment>The disabled Remove action does not explain why workspace admins cannot be removed. Pass `skillEditorLockReason(editor.isWorkspaceAdmin)` as `disabledReason` so the shared row shows the inheritance explanation on hover.</comment>
<file context>
@@ -47,10 +47,17 @@ export function SkillEditorsCard({ editors, canEdit }: SkillEditorsCardProps) {
+ canEdit
+ ? {
+ label: 'Remove',
+ disabled: editor.isWorkspaceAdmin,
+ onSelect: () => editors.removeEditor(editor.userId),
+ }
</file context>
| disabled: editor.isWorkspaceAdmin, | |
| disabled: editor.isWorkspaceAdmin, | |
| disabledReason: skillEditorLockReason(editor.isWorkspaceAdmin), |
| /> | ||
| ) : accounts.isPending ? ( | ||
| <SettingsEmptyState>Loading connected accounts…</SettingsEmptyState> | ||
| ) : list.length === 0 ? ( |
There was a problem hiding this comment.
P3: A refetch failure with a cached empty result is shown as a successful empty state, leaving the user no retry control. Gate this empty state on !accounts.isError so the error branch remains reachable.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/sim/app/workspace/[workspaceId]/settings/components/organization-accounts/organization-accounts.tsx, line 108:
<comment>A refetch failure with a cached empty result is shown as a successful empty state, leaving the user no retry control. Gate this empty state on `!accounts.isError` so the error branch remains reachable.</comment>
<file context>
@@ -0,0 +1,199 @@
+ />
+ ) : accounts.isPending ? (
+ <SettingsEmptyState>Loading connected accounts…</SettingsEmptyState>
+ ) : list.length === 0 ? (
+ <SettingsEmptyState>No accounts connected for an organization</SettingsEmptyState>
+ ) : (
</file context>
| ) : list.length === 0 ? ( | |
| ) : list.length === 0 && !accounts.isError ? ( |
Brings dev up to current staging, including #9611, which lets dev's push-managed database bootstrap Projects (the previous sync's deploy stopped at the Project migration guard). The merge result is identical to staging.