From 84a1ccfc4c8776685da057540b3f77c96d21a9c1 Mon Sep 17 00:00:00 2001
From: mike12345567 <me@michaeldrury.co.uk>
Date: Mon, 24 Feb 2025 14:59:04 +0000
Subject: [PATCH 01/21] Fixing an issue with scrollbar always appearing on end
 user portal.

---
 packages/bbui/src/Layout/Page.svelte | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/bbui/src/Layout/Page.svelte b/packages/bbui/src/Layout/Page.svelte
index e469927e60..e8dad8bc38 100644
--- a/packages/bbui/src/Layout/Page.svelte
+++ b/packages/bbui/src/Layout/Page.svelte
@@ -47,7 +47,7 @@
     overflow-x: hidden;
   }
   .main {
-    overflow-y: scroll;
+    overflow-y: auto;
   }
   .content {
     display: flex;

From f5a7e5bf4951c5c1cc62adfb88f2d51b291fdfb4 Mon Sep 17 00:00:00 2001
From: mike12345567 <me@michaeldrury.co.uk>
Date: Mon, 24 Feb 2025 15:34:30 +0000
Subject: [PATCH 02/21] Fixing app count in user page.

---
 .../src/pages/builder/portal/users/users/index.svelte |  2 +-
 packages/shared-core/src/sdk/documents/users.ts       | 11 +++++++++++
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/packages/builder/src/pages/builder/portal/users/users/index.svelte b/packages/builder/src/pages/builder/portal/users/users/index.svelte
index c77e40c964..6a1facfd01 100644
--- a/packages/builder/src/pages/builder/portal/users/users/index.svelte
+++ b/packages/builder/src/pages/builder/portal/users/users/index.svelte
@@ -128,7 +128,7 @@
           $auth.user?.email === user.email
             ? false
             : true,
-        apps: [...new Set(Object.keys(user.roles))],
+        apps: sdk.users.userAppAccessList(user, $groups),
         access: role.sortOrder,
       }
     })
diff --git a/packages/shared-core/src/sdk/documents/users.ts b/packages/shared-core/src/sdk/documents/users.ts
index 17aa8a1e58..c4f1a16025 100644
--- a/packages/shared-core/src/sdk/documents/users.ts
+++ b/packages/shared-core/src/sdk/documents/users.ts
@@ -4,6 +4,7 @@ import {
   SEPARATOR,
   User,
   InternalTable,
+  UserGroup,
 } from "@budibase/types"
 import { getProdAppID } from "./applications"
 import * as _ from "lodash/fp"
@@ -129,3 +130,13 @@ export function containsUserID(value: string | undefined): boolean {
   }
   return value.includes(`${DocumentType.USER}${SEPARATOR}`)
 }
+
+export function userAppAccessList(user: User, groups?: UserGroup[]) {
+  const userGroups =
+    groups?.filter(group => group.users?.find(u => u._id === user._id)) || []
+  const userGroupApps = userGroups.flatMap(userGroup =>
+    Object.keys(userGroup.roles || {})
+  )
+  const fullList = [...Object.keys(user.roles), ...userGroupApps]
+  return [...new Set(fullList)]
+}

From a1589b0dc05af57d65aa9ac6dda7dd81d3ea814f Mon Sep 17 00:00:00 2001
From: mike12345567 <me@michaeldrury.co.uk>
Date: Mon, 24 Feb 2025 18:07:01 +0000
Subject: [PATCH 03/21] Adding group badges to users.

---
 packages/bbui/src/Badge/Badge.svelte                     | 2 ++
 .../[application]/_components/BuilderSidePanel.svelte    | 5 +++++
 packages/shared-core/src/sdk/documents/users.ts          | 9 +++++++--
 3 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/packages/bbui/src/Badge/Badge.svelte b/packages/bbui/src/Badge/Badge.svelte
index e4ec7d4f33..03aa544dfe 100644
--- a/packages/bbui/src/Badge/Badge.svelte
+++ b/packages/bbui/src/Badge/Badge.svelte
@@ -11,6 +11,7 @@
   export let active = false
   export let inactive = false
   export let hoverable = false
+  export let customColor = null
 </script>
 
 <!-- svelte-ignore a11y-no-static-element-interactions -->
@@ -29,6 +30,7 @@
   class:spectrum-Label--seafoam={seafoam}
   class:spectrum-Label--active={active}
   class:spectrum-Label--inactive={inactive}
+  style={customColor ? `background-color: ${customColor};` : ""}
 >
   <slot />
 </span>
diff --git a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
index 2260892913..5b1bf852aa 100644
--- a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
@@ -13,6 +13,7 @@
     FancyInput,
     Button,
     FancySelect,
+    Badge,
   } from "@budibase/bbui"
   import { builderStore, appStore, roles, appPublished } from "@/stores/builder"
   import {
@@ -741,11 +742,15 @@
                 <div class="auth-entity-access-title">Access</div>
               </div>
               {#each allUsers as user}
+                {@const userGroups = sdk.users.getUserGroups(user, $groups)}
                 <div class="auth-entity">
                   <div class="details">
                     <div class="user-email" title={user.email}>
                       {user.email}
                     </div>
+                    {#each userGroups as group}
+                      <Badge size="S" customColor={group.color}>{group.name}</Badge>
+                    {/each}
                   </div>
                   <div class="auth-entity-access" class:muted={user.group}>
                     <RoleSelect
diff --git a/packages/shared-core/src/sdk/documents/users.ts b/packages/shared-core/src/sdk/documents/users.ts
index c4f1a16025..8b5abfd822 100644
--- a/packages/shared-core/src/sdk/documents/users.ts
+++ b/packages/shared-core/src/sdk/documents/users.ts
@@ -131,9 +131,14 @@ export function containsUserID(value: string | undefined): boolean {
   return value.includes(`${DocumentType.USER}${SEPARATOR}`)
 }
 
-export function userAppAccessList(user: User, groups?: UserGroup[]) {
-  const userGroups =
+export function getUserGroups(user: User, groups?: UserGroup[]) {
+  return (
     groups?.filter(group => group.users?.find(u => u._id === user._id)) || []
+  )
+}
+
+export function userAppAccessList(user: User, groups?: UserGroup[]) {
+  const userGroups = getUserGroups(user, groups)
   const userGroupApps = userGroups.flatMap(userGroup =>
     Object.keys(userGroup.roles || {})
   )

From f7d216c188562cf793673e04d3a17e5face33264 Mon Sep 17 00:00:00 2001
From: mike12345567 <me@michaeldrury.co.uk>
Date: Mon, 24 Feb 2025 18:08:20 +0000
Subject: [PATCH 04/21] Setting override placeholder.

---
 .../app/[application]/_components/BuilderSidePanel.svelte       | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
index 5b1bf852aa..a9d3bb70ba 100644
--- a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
@@ -755,7 +755,7 @@
                   <div class="auth-entity-access" class:muted={user.group}>
                     <RoleSelect
                       footer={getRoleFooter(user)}
-                      placeholder={false}
+                      placeholder={userGroups?.length ? "Override" : false}
                       value={parseRole(user)}
                       allowRemove={user.role && !user.group}
                       allowPublic={false}

From b7f34b5f57408c111f2165301e21486fe3d34492 Mon Sep 17 00:00:00 2001
From: mike12345567 <me@michaeldrury.co.uk>
Date: Tue, 25 Feb 2025 10:45:50 +0000
Subject: [PATCH 05/21] Changing how badges are displayed.

---
 .../_components/BuilderSidePanel.svelte       | 27 ++++++++++++++-----
 .../shared-core/src/sdk/documents/users.ts    | 12 +++++++++
 2 files changed, 33 insertions(+), 6 deletions(-)

diff --git a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
index a9d3bb70ba..8136dd256c 100644
--- a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
@@ -742,15 +742,19 @@
                 <div class="auth-entity-access-title">Access</div>
               </div>
               {#each allUsers as user}
-                {@const userGroups = sdk.users.getUserGroups(user, $groups)}
+                {@const userGroups = sdk.users.getUserAppGroups($appStore.appId, user, $groups)}
                 <div class="auth-entity">
                   <div class="details">
-                    <div class="user-email" title={user.email}>
-                      {user.email}
+                    <div class="user-groups">
+                      <div class="user-email" title={user.email}>
+                        {user.email}
+                      </div>
+                      <div class="group-badges">
+                        {#each userGroups as group}
+                          <Badge size="S" customColor={group.color}>{group.name}</Badge>
+                        {/each}
+                      </div>
                     </div>
-                    {#each userGroups as group}
-                      <Badge size="S" customColor={group.color}>{group.name}</Badge>
-                    {/each}
                   </div>
                   <div class="auth-entity-access" class:muted={user.group}>
                     <RoleSelect
@@ -1053,4 +1057,15 @@
   .alert {
     padding: 0 var(--spacing-xl);
   }
+
+  .user-groups {
+    display: flex;
+    flex-direction: column;
+  }
+  .group-badges {
+    padding-top: var(--spacing-s);
+    display: flex;
+    gap: var(--spacing-s);
+    flex-wrap: wrap;
+  }
 </style>
diff --git a/packages/shared-core/src/sdk/documents/users.ts b/packages/shared-core/src/sdk/documents/users.ts
index 8b5abfd822..d2ee2dec00 100644
--- a/packages/shared-core/src/sdk/documents/users.ts
+++ b/packages/shared-core/src/sdk/documents/users.ts
@@ -137,6 +137,18 @@ export function getUserGroups(user: User, groups?: UserGroup[]) {
   )
 }
 
+export function getUserAppGroups(
+  appId: string,
+  user: User,
+  groups?: UserGroup[]
+) {
+  const prodAppId = getProdAppID(appId)
+  const userGroups = getUserGroups(user, groups)
+  return userGroups.filter(group =>
+    Object.keys(group.roles || {}).find(app => app === prodAppId)
+  )
+}
+
 export function userAppAccessList(user: User, groups?: UserGroup[]) {
   const userGroups = getUserGroups(user, groups)
   const userGroupApps = userGroups.flatMap(userGroup =>

From df102e7b6b6e9e518bfec8907eba79782f06b551 Mon Sep 17 00:00:00 2001
From: mike12345567 <me@michaeldrury.co.uk>
Date: Tue, 25 Feb 2025 17:18:27 +0000
Subject: [PATCH 06/21] Opacity of group badges.

---
 packages/bbui/src/Badge/Badge.svelte                        | 2 +-
 .../app/[application]/_components/BuilderSidePanel.svelte   | 6 ++++--
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/packages/bbui/src/Badge/Badge.svelte b/packages/bbui/src/Badge/Badge.svelte
index 03aa544dfe..15bb6c62c2 100644
--- a/packages/bbui/src/Badge/Badge.svelte
+++ b/packages/bbui/src/Badge/Badge.svelte
@@ -30,7 +30,7 @@
   class:spectrum-Label--seafoam={seafoam}
   class:spectrum-Label--active={active}
   class:spectrum-Label--inactive={inactive}
-  style={customColor ? `background-color: ${customColor};` : ""}
+  style={customColor ? `background-color: ${customColor}` : ""}
 >
   <slot />
 </span>
diff --git a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
index 8136dd256c..ff4b872153 100644
--- a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
@@ -742,7 +742,7 @@
                 <div class="auth-entity-access-title">Access</div>
               </div>
               {#each allUsers as user}
-                {@const userGroups = sdk.users.getUserAppGroups($appStore.appId, user, $groups)}
+                {@const userGroups = sdk.users.getUserAppGroups($appStore.appId, user, $groups).slice(0, 3)}
                 <div class="auth-entity">
                   <div class="details">
                     <div class="user-groups">
@@ -751,7 +751,7 @@
                       </div>
                       <div class="group-badges">
                         {#each userGroups as group}
-                          <Badge size="S" customColor={group.color}>{group.name}</Badge>
+                          <Badge size="S" customColor={`color-mix(in srgb, ${group.color} 30%, transparent)`}>{group.name}</Badge>
                         {/each}
                       </div>
                     </div>
@@ -949,6 +949,8 @@
     grid-template-columns: 1fr 220px;
     align-items: center;
     gap: var(--spacing-xl);
+    border-bottom: var(--border-light);
+    padding-bottom: var(--spacing-s);
   }
 
   .auth-entity .details {

From c3de44a6a9270fba6b8184ed379fa4b304ba4efe Mon Sep 17 00:00:00 2001
From: mike12345567 <me@michaeldrury.co.uk>
Date: Tue, 25 Feb 2025 18:17:41 +0000
Subject: [PATCH 07/21] Adding a way to limit the groups shown and then display
 a popover.

---
 packages/bbui/src/Icon/Icon.svelte            |  3 ++
 .../_components/BuilderGroupPopover.svelte    | 49 +++++++++++++++++++
 .../_components/BuilderSidePanel.svelte       | 15 ++++--
 .../_components/GroupBadge.svelte             | 10 ++++
 4 files changed, 74 insertions(+), 3 deletions(-)
 create mode 100644 packages/builder/src/pages/builder/app/[application]/_components/BuilderGroupPopover.svelte
 create mode 100644 packages/builder/src/pages/builder/app/[application]/_components/GroupBadge.svelte

diff --git a/packages/bbui/src/Icon/Icon.svelte b/packages/bbui/src/Icon/Icon.svelte
index 2f12935f53..46b3140be8 100644
--- a/packages/bbui/src/Icon/Icon.svelte
+++ b/packages/bbui/src/Icon/Icon.svelte
@@ -28,6 +28,9 @@
     <svg
       on:contextmenu
       on:click
+      on:mouseover
+      on:mouseleave
+      on:focus
       class:hoverable
       class:disabled
       class="spectrum-Icon spectrum-Icon--size{size}"
diff --git a/packages/builder/src/pages/builder/app/[application]/_components/BuilderGroupPopover.svelte b/packages/builder/src/pages/builder/app/[application]/_components/BuilderGroupPopover.svelte
new file mode 100644
index 0000000000..47811cbcb9
--- /dev/null
+++ b/packages/builder/src/pages/builder/app/[application]/_components/BuilderGroupPopover.svelte
@@ -0,0 +1,49 @@
+<script>
+  import { Icon, Popover } from "@budibase/bbui"
+  import GroupBadge from "./GroupBadge.svelte"
+
+  export let groups = []
+
+  let anchor, popover
+
+  function show() {
+    popover.show()
+  }
+
+  function hide() {
+    popover.hide()
+  }
+</script>
+
+<div class="icon" bind:this={anchor}>
+  <Icon name="More" size="S" hoverable on:mouseover={show} on:mouseleave={hide} />
+</div>
+
+<Popover
+  customZIndex={1002}
+  bind:this={popover}
+  align="center"
+  {anchor}
+  showPopover={true}
+  minWidth={0}
+>
+  <div class="badges">
+    {#each groups as group}
+      <GroupBadge color={group.color} name={group.name} />
+    {/each}
+  </div>
+</Popover>
+
+<style>
+  .icon {
+    height: auto;
+    display: flex;
+    justify-content: center;
+  }
+  .badges {
+    display: flex;
+    flex-wrap: wrap;
+    gap: var(--spacing-s);
+    padding: var(--spacing-m);
+  }
+</style>
diff --git a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
index ff4b872153..11a36706cf 100644
--- a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
@@ -38,6 +38,8 @@
   import { emailValidator } from "@/helpers/validation"
   import { fly } from "svelte/transition"
   import InfoDisplay from "../design/[screenId]/[componentId]/_components/Component/InfoDisplay.svelte"
+  import BuilderGroupPopover from "./BuilderGroupPopover.svelte"
+  import GroupBadge from "./GroupBadge.svelte"
 
   let query = null
   let loaded = false
@@ -742,7 +744,11 @@
                 <div class="auth-entity-access-title">Access</div>
               </div>
               {#each allUsers as user}
-                {@const userGroups = sdk.users.getUserAppGroups($appStore.appId, user, $groups).slice(0, 3)}
+                {@const userGroups = sdk.users.getUserAppGroups(
+                  $appStore.appId,
+                  user,
+                  $groups
+                )}
                 <div class="auth-entity">
                   <div class="details">
                     <div class="user-groups">
@@ -750,9 +756,12 @@
                         {user.email}
                       </div>
                       <div class="group-badges">
-                        {#each userGroups as group}
-                          <Badge size="S" customColor={`color-mix(in srgb, ${group.color} 30%, transparent)`}>{group.name}</Badge>
+                        {#each userGroups.slice(0, 3) as group}
+                          <GroupBadge color={group.color} name={group.name} />
                         {/each}
+                        {#if userGroups.length > 3}
+                          <BuilderGroupPopover groups={userGroups.slice(3)} />
+                        {/if}
                       </div>
                     </div>
                   </div>
diff --git a/packages/builder/src/pages/builder/app/[application]/_components/GroupBadge.svelte b/packages/builder/src/pages/builder/app/[application]/_components/GroupBadge.svelte
new file mode 100644
index 0000000000..f10e2aa220
--- /dev/null
+++ b/packages/builder/src/pages/builder/app/[application]/_components/GroupBadge.svelte
@@ -0,0 +1,10 @@
+<script>
+  import { Badge } from "@budibase/bbui"
+
+  export let color
+  export let name
+</script>
+
+<Badge size="S" customColor={`color-mix(in srgb, ${color} 30%, transparent)`}
+  >{name}</Badge
+>

From 336bc72de27860e8941e3a984929d2b83062de01 Mon Sep 17 00:00:00 2001
From: mike12345567 <me@michaeldrury.co.uk>
Date: Wed, 26 Feb 2025 12:53:18 +0000
Subject: [PATCH 08/21] Design after discussion with Cheeks and Joe.

---
 packages/bbui/src/Badge/Badge.svelte          |  4 +-
 .../_components/BuilderGroupPopover.svelte    | 41 +++-------------
 .../_components/BuilderSidePanel.svelte       | 48 ++++++++++---------
 .../_components/GroupBadge.svelte             | 10 ----
 packages/shared-core/src/helpers/index.ts     |  1 +
 packages/shared-core/src/helpers/lists.ts     |  7 +++
 6 files changed, 43 insertions(+), 68 deletions(-)
 delete mode 100644 packages/builder/src/pages/builder/app/[application]/_components/GroupBadge.svelte
 create mode 100644 packages/shared-core/src/helpers/lists.ts

diff --git a/packages/bbui/src/Badge/Badge.svelte b/packages/bbui/src/Badge/Badge.svelte
index 15bb6c62c2..dadbaa3317 100644
--- a/packages/bbui/src/Badge/Badge.svelte
+++ b/packages/bbui/src/Badge/Badge.svelte
@@ -11,7 +11,7 @@
   export let active = false
   export let inactive = false
   export let hoverable = false
-  export let customColor = null
+  export let outlineColor = null
 </script>
 
 <!-- svelte-ignore a11y-no-static-element-interactions -->
@@ -30,7 +30,7 @@
   class:spectrum-Label--seafoam={seafoam}
   class:spectrum-Label--active={active}
   class:spectrum-Label--inactive={inactive}
-  style={customColor ? `background-color: ${customColor}` : ""}
+  style={outlineColor ? `border: 2px solid ${outlineColor}` : ""}
 >
   <slot />
 </span>
diff --git a/packages/builder/src/pages/builder/app/[application]/_components/BuilderGroupPopover.svelte b/packages/builder/src/pages/builder/app/[application]/_components/BuilderGroupPopover.svelte
index 47811cbcb9..43c662a6d2 100644
--- a/packages/builder/src/pages/builder/app/[application]/_components/BuilderGroupPopover.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/_components/BuilderGroupPopover.svelte
@@ -1,49 +1,22 @@
 <script>
-  import { Icon, Popover } from "@budibase/bbui"
-  import GroupBadge from "./GroupBadge.svelte"
+  import { Icon } from "@budibase/bbui"
+  import { helpers } from "@budibase/shared-core"
 
   export let groups = []
-
-  let anchor, popover
-
-  function show() {
-    popover.show()
-  }
-
-  function hide() {
-    popover.hide()
+  function tooltip(groups) {
+    const sortedNames = groups.sort((a, b) => a.name.localeCompare(b.name)).map(group => group.name)
+    return `Member of ${helpers.lists.punctuateList(sortedNames)}`
   }
 </script>
 
-<div class="icon" bind:this={anchor}>
-  <Icon name="More" size="S" hoverable on:mouseover={show} on:mouseleave={hide} />
+<div class="icon">
+  <Icon name="Info" size="XS" color="grey" hoverable tooltip={tooltip(groups)} tooltipPosition="top" />
 </div>
 
-<Popover
-  customZIndex={1002}
-  bind:this={popover}
-  align="center"
-  {anchor}
-  showPopover={true}
-  minWidth={0}
->
-  <div class="badges">
-    {#each groups as group}
-      <GroupBadge color={group.color} name={group.name} />
-    {/each}
-  </div>
-</Popover>
-
 <style>
   .icon {
     height: auto;
     display: flex;
     justify-content: center;
   }
-  .badges {
-    display: flex;
-    flex-wrap: wrap;
-    gap: var(--spacing-s);
-    padding: var(--spacing-m);
-  }
 </style>
diff --git a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
index 11a36706cf..0bf32e4b76 100644
--- a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
@@ -13,7 +13,6 @@
     FancyInput,
     Button,
     FancySelect,
-    Badge,
   } from "@budibase/bbui"
   import { builderStore, appStore, roles, appPublished } from "@/stores/builder"
   import {
@@ -39,7 +38,6 @@
   import { fly } from "svelte/transition"
   import InfoDisplay from "../design/[screenId]/[componentId]/_components/Component/InfoDisplay.svelte"
   import BuilderGroupPopover from "./BuilderGroupPopover.svelte"
-  import GroupBadge from "./GroupBadge.svelte"
 
   let query = null
   let loaded = false
@@ -542,6 +540,12 @@
       creationAccessType = Constants.Roles.CREATOR
     }
   }
+
+  const itemCountText = (word, count) => {
+    return `${count} ${word}${
+      count !== 1 ? "s" : ""
+    }`
+  }
 </script>
 
 <svelte:window on:keydown={handleKeyDown} />
@@ -708,9 +712,7 @@
                       {group.name}
                     </div>
                     <div class="auth-entity-meta">
-                      {`${group.users?.length} user${
-                        group.users?.length != 1 ? "s" : ""
-                      }`}
+                      {itemCountText("user", group.users?.length)}
                     </div>
                   </div>
                   <div class="auth-entity-access">
@@ -755,20 +757,20 @@
                       <div class="user-email" title={user.email}>
                         {user.email}
                       </div>
-                      <div class="group-badges">
-                        {#each userGroups.slice(0, 3) as group}
-                          <GroupBadge color={group.color} name={group.name} />
-                        {/each}
-                        {#if userGroups.length > 3}
-                          <BuilderGroupPopover groups={userGroups.slice(3)} />
-                        {/if}
-                      </div>
+                      {#if userGroups.length}
+                        <div class="group-info">
+                          <div class="auth-entity-meta">
+                            {itemCountText("group", userGroups.length)}
+                          </div>
+                          <BuilderGroupPopover groups={userGroups} />
+                        </div>
+                      {/if}
                     </div>
                   </div>
                   <div class="auth-entity-access" class:muted={user.group}>
                     <RoleSelect
                       footer={getRoleFooter(user)}
-                      placeholder={userGroups?.length ? "Override" : false}
+                      placeholder={userGroups?.length ? "Controlled by group" : false}
                       value={parseRole(user)}
                       allowRemove={user.role && !user.group}
                       allowPublic={false}
@@ -949,7 +951,7 @@
 
   .auth-entity,
   .auth-entity-header {
-    padding: 0px var(--spacing-xl);
+    padding: 0 var(--spacing-xl);
   }
 
   .auth-entity,
@@ -958,8 +960,6 @@
     grid-template-columns: 1fr 220px;
     align-items: center;
     gap: var(--spacing-xl);
-    border-bottom: var(--border-light);
-    padding-bottom: var(--spacing-s);
   }
 
   .auth-entity .details {
@@ -1071,12 +1071,16 @@
 
   .user-groups {
     display: flex;
-    flex-direction: column;
+    flex-direction: row;
+    justify-content: center;
+    align-items: center;
+    gap: var(--spacing-m);
   }
-  .group-badges {
-    padding-top: var(--spacing-s);
+
+  .group-info {
     display: flex;
-    gap: var(--spacing-s);
-    flex-wrap: wrap;
+    flex-direction: row;
+    gap: var(--spacing-xs);
+    justify-content: center;
   }
 </style>
diff --git a/packages/builder/src/pages/builder/app/[application]/_components/GroupBadge.svelte b/packages/builder/src/pages/builder/app/[application]/_components/GroupBadge.svelte
deleted file mode 100644
index f10e2aa220..0000000000
--- a/packages/builder/src/pages/builder/app/[application]/_components/GroupBadge.svelte
+++ /dev/null
@@ -1,10 +0,0 @@
-<script>
-  import { Badge } from "@budibase/bbui"
-
-  export let color
-  export let name
-</script>
-
-<Badge size="S" customColor={`color-mix(in srgb, ${color} 30%, transparent)`}
-  >{name}</Badge
->
diff --git a/packages/shared-core/src/helpers/index.ts b/packages/shared-core/src/helpers/index.ts
index 0aa378b46a..b924617a0f 100644
--- a/packages/shared-core/src/helpers/index.ts
+++ b/packages/shared-core/src/helpers/index.ts
@@ -6,3 +6,4 @@ export * as cron from "./cron"
 export * as schema from "./schema"
 export * as views from "./views"
 export * as roles from "./roles"
+export * as lists from "./lists"
diff --git a/packages/shared-core/src/helpers/lists.ts b/packages/shared-core/src/helpers/lists.ts
new file mode 100644
index 0000000000..64b906fef8
--- /dev/null
+++ b/packages/shared-core/src/helpers/lists.ts
@@ -0,0 +1,7 @@
+export function punctuateList(list: string[]) {
+  if (list.length === 0) return ""
+  if (list.length === 1) return list[0]
+  if (list.length === 2) return list.join(" and ")
+  // For more than 2 elements: join all but the last with commas, then add "and" before the last element.
+  return list.slice(0, -1).join(", ") + " and " + list[list.length - 1]
+}

From c2841b824d5ad83a12fe777f356ae0ef55548152 Mon Sep 17 00:00:00 2001
From: mike12345567 <me@michaeldrury.co.uk>
Date: Wed, 26 Feb 2025 13:25:58 +0000
Subject: [PATCH 09/21] Linting.

---
 .../_components/BuilderGroupPopover.svelte          | 13 +++++++++++--
 .../_components/BuilderSidePanel.svelte             |  8 ++++----
 2 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/packages/builder/src/pages/builder/app/[application]/_components/BuilderGroupPopover.svelte b/packages/builder/src/pages/builder/app/[application]/_components/BuilderGroupPopover.svelte
index 43c662a6d2..367d84e029 100644
--- a/packages/builder/src/pages/builder/app/[application]/_components/BuilderGroupPopover.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/_components/BuilderGroupPopover.svelte
@@ -4,13 +4,22 @@
 
   export let groups = []
   function tooltip(groups) {
-    const sortedNames = groups.sort((a, b) => a.name.localeCompare(b.name)).map(group => group.name)
+    const sortedNames = groups
+      .sort((a, b) => a.name.localeCompare(b.name))
+      .map(group => group.name)
     return `Member of ${helpers.lists.punctuateList(sortedNames)}`
   }
 </script>
 
 <div class="icon">
-  <Icon name="Info" size="XS" color="grey" hoverable tooltip={tooltip(groups)} tooltipPosition="top" />
+  <Icon
+    name="Info"
+    size="XS"
+    color="grey"
+    hoverable
+    tooltip={tooltip(groups)}
+    tooltipPosition="top"
+  />
 </div>
 
 <style>
diff --git a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
index 0bf32e4b76..7a9b6ba7a0 100644
--- a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
@@ -542,9 +542,7 @@
   }
 
   const itemCountText = (word, count) => {
-    return `${count} ${word}${
-      count !== 1 ? "s" : ""
-    }`
+    return `${count} ${word}${count !== 1 ? "s" : ""}`
   }
 </script>
 
@@ -770,7 +768,9 @@
                   <div class="auth-entity-access" class:muted={user.group}>
                     <RoleSelect
                       footer={getRoleFooter(user)}
-                      placeholder={userGroups?.length ? "Controlled by group" : false}
+                      placeholder={userGroups?.length
+                        ? "Controlled by group"
+                        : false}
                       value={parseRole(user)}
                       allowRemove={user.role && !user.group}
                       allowPublic={false}

From 1d35f2a029b2fcc0e37564bf8ee563bf94629c04 Mon Sep 17 00:00:00 2001
From: mike12345567 <me@michaeldrury.co.uk>
Date: Wed, 26 Feb 2025 13:41:45 +0000
Subject: [PATCH 10/21] Remove comment

---
 packages/shared-core/src/helpers/lists.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/packages/shared-core/src/helpers/lists.ts b/packages/shared-core/src/helpers/lists.ts
index 64b906fef8..3a2cf743a3 100644
--- a/packages/shared-core/src/helpers/lists.ts
+++ b/packages/shared-core/src/helpers/lists.ts
@@ -2,6 +2,5 @@ export function punctuateList(list: string[]) {
   if (list.length === 0) return ""
   if (list.length === 1) return list[0]
   if (list.length === 2) return list.join(" and ")
-  // For more than 2 elements: join all but the last with commas, then add "and" before the last element.
   return list.slice(0, -1).join(", ") + " and " + list[list.length - 1]
 }

From 2022aa1c742bb8ce695d762a15fff80bc9778158 Mon Sep 17 00:00:00 2001
From: mike12345567 <me@michaeldrury.co.uk>
Date: Wed, 26 Feb 2025 15:18:43 +0000
Subject: [PATCH 11/21] Attempting to remove elasticsearch

---
 .github/workflows/budibase_ci.yml | 1 -
 1 file changed, 1 deletion(-)

diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml
index 6a9aa3e8d0..5e769fc8dc 100644
--- a/.github/workflows/budibase_ci.yml
+++ b/.github/workflows/budibase_ci.yml
@@ -164,7 +164,6 @@ jobs:
             mariadb,
             oracle,
             sqs,
-            elasticsearch,
             none,
           ]
     steps:

From 04324e6220a7c8783bcab1ed72fb84f17e397fa5 Mon Sep 17 00:00:00 2001
From: mike12345567 <me@michaeldrury.co.uk>
Date: Wed, 26 Feb 2025 15:31:17 +0000
Subject: [PATCH 12/21] Undo.

---
 .github/workflows/budibase_ci.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml
index 5e769fc8dc..6a9aa3e8d0 100644
--- a/.github/workflows/budibase_ci.yml
+++ b/.github/workflows/budibase_ci.yml
@@ -164,6 +164,7 @@ jobs:
             mariadb,
             oracle,
             sqs,
+            elasticsearch,
             none,
           ]
     steps:

From 5c20b496b720f58e768ce36b4397f528e53a240c Mon Sep 17 00:00:00 2001
From: mike12345567 <me@michaeldrury.co.uk>
Date: Thu, 27 Feb 2025 12:08:00 +0000
Subject: [PATCH 13/21] Handling long email addresses.

---
 .../_components/BuilderSidePanel.svelte         | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
index 7a9b6ba7a0..16af731e02 100644
--- a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
@@ -966,15 +966,16 @@
     display: flex;
     align-items: center;
     gap: var(--spacing-m);
-    color: var(--spectrum-global-color-gray-900);
     overflow: hidden;
+    width: 100%;
   }
 
   .auth-entity .user-email {
-    text-overflow: ellipsis;
-    white-space: nowrap;
+    flex: 1 1 0;
+    min-width: 0;
     overflow: hidden;
-    color: var(--spectrum-global-color-gray-900);
+    white-space: nowrap;
+    text-overflow: ellipsis;
   }
 
   #builder-side-panel-container {
@@ -1072,15 +1073,19 @@
   .user-groups {
     display: flex;
     flex-direction: row;
-    justify-content: center;
+    justify-content: flex-start; /* or space-between */
     align-items: center;
     gap: var(--spacing-m);
+    width: 100%;
+    min-width: 0;
   }
 
   .group-info {
     display: flex;
     flex-direction: row;
     gap: var(--spacing-xs);
-    justify-content: center;
+    justify-content: end;
+    width: 60px;
+    flex: 0 0 auto;
   }
 </style>

From 33b8751d7817b0f385cd0e9f081aad8177cd7a39 Mon Sep 17 00:00:00 2001
From: mike12345567 <me@michaeldrury.co.uk>
Date: Thu, 27 Feb 2025 12:36:42 +0000
Subject: [PATCH 14/21] Fixing users meta info for groups.

---
 .../app/[application]/_components/BuilderSidePanel.svelte  | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
index 16af731e02..4a7af9342e 100644
--- a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
@@ -706,7 +706,7 @@
                 >
                   <div class="details">
                     <GroupIcon {group} size="S" />
-                    <div>
+                    <div class="group-name">
                       {group.name}
                     </div>
                     <div class="auth-entity-meta">
@@ -935,6 +935,7 @@
     color: var(--spectrum-global-color-gray-600);
     font-size: 12px;
     white-space: nowrap;
+    text-align: end;
   }
 
   .auth-entity-access {
@@ -970,7 +971,7 @@
     width: 100%;
   }
 
-  .auth-entity .user-email {
+  .auth-entity .user-email, .group-name {
     flex: 1 1 0;
     min-width: 0;
     overflow: hidden;
@@ -1073,7 +1074,7 @@
   .user-groups {
     display: flex;
     flex-direction: row;
-    justify-content: flex-start; /* or space-between */
+    justify-content: flex-start;
     align-items: center;
     gap: var(--spacing-m);
     width: 100%;

From 4f697b7731a3e3eca298a69196383bd4f256e987 Mon Sep 17 00:00:00 2001
From: mike12345567 <me@michaeldrury.co.uk>
Date: Thu, 27 Feb 2025 17:12:20 +0000
Subject: [PATCH 15/21] Adding groups to user app list.

---
 .../_components/BuilderSidePanel.svelte       |  3 +-
 .../portal/users/users/[userId].svelte        | 40 ++++++++++++-------
 .../_components/AppRoleTableRenderer.svelte   |  4 +-
 packages/frontend-core/src/constants.ts       |  1 +
 4 files changed, 32 insertions(+), 16 deletions(-)

diff --git a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
index 4a7af9342e..59d14770ae 100644
--- a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
@@ -971,7 +971,8 @@
     width: 100%;
   }
 
-  .auth-entity .user-email, .group-name {
+  .auth-entity .user-email,
+  .group-name {
     flex: 1 1 0;
     min-width: 0;
     overflow: hidden;
diff --git a/packages/builder/src/pages/builder/portal/users/users/[userId].svelte b/packages/builder/src/pages/builder/portal/users/users/[userId].svelte
index 6c480d9ef8..f02c2fe058 100644
--- a/packages/builder/src/pages/builder/portal/users/users/[userId].svelte
+++ b/packages/builder/src/pages/builder/portal/users/users/[userId].svelte
@@ -98,7 +98,9 @@
   $: privileged = sdk.users.isAdminOrGlobalBuilder(user)
   $: nameLabel = getNameLabel(user)
   $: filteredGroups = getFilteredGroups(internalGroups, searchTerm)
-  $: availableApps = getAvailableApps($appsStore.apps, privileged, user?.roles)
+  $: availableApps = user
+    ? getApps(user, sdk.users.userAppAccessList(user, $groups || []))
+    : []
   $: userGroups = $groups.filter(x => {
     return x.users?.find(y => {
       return y._id === userId
@@ -107,23 +109,19 @@
   $: globalRole = users.getUserRole(user)
   $: isTenantOwner = tenantOwner?.email && tenantOwner.email === user?.email
 
-  const getAvailableApps = (appList, privileged, roles) => {
-    let availableApps = appList.slice()
-    if (!privileged) {
-      availableApps = availableApps.filter(x => {
-        let roleKeys = Object.keys(roles || {})
-        return roleKeys.concat(user?.builder?.apps).find(y => {
-          return x.appId === appsStore.extractAppId(y)
-        })
-      })
-    }
+  const getApps = (user, appIds) => {
+    let availableApps = $appsStore.apps
+      .slice()
+      .filter(app =>
+        appIds.find(id => id === appsStore.getProdAppID(app.devId))
+      )
     return availableApps.map(app => {
       const prodAppId = appsStore.getProdAppID(app.devId)
       return {
         name: app.name,
         devId: app.devId,
         icon: app.icon,
-        role: getRole(prodAppId, roles),
+        role: getRole(prodAppId, user),
       }
     })
   }
@@ -136,7 +134,7 @@
     return groups.filter(group => group.name?.toLowerCase().includes(search))
   }
 
-  const getRole = (prodAppId, roles) => {
+  const getRole = (prodAppId, user) => {
     if (privileged) {
       return Constants.Roles.ADMIN
     }
@@ -145,7 +143,21 @@
       return Constants.Roles.CREATOR
     }
 
-    return roles[prodAppId]
+    if (user?.roles[prodAppId]) {
+      return user.roles[prodAppId]
+    }
+
+    // check if access via group for creator
+    const foundGroup = $groups?.find(
+      group => group.roles[prodAppId] || group.builder?.apps[prodAppId]
+    )
+    if (foundGroup.builder?.apps[prodAppId]) {
+      return Constants.Roles.CREATOR
+    }
+    // can't tell how groups will control role
+    if (foundGroup.roles[prodAppId]) {
+      return Constants.Roles.GROUP
+    }
   }
 
   const getNameLabel = user => {
diff --git a/packages/builder/src/pages/builder/portal/users/users/_components/AppRoleTableRenderer.svelte b/packages/builder/src/pages/builder/portal/users/users/_components/AppRoleTableRenderer.svelte
index 9429cfbc23..5adc38ebc6 100644
--- a/packages/builder/src/pages/builder/portal/users/users/_components/AppRoleTableRenderer.svelte
+++ b/packages/builder/src/pages/builder/portal/users/users/_components/AppRoleTableRenderer.svelte
@@ -15,7 +15,9 @@
   }
 </script>
 
-{#if value === Constants.Roles.CREATOR}
+{#if value === Constants.Roles.GROUP}
+  Controlled by group
+{:else if value === Constants.Roles.CREATOR}
   Can edit
 {:else}
   <StatusLight
diff --git a/packages/frontend-core/src/constants.ts b/packages/frontend-core/src/constants.ts
index 3e51719bfd..fdf4a8c36f 100644
--- a/packages/frontend-core/src/constants.ts
+++ b/packages/frontend-core/src/constants.ts
@@ -106,6 +106,7 @@ export const Roles = {
   PUBLIC: "PUBLIC",
   BUILDER: "BUILDER",
   CREATOR: "CREATOR",
+  GROUP: "GROUP",
 }
 
 export const EventPublishType = {

From 5cd5a201772b18863176d53d558ee44452be482d Mon Sep 17 00:00:00 2001
From: mike12345567 <me@michaeldrury.co.uk>
Date: Fri, 28 Feb 2025 15:01:34 +0000
Subject: [PATCH 16/21] Fixing an issue switching back to controlled by group.

---
 .../_components/BuilderSidePanel.svelte           | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
index 59d14770ae..737edd69f7 100644
--- a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
@@ -198,12 +198,19 @@
       return
     }
     const update = await users.get(user._id)
+    const newRoles = {
+      ...update.roles,
+      [prodAppId]: role,
+    }
+    // make sure no undefined/null roles (during removal)
+    for (let [appId, role] of Object.entries(newRoles)) {
+      if (!role) {
+        delete newRoles[appId]
+      }
+    }
     await users.save({
       ...update,
-      roles: {
-        ...update.roles,
-        [prodAppId]: role,
-      },
+      roles: newRoles,
     })
     await searchUsers(query, $builderStore.builderSidePanel, loaded)
   }

From 67d31b971a69e3d664606c36672c2758f51df7ab Mon Sep 17 00:00:00 2001
From: mike12345567 <me@michaeldrury.co.uk>
Date: Fri, 28 Feb 2025 16:56:31 +0000
Subject: [PATCH 17/21] Updating master reference.

---
 packages/pro | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/pro b/packages/pro
index e3843dd4ea..b28dbd5492 160000
--- a/packages/pro
+++ b/packages/pro
@@ -1 +1 @@
-Subproject commit e3843dd4eaced68ae063355b77df200dbc789c98
+Subproject commit b28dbd549284cf450be7f25ad85aadf614d08f0b

From ebfd8eb6c8a75e682ead8fd94d1079e11bd27c1f Mon Sep 17 00:00:00 2001
From: mike12345567 <me@michaeldrury.co.uk>
Date: Fri, 28 Feb 2025 17:14:19 +0000
Subject: [PATCH 18/21] Fixing readme action.

---
 .github/workflows/readme-openapi.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/readme-openapi.yml b/.github/workflows/readme-openapi.yml
index 9f42f6141b..e6224156b6 100644
--- a/.github/workflows/readme-openapi.yml
+++ b/.github/workflows/readme-openapi.yml
@@ -20,7 +20,7 @@ jobs:
       - run: yarn --frozen-lockfile
 
       - name: Install OpenAPI pkg
-        run: yarn global add openapi
+        run: yarn global add rdme openapi
 
       - name: update specs
-        run: cd packages/server && yarn specs && openapi specs/openapi.yaml --key=${{ secrets.README_API_KEY }} --id=6728a74f5918b50036c61841
+        run: cd packages/server && yarn specs && rdme openapi specs/openapi.yaml --key=${{ secrets.README_API_KEY }} --id=6728a74f5918b50036c61841

From 415570b802b3eea7aa9e4d2b5a5d0b135d70eb17 Mon Sep 17 00:00:00 2001
From: mike12345567 <me@michaeldrury.co.uk>
Date: Fri, 28 Feb 2025 17:21:57 +0000
Subject: [PATCH 19/21] Another readme action fix.

---
 .github/workflows/readme-openapi.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/readme-openapi.yml b/.github/workflows/readme-openapi.yml
index e6224156b6..f7a563fc45 100644
--- a/.github/workflows/readme-openapi.yml
+++ b/.github/workflows/readme-openapi.yml
@@ -23,4 +23,4 @@ jobs:
         run: yarn global add rdme openapi
 
       - name: update specs
-        run: cd packages/server && yarn specs && rdme openapi specs/openapi.yaml --key=${{ secrets.README_API_KEY }} --id=6728a74f5918b50036c61841
+        run: cd packages/server && yarn specs && rdme openapi upload specs/openapi.yaml --key=${{ secrets.README_API_KEY }} --id=6728a74f5918b50036c61841

From 4077a6df35c319bfdab9962ea7c2101388e7702c Mon Sep 17 00:00:00 2001
From: mike12345567 <me@michaeldrury.co.uk>
Date: Fri, 28 Feb 2025 17:48:23 +0000
Subject: [PATCH 20/21] Final fix for rdme.

---
 .github/workflows/readme-openapi.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/readme-openapi.yml b/.github/workflows/readme-openapi.yml
index f7a563fc45..ec04e4001c 100644
--- a/.github/workflows/readme-openapi.yml
+++ b/.github/workflows/readme-openapi.yml
@@ -20,7 +20,7 @@ jobs:
       - run: yarn --frozen-lockfile
 
       - name: Install OpenAPI pkg
-        run: yarn global add rdme openapi
+        run: yarn global add rdme
 
       - name: update specs
-        run: cd packages/server && yarn specs && rdme openapi upload specs/openapi.yaml --key=${{ secrets.README_API_KEY }} --id=6728a74f5918b50036c61841
+        run: cd packages/server && yarn specs && rdme openapi specs/openapi.yaml --key=${{ secrets.README_API_KEY }} --id=67c16880add6da002352069a

From b4ad744261a9cb47fcadf8e25af57909311900ee Mon Sep 17 00:00:00 2001
From: mike12345567 <me@michaeldrury.co.uk>
Date: Fri, 28 Feb 2025 17:52:18 +0000
Subject: [PATCH 21/21] Set rdme cli version (hardcoded).

---
 .github/workflows/readme-openapi.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/readme-openapi.yml b/.github/workflows/readme-openapi.yml
index ec04e4001c..27a68e69cf 100644
--- a/.github/workflows/readme-openapi.yml
+++ b/.github/workflows/readme-openapi.yml
@@ -20,7 +20,7 @@ jobs:
       - run: yarn --frozen-lockfile
 
       - name: Install OpenAPI pkg
-        run: yarn global add rdme
+        run: yarn global add rdme@8.6.6
 
       - name: update specs
         run: cd packages/server && yarn specs && rdme openapi specs/openapi.yaml --key=${{ secrets.README_API_KEY }} --id=67c16880add6da002352069a