@@ -50,8 +50,18 @@ const SELF_HOSTED: DeploymentShape = {
5050
5151const HOSTED : DeploymentShape = { ...SELF_HOSTED , hosted : true , billingEnabled : true }
5252
53+ /** A self-hosted deployment with every feature override on. */
54+ const SELF_HOSTED_ALL_FEATURES : DeploymentShape = {
55+ ...SELF_HOSTED ,
56+ features : { ...SELF_HOSTED . features , customBlocks : true } ,
57+ }
58+
59+ /** Every workspace-plane section a self-hosted deployment offers; BYOK is Sim Cloud only. */
60+ const SELF_HOSTED_WORKSPACE_SECTIONS = WORKSPACE_SETTINGS_ITEMS . map ( ( { id } ) => id ) . filter (
61+ ( id ) => id !== 'byok'
62+ )
63+
5364const ALL_ENTITLEMENTS = {
54- byok : true ,
5565 credentialGroups : true ,
5666 customBlocks : true ,
5767 forks : true ,
@@ -148,7 +158,7 @@ describe('settings navigation boundaries', () => {
148158 permission : 'admin' ,
149159 permissionConfig : { } ,
150160 entitlements : ALL_ENTITLEMENTS ,
151- hosted : false ,
161+ deployment : SELF_HOSTED ,
152162 } ) . map ( ( { id } ) => id )
153163 ) . toContain ( 'sandboxes' )
154164 } )
@@ -160,16 +170,43 @@ describe('settings navigation boundaries', () => {
160170 * drop it there.
161171 */
162172 it ( 'shows the Self-host section only on a self-hosted deployment' , ( ) => {
163- const navigate = ( hosted : boolean ) =>
173+ const navigate = ( deployment : DeploymentShape ) =>
164174 resolveWorkspaceNavigation ( {
165175 permission : 'admin' ,
166176 permissionConfig : { } ,
167177 entitlements : ALL_ENTITLEMENTS ,
168- hosted ,
178+ deployment ,
169179 } ) . map ( ( { id } ) => id )
170180
171- expect ( navigate ( false ) ) . toContain ( 'self-host' )
172- expect ( navigate ( true ) ) . not . toContain ( 'self-host' )
181+ expect ( navigate ( SELF_HOSTED ) ) . toContain ( 'self-host' )
182+ expect ( navigate ( HOSTED ) ) . not . toContain ( 'self-host' )
183+ } )
184+
185+ /**
186+ * The route gate and the sidebar must agree on deployment-gated sections: a
187+ * hosted-only section is offered on a self-hosted deployment only when its
188+ * feature override resolves on, so a direct link cannot open what the sidebar
189+ * hides. BYOK has no override and stays Sim Cloud only.
190+ */
191+ it ( 'offers hosted-only workspace sections on self-hosted only through their override' , ( ) => {
192+ const navigate = ( deployment : DeploymentShape ) =>
193+ resolveWorkspaceNavigation ( {
194+ permission : 'admin' ,
195+ permissionConfig : { } ,
196+ entitlements : ALL_ENTITLEMENTS ,
197+ deployment,
198+ } ) . map ( ( { id } ) => id )
199+
200+ const inboxDisabled : DeploymentShape = {
201+ ...SELF_HOSTED ,
202+ features : { ...SELF_HOSTED . features , inbox : false } ,
203+ }
204+ expect ( navigate ( inboxDisabled ) ) . not . toContain ( 'inbox' )
205+ expect ( navigate ( SELF_HOSTED ) ) . toContain ( 'inbox' )
206+ expect ( navigate ( { ...HOSTED , features : inboxDisabled . features } ) ) . toContain ( 'inbox' )
207+
208+ expect ( navigate ( SELF_HOSTED ) ) . not . toContain ( 'byok' )
209+ expect ( navigate ( HOSTED ) ) . toContain ( 'byok' )
173210 } )
174211
175212 /**
@@ -457,7 +494,6 @@ describe('settings navigation boundaries', () => {
457494 visible : [
458495 'teammates' ,
459496 'secrets' ,
460- 'byok' ,
461497 'sandboxes' ,
462498 'custom-tools' ,
463499 'mcp' ,
@@ -475,7 +511,6 @@ describe('settings navigation boundaries', () => {
475511 visible : [
476512 'teammates' ,
477513 'secrets' ,
478- 'byok' ,
479514 'sandboxes' ,
480515 'custom-tools' ,
481516 'mcp' ,
@@ -490,8 +525,8 @@ describe('settings navigation boundaries', () => {
490525 } ,
491526 {
492527 permission : 'admin' as const ,
493- visible : WORKSPACE_SETTINGS_ITEMS . map ( ( { id } ) => id ) ,
494- mutable : WORKSPACE_SETTINGS_ITEMS . map ( ( { id } ) => id ) ,
528+ visible : SELF_HOSTED_WORKSPACE_SECTIONS ,
529+ mutable : SELF_HOSTED_WORKSPACE_SECTIONS ,
495530 } ,
496531 ] ) (
497532 'makes workspace $permission navigation and mutation chrome explicit' ,
@@ -500,7 +535,7 @@ describe('settings navigation boundaries', () => {
500535 permission,
501536 permissionConfig : { } ,
502537 entitlements : ALL_ENTITLEMENTS ,
503- hosted : false ,
538+ deployment : SELF_HOSTED_ALL_FEATURES ,
504539 } )
505540
506541 expect ( items . map ( ( { id } ) => id ) ) . toEqual ( visible )
@@ -520,12 +555,11 @@ describe('settings navigation boundaries', () => {
520555 hideSandboxesTab : true ,
521556 } ,
522557 entitlements : ALL_ENTITLEMENTS ,
523- hosted : false ,
558+ deployment : SELF_HOSTED_ALL_FEATURES ,
524559 } )
525560
526561 expect ( items . map ( ( { id } ) => id ) ) . toEqual ( [
527562 'teammates' ,
528- 'byok' ,
529563 'credential-groups' ,
530564 'workflow-mcp-servers' ,
531565 'recently-deleted' ,
0 commit comments