From f6da6d11d7af2abdeda51cc866fa712f19f69122 Mon Sep 17 00:00:00 2001 From: jawad-khan Date: Tue, 19 May 2026 21:06:06 +0500 Subject: [PATCH 1/2] fix: fixed twenty worspace provisiong --- .../bootstrap-sso-admin.command.spec.ts | 34 +++++++++---- .../commands/bootstrap-sso-admin.command.ts | 13 +++-- .../sso-user-provisioning.service.spec.ts | 33 +++++++++---- .../services/sso-user-provisioning.service.ts | 7 +-- .../twenty-config/config-variables.ts | 11 ++++- .../get-smb-workspace-subdomain.util.spec.ts | 49 +++++++++++++++++++ .../utils/get-smb-workspace-subdomain.util.ts | 32 ++++++++++++ .../constants/seeder-workspaces.constant.ts | 12 +++-- .../dev-seeder/services/dev-seeder.service.ts | 10 ++-- 9 files changed, 165 insertions(+), 36 deletions(-) create mode 100644 packages/twenty-server/src/engine/core-modules/twenty-config/utils/__tests__/get-smb-workspace-subdomain.util.spec.ts create mode 100644 packages/twenty-server/src/engine/core-modules/twenty-config/utils/get-smb-workspace-subdomain.util.ts diff --git a/packages/twenty-server/src/database/commands/bootstrap-sso-admin.command.spec.ts b/packages/twenty-server/src/database/commands/bootstrap-sso-admin.command.spec.ts index f18692f8c068b..ec79b61320415 100644 --- a/packages/twenty-server/src/database/commands/bootstrap-sso-admin.command.spec.ts +++ b/packages/twenty-server/src/database/commands/bootstrap-sso-admin.command.spec.ts @@ -2,7 +2,7 @@ import { Logger } from '@nestjs/common'; import { BootstrapSsoAdminCommand } from 'src/database/commands/bootstrap-sso-admin.command'; -type ConfigKey = 'SMB_NAME'; +type ConfigKey = 'SMB_NAME' | 'SMB_DEFAULT_WORKSPACE_NAME'; const buildCommand = (overrides?: { existingUser?: unknown; @@ -38,12 +38,19 @@ const buildCommand = (overrides?: { overrides?.helperResult ?? { wasExistingMember: false }, ), }; + const configuredSubdomain = overrides?.configuredSubdomain ?? 'askii'; const twentyConfigService = { - get: jest.fn((key: ConfigKey) => - key === 'SMB_NAME' - ? (overrides?.configuredSubdomain ?? 'askii') - : undefined, - ), + get: jest.fn((key: ConfigKey) => { + if (key === 'SMB_DEFAULT_WORKSPACE_NAME') { + return configuredSubdomain; + } + + if (key === 'SMB_NAME') { + return 'portal-slug'; + } + + return undefined; + }), }; const command = new BootstrapSsoAdminCommand( @@ -77,11 +84,20 @@ describe('BootstrapSsoAdminCommand', () => { logSpy.mockRestore(); }); - it('throws when SMB_NAME is not configured', async () => { - const { command } = buildCommand({ configuredSubdomain: '' }); + it('throws when SMB workspace subdomain is not configured', async () => { + const twentyConfigService = { + get: jest.fn(() => ''), + }; + const command = new BootstrapSsoAdminCommand( + { findOne: jest.fn(), create: jest.fn(), save: jest.fn() } as any, + { findOne: jest.fn() } as any, + { findOne: jest.fn() } as any, + { addUserToWorkspaceOrEnsureRole: jest.fn() } as any, + twentyConfigService as any, + ); await expect(command.run([], { email: 'admin@askii.ai' })).rejects.toThrow( - 'SMB_NAME is not configured', + 'SMB_DEFAULT_WORKSPACE_NAME (or SMB_NAME) is not configured', ); }); diff --git a/packages/twenty-server/src/database/commands/bootstrap-sso-admin.command.ts b/packages/twenty-server/src/database/commands/bootstrap-sso-admin.command.ts index 50578d060e7b4..11d41caa3c8dd 100644 --- a/packages/twenty-server/src/database/commands/bootstrap-sso-admin.command.ts +++ b/packages/twenty-server/src/database/commands/bootstrap-sso-admin.command.ts @@ -8,6 +8,7 @@ import { Repository } from 'typeorm'; import { hashPassword } from 'src/engine/core-modules/auth/auth.util'; import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; +import { getSmbWorkspaceSubdomain } from 'src/engine/core-modules/twenty-config/utils/get-smb-workspace-subdomain.util'; import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service'; import { UserEntity } from 'src/engine/core-modules/user/user.entity'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; @@ -18,7 +19,7 @@ type BootstrapSsoAdminOptions = { email: string; }; -// Pre-seed one Admin user for the SMB_NAME workspace so SSO sign-in lands a +// Pre-seed one Admin user for the SMB workspace so SSO sign-in lands a // useful role on first hit. Mirrors `provision-plane.sh` (ADMIN_EMAIL → // InstanceAdmin). Idempotent: subsequent runs ensure the Admin role on the // existing userWorkspace, even if the user signed in via SSO first and was @@ -26,12 +27,12 @@ type BootstrapSsoAdminOptions = { // // Wired into the foss-server-bundle-devstack provisioning pipeline at // `provision/provision-twenty.sh`, which runs `workspace:seed:dev --light` -// (creates the SMB_NAME workspace + standard roles) and then this command. +// (creates the SMB workspace + standard roles) and then this command. // Both steps are idempotent on re-runs. @Command({ name: 'workspace:bootstrap-sso-admin', description: - 'Pre-seed a Cognito-known email as the Admin user of the SMB_NAME workspace. Run once after `workspace:seed:dev --light` (see foss-server-bundle-devstack/provision/provision-twenty.sh).', + 'Pre-seed a Cognito-known email as the Admin user of the SMB workspace. Run once after `workspace:seed:dev --light` (see foss-server-bundle-devstack/provision/provision-twenty.sh).', }) export class BootstrapSsoAdminCommand extends CommandRunner { private readonly logger = new Logger(BootstrapSsoAdminCommand.name); @@ -62,10 +63,12 @@ export class BootstrapSsoAdminCommand extends CommandRunner { _passedParams: string[], options: BootstrapSsoAdminOptions, ): Promise { - const subdomain = this.twentyConfigService.get('SMB_NAME'); + const subdomain = getSmbWorkspaceSubdomain(this.twentyConfigService); if (!subdomain) { - throw new Error('SMB_NAME is not configured — refusing to bootstrap.'); + throw new Error( + 'SMB_DEFAULT_WORKSPACE_NAME (or SMB_NAME) is not configured — refusing to bootstrap.', + ); } const normalizedEmail = options.email.trim().toLowerCase(); diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/sso-user-provisioning.service.spec.ts b/packages/twenty-server/src/engine/core-modules/auth/services/sso-user-provisioning.service.spec.ts index 8a20488c99ef1..a995bf7ad6aad 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/sso-user-provisioning.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/sso-user-provisioning.service.spec.ts @@ -1,7 +1,7 @@ import { AuthException } from 'src/engine/core-modules/auth/auth.exception'; import { SsoUserProvisioningService } from 'src/engine/core-modules/auth/services/sso-user-provisioning.service'; -type ConfigKey = 'SMB_NAME'; +type ConfigKey = 'SMB_NAME' | 'SMB_DEFAULT_WORKSPACE_NAME'; const buildService = (overrides?: { existingUser?: unknown; @@ -24,12 +24,19 @@ const buildService = (overrides?: { const userWorkspaceService = { addUserToWorkspaceIfUserNotInWorkspace: jest.fn(), }; + const configuredSubdomain = overrides?.configuredSubdomain ?? 'askii'; const twentyConfigService = { - get: jest.fn((key: ConfigKey) => - key === 'SMB_NAME' - ? (overrides?.configuredSubdomain ?? 'askii') - : undefined, - ), + get: jest.fn((key: ConfigKey) => { + if (key === 'SMB_DEFAULT_WORKSPACE_NAME') { + return configuredSubdomain; + } + + if (key === 'SMB_NAME') { + return 'portal-slug'; + } + + return undefined; + }), }; const service = new SsoUserProvisioningService( @@ -63,11 +70,19 @@ describe('SsoUserProvisioningService', () => { await expect(service.findOrProvision(' ')).rejects.toThrow(AuthException); }); - it('should throw when SMB_NAME is not configured', async () => { - const { service } = buildService({ configuredSubdomain: '' }); + it('should throw when SMB workspace subdomain is not configured', async () => { + const twentyConfigService = { + get: jest.fn(() => ''), + }; + const service = new SsoUserProvisioningService( + { findOne: jest.fn(), create: jest.fn(), save: jest.fn() } as any, + { findOne: jest.fn() } as any, + { addUserToWorkspaceIfUserNotInWorkspace: jest.fn() } as any, + twentyConfigService as any, + ); await expect(service.findOrProvision('user@askii.ai')).rejects.toThrow( - 'SMB_NAME not configured', + 'SMB_DEFAULT_WORKSPACE_NAME (or SMB_NAME) not configured', ); }); diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/sso-user-provisioning.service.ts b/packages/twenty-server/src/engine/core-modules/auth/services/sso-user-provisioning.service.ts index 83ba04752f8f1..1a06ddb7b3200 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/sso-user-provisioning.service.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/sso-user-provisioning.service.ts @@ -11,6 +11,7 @@ import { } from 'src/engine/core-modules/auth/auth.exception'; import { hashPassword } from 'src/engine/core-modules/auth/auth.util'; import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; +import { getSmbWorkspaceSubdomain } from 'src/engine/core-modules/twenty-config/utils/get-smb-workspace-subdomain.util'; import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service'; import { UserEntity } from 'src/engine/core-modules/user/user.entity'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; @@ -47,11 +48,11 @@ export class SsoUserProvisioningService { ); } - const subdomain = this.twentyConfigService.get('SMB_NAME'); + const subdomain = getSmbWorkspaceSubdomain(this.twentyConfigService); if (!subdomain) { throw new AuthException( - 'SMB_NAME not configured', + 'SMB_DEFAULT_WORKSPACE_NAME (or SMB_NAME) not configured', AuthExceptionCode.INTERNAL_SERVER_ERROR, ); } @@ -62,7 +63,7 @@ export class SsoUserProvisioningService { if (!workspace) { this.logger.error( - `SSO landing workspace (subdomain="${subdomain}") missing — the seeder should have created it from SMB_NAME on first init.`, + `SSO landing workspace (subdomain="${subdomain}") missing — the seeder should have created it on first init.`, ); throw new AuthException( 'SSO workspace not provisioned', diff --git a/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts b/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts index 1b49bd7a7e83e..460e807862337 100644 --- a/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts +++ b/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts @@ -1715,11 +1715,20 @@ export class ConfigVariables { @ConfigVariablesMetadata({ group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: - 'Per-deployment tenant identifier wired into every foss-server-bundle-devstack app. Twenty uses it both as the seed workspace subdomain (see seeder-workspaces.constant.ts) and as the workspace SSO users join on first login. Required when AUTH_TYPE=SSO.', + 'Per-deployment tenant identifier (portal hostname segment) for the foss-server-bundle-devstack. Used by the frontend runtime config when AUTH_TYPE=SSO.', type: ConfigVariableType.STRING, }) @IsOptional() SMB_NAME = ''; + + @ConfigVariablesMetadata({ + group: ConfigVariablesGroup.ADVANCED_SETTINGS, + description: + 'SMB workspace subdomain for seeding and SSO auto-join (same as Plane/Outline/SurfSense). Falls back to SMB_NAME when unset.', + type: ConfigVariableType.STRING, + }) + @IsOptional() + SMB_DEFAULT_WORKSPACE_NAME = ''; } export const validate = (config: Record): ConfigVariables => { diff --git a/packages/twenty-server/src/engine/core-modules/twenty-config/utils/__tests__/get-smb-workspace-subdomain.util.spec.ts b/packages/twenty-server/src/engine/core-modules/twenty-config/utils/__tests__/get-smb-workspace-subdomain.util.spec.ts new file mode 100644 index 0000000000000..46bd7372cc90c --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/twenty-config/utils/__tests__/get-smb-workspace-subdomain.util.spec.ts @@ -0,0 +1,49 @@ +import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; +import { + getSmbWorkspaceSubdomain, + getSmbWorkspaceSubdomainFromProcessEnv, + isSmbBundleDeployment, +} from 'src/engine/core-modules/twenty-config/utils/get-smb-workspace-subdomain.util'; + +describe('getSmbWorkspaceSubdomain utils', () => { + const buildConfig = (values: Record) => + ({ + get: jest.fn((key: string) => values[key]), + }) as unknown as TwentyConfigService; + + const originalEnv = process.env; + + beforeEach(() => { + process.env = { ...originalEnv }; + }); + + afterAll(() => { + process.env = originalEnv; + }); + + it('prefers SMB_DEFAULT_WORKSPACE_NAME from process env', () => { + process.env.SMB_DEFAULT_WORKSPACE_NAME = 'acme-team'; + process.env.SMB_NAME = 'acme'; + + expect(getSmbWorkspaceSubdomainFromProcessEnv()).toBe('acme-team'); + }); + + it('prefers SMB_DEFAULT_WORKSPACE_NAME from config', () => { + const config = buildConfig({ + SMB_DEFAULT_WORKSPACE_NAME: 'acme-team', + SMB_NAME: 'acme', + }); + + expect(getSmbWorkspaceSubdomain(config)).toBe('acme-team'); + expect(isSmbBundleDeployment(config)).toBe(true); + }); + + it('falls back to SMB_NAME when default workspace name is unset', () => { + const config = buildConfig({ + SMB_DEFAULT_WORKSPACE_NAME: '', + SMB_NAME: 'acme', + }); + + expect(getSmbWorkspaceSubdomain(config)).toBe('acme'); + }); +}); diff --git a/packages/twenty-server/src/engine/core-modules/twenty-config/utils/get-smb-workspace-subdomain.util.ts b/packages/twenty-server/src/engine/core-modules/twenty-config/utils/get-smb-workspace-subdomain.util.ts new file mode 100644 index 0000000000000..9076c18e321e0 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/twenty-config/utils/get-smb-workspace-subdomain.util.ts @@ -0,0 +1,32 @@ +import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; + +export const getSmbWorkspaceSubdomainFromProcessEnv = (): string => { + const fromDefault = process.env.SMB_DEFAULT_WORKSPACE_NAME?.trim(); + + if (fromDefault) { + return fromDefault; + } + + return process.env.SMB_NAME?.trim() ?? ''; +}; + +export const getSmbWorkspaceSubdomain = ( + config: TwentyConfigService, +): string => { + const fromDefault = config.get('SMB_DEFAULT_WORKSPACE_NAME'); + + if (typeof fromDefault === 'string' && fromDefault.trim()) { + return fromDefault.trim(); + } + + const fromName = config.get('SMB_NAME'); + + if (typeof fromName === 'string' && fromName.trim()) { + return fromName.trim(); + } + + return ''; +}; + +export const isSmbBundleDeployment = (config: TwentyConfigService): boolean => + getSmbWorkspaceSubdomain(config).length > 0; diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/constants/seeder-workspaces.constant.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/constants/seeder-workspaces.constant.ts index fb0e312f13434..83beb5c7251e8 100644 --- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/constants/seeder-workspaces.constant.ts +++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/constants/seeder-workspaces.constant.ts @@ -34,12 +34,14 @@ export type SeededEmptyWorkspacesIds = // The "apple" seed slot is repurposed as the SSO landing workspace in the // foss-server-bundle-devstack: ForwardAuth + Traefik route -// foss-twenty. to the workspace whose subdomain matches the -// bundle's SMB_NAME (the canonical per-deployment tenant identifier wired -// into every app via docker-compose). Fall back to the upstream "apple" -// identity when SMB_NAME is unset so dev/test runs that don't set it stay +// twenty. to the workspace whose subdomain matches +// SMB_DEFAULT_WORKSPACE_NAME (or SMB_NAME when unset). Fall back to the +// upstream "apple" identity when neither is set so dev/test runs stay // byte-for-byte compatible with twentyhq. -const SEED_WORKSPACE_SUBDOMAIN = process.env.SMB_NAME ?? 'apple'; +const SEED_WORKSPACE_SUBDOMAIN = + process.env.SMB_DEFAULT_WORKSPACE_NAME?.trim() || + process.env.SMB_NAME?.trim() || + 'apple'; const SEED_WORKSPACE_DISPLAY_NAME = SEED_WORKSPACE_SUBDOMAIN.charAt(0).toUpperCase() + SEED_WORKSPACE_SUBDOMAIN.slice(1); diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/services/dev-seeder.service.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/services/dev-seeder.service.ts index da33e5a3e2310..7ace85ff6ec99 100644 --- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/services/dev-seeder.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/services/dev-seeder.service.ts @@ -8,6 +8,7 @@ import { ApplicationRegistrationService } from 'src/engine/core-modules/applicat import { ApplicationService } from 'src/engine/core-modules/application/application.service'; import { SdkClientGenerationService } from 'src/engine/core-modules/sdk-client/sdk-client-generation.service'; import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; +import { isSmbBundleDeployment } from 'src/engine/core-modules/twenty-config/utils/get-smb-workspace-subdomain.util'; import { UpgradeMigrationService } from 'src/engine/core-modules/upgrade/services/upgrade-migration.service'; import { UpgradeSequenceReaderService } from 'src/engine/core-modules/upgrade/services/upgrade-sequence-reader.service'; import { type UpgradeMigrationStatus } from 'src/engine/core-modules/upgrade/upgrade-migration.entity'; @@ -149,7 +150,7 @@ export class DevSeederService { // the minimal init path (member role + workspace activation, no // user-specific assignments). The first SSO user provisioned via // ForwardAuth picks up the member role from defaultRoleId on sign-in. - if (this.twentyConfigService.get('SMB_NAME')) { + if (isSmbBundleDeployment(this.twentyConfigService)) { await this.devSeederPermissionsService.initMinimalPermissionsAndActivateWorkspace( { workspaceId, @@ -199,7 +200,7 @@ export class DevSeederService { // when SMB_NAME is set, so skip the entire data fixture in that mode and // leave the workspace clean. SSO users provisioned via ForwardAuth get // their workspaceMember rows created at sign-in time. - if (!this.twentyConfigService.get('SMB_NAME')) { + if (!isSmbBundleDeployment(this.twentyConfigService)) { await this.devSeederDataService.seed({ schemaName, workspaceId, @@ -337,8 +338,9 @@ export class DevSeederService { // downstream FK reference (seedAgents / seedMetadataEntities) would // break against missing userWorkspace rows. Same single switch as the // workspace subdomain/displayName override in seeder-workspaces.constant.ts. - const seedDemoUsersAndDependents = - !this.twentyConfigService.get('SMB_NAME'); + const seedDemoUsersAndDependents = !isSmbBundleDeployment( + this.twentyConfigService, + ); await seedServerId({ queryRunner, schemaName }); if (seedDemoUsersAndDependents) { From 98680b1aa55583de7e24cd11c048e1dc5168d91c Mon Sep 17 00:00:00 2001 From: jawad-khan Date: Wed, 20 May 2026 13:20:35 +0500 Subject: [PATCH 2/2] fix: fixed copilot points --- .../constants/seeder-workspaces.constant.ts | 5 ++--- .../dev-seeder/services/dev-seeder.service.ts | 17 +++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/constants/seeder-workspaces.constant.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/constants/seeder-workspaces.constant.ts index 83beb5c7251e8..d812583c2929e 100644 --- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/constants/seeder-workspaces.constant.ts +++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/constants/seeder-workspaces.constant.ts @@ -1,5 +1,6 @@ import { WorkspaceActivationStatus } from 'twenty-shared/workspace'; +import { getSmbWorkspaceSubdomainFromProcessEnv } from 'src/engine/core-modules/twenty-config/utils/get-smb-workspace-subdomain.util'; import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; export const WORKSPACE_FIELDS_TO_SEED = [ @@ -39,9 +40,7 @@ export type SeededEmptyWorkspacesIds = // upstream "apple" identity when neither is set so dev/test runs stay // byte-for-byte compatible with twentyhq. const SEED_WORKSPACE_SUBDOMAIN = - process.env.SMB_DEFAULT_WORKSPACE_NAME?.trim() || - process.env.SMB_NAME?.trim() || - 'apple'; + getSmbWorkspaceSubdomainFromProcessEnv() || 'apple'; const SEED_WORKSPACE_DISPLAY_NAME = SEED_WORKSPACE_SUBDOMAIN.charAt(0).toUpperCase() + SEED_WORKSPACE_SUBDOMAIN.slice(1); diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/services/dev-seeder.service.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/services/dev-seeder.service.ts index 7ace85ff6ec99..82bd320bca57f 100644 --- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/services/dev-seeder.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/services/dev-seeder.service.ts @@ -145,11 +145,12 @@ export class DevSeederService { }); // initPermissions assigns admin/limited/guest/member roles to the demo - // userWorkspace rows (Tim, Jane, Jony, Phil + ~200 random). When SMB_NAME - // is set those userWorkspace rows weren't seeded above, so fall back to - // the minimal init path (member role + workspace activation, no - // user-specific assignments). The first SSO user provisioned via - // ForwardAuth picks up the member role from defaultRoleId on sign-in. + // userWorkspace rows (Tim, Jane, Jony, Phil + ~200 random). In SMB bundle + // deployments (SMB_DEFAULT_WORKSPACE_NAME or SMB_NAME set) those + // userWorkspace rows weren't seeded above, so fall back to the minimal init + // path (member role + workspace activation, no user-specific assignments). + // The first SSO user provisioned via ForwardAuth picks up the member role + // from defaultRoleId on sign-in. if (isSmbBundleDeployment(this.twentyConfigService)) { await this.devSeederPermissionsService.initMinimalPermissionsAndActivateWorkspace( { @@ -197,9 +198,9 @@ export class DevSeederService { // devSeederDataService.seed populates the workspace schema with demo CRM // data (companies, people, opportunities, workspace members). Workspace // member rows reference core."user" via userId FK, which we didn't seed - // when SMB_NAME is set, so skip the entire data fixture in that mode and - // leave the workspace clean. SSO users provisioned via ForwardAuth get - // their workspaceMember rows created at sign-in time. + // in SMB bundle deployments, so skip the entire data fixture in that mode + // and leave the workspace clean. SSO users provisioned via ForwardAuth + // get their workspaceMember rows created at sign-in time. if (!isSmbBundleDeployment(this.twentyConfigService)) { await this.devSeederDataService.seed({ schemaName,