From 704a3702573ee3bce7a3aa2ff8bc4c23a1bf4a7c Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Sat, 31 Oct 2026 01:21:41 -0700 Subject: [PATCH 1/3] fix(settings): hide Developer apps behind a default-off flag (TASK-674) Settings > Connected apps mounts AppsManagement (custom app create, edit, delete, use). Those paths are not fully walked, so the disclosure is now gated on REACT_APP_DEVELOPER_APPS=true, default off. The Connectors link stays, and the component is kept so the flag flips it back on. Written by Rivet, a Commonly agent. Co-Authored-By: Claude Haiku 5.5 Co-authored-by: rivet Agent-Model: claude/claude-haiku-5-5/default --- frontend/src/v2/__tests__/V2SettingsPage.test.tsx | 7 +++---- frontend/src/v2/components/V2SettingsPage.tsx | 14 ++++++++++---- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/frontend/src/v2/__tests__/V2SettingsPage.test.tsx b/frontend/src/v2/__tests__/V2SettingsPage.test.tsx index 73e73a97f5..ddb6cef889 100644 --- a/frontend/src/v2/__tests__/V2SettingsPage.test.tsx +++ b/frontend/src/v2/__tests__/V2SettingsPage.test.tsx @@ -138,14 +138,13 @@ describe('V2SettingsPage', () => { expect(screen.getByText('Device controls')).toBeInTheDocument(); expect(screen.getByText('Plan controls')).toHaveAttribute('data-show-heading', 'false'); expect(screen.getByText('Device controls')).toHaveAttribute('data-show-heading', 'false'); - expect(screen.getByText('Connected app controls')).toBeInTheDocument(); + // Developer apps stay hidden unless REACT_APP_DEVELOPER_APPS=true (TASK-674). + expect(screen.queryByText('Connected app controls')).not.toBeInTheDocument(); expect(screen.getByRole('img', { name: 'lily avatar' })).toHaveClass('v2-settings__avatar'); expect(screen.getByRole('img', { name: 'lily avatar' })).toHaveAttribute('src', '/uploads/lily.png'); expect(screen.getByRole('img', { name: 'lily avatar' })).toHaveAttribute('data-kind', 'human'); - expect(screen.getByText('Connected app controls')).toHaveAttribute('data-variant', 'settings'); - expect(screen.getByText('Connected app controls')).toHaveAttribute('data-show-integrations', 'false'); expect(screen.getByRole('link', { name: /Connectors/ })).toHaveAttribute('href', '/v2/connectors'); - expect(screen.getByText('Developer apps').closest('details')).not.toHaveAttribute('open'); + expect(screen.queryByText('Developer apps')).not.toBeInTheDocument(); expect(screen.getByRole('button', { name: 'Regenerate' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: 'Upload photo' })).toBeInTheDocument(); expect(screen.getByText('Regenerate for another, or upload a photo.')).toBeInTheDocument(); diff --git a/frontend/src/v2/components/V2SettingsPage.tsx b/frontend/src/v2/components/V2SettingsPage.tsx index 9e4c8f2c0d..a118987e69 100644 --- a/frontend/src/v2/components/V2SettingsPage.tsx +++ b/frontend/src/v2/components/V2SettingsPage.tsx @@ -42,6 +42,10 @@ const SETTINGS_SECTIONS = [ { id: 'language', title: 'Language' }, ] as const; +// Custom "Developer apps" (AppsManagement) stays hidden until every path +// (create, edit, delete, use) is walked live. Default off (TASK-674, D215). +const DEVELOPER_APPS_ENABLED = process.env.REACT_APP_DEVELOPER_APPS === 'true'; + // Admin-only: the instance's user list and usage analytics. Their routes // survived the Settings consolidation (#1544) but the way in did not β€” the // pages were reachable only by typing the URL (2026-09-08). @@ -554,10 +558,12 @@ const V2SettingsPage: React.FC = () => { label="Connectors" detail="Manage connected services and channels" /> -
- Developer apps - -
+ {DEVELOPER_APPS_ENABLED && ( +
+ Developer apps + +
+ )} {isAdmin && ( From d05ca60dcfc6136ff95b2aa21a2dcc4b4428110a Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Sat, 31 Oct 2026 01:26:53 -0700 Subject: [PATCH 2/3] fix(build): pass REACT_APP_DEVELOPER_APPS through vite define (TASK-674) The bare `process.env: {}` define erases every process.env read that is not listed, so the developer-apps flag would always be undefined in a built bundle and could never turn the section back on. Pass it through explicitly, empty by default, so CI and deploy leave it off. Written by Rivet, a Commonly agent. Co-Authored-By: Claude Haiku 5.5 Co-authored-by: rivet Agent-Model: claude/claude-haiku-5-5/default --- frontend/vite.config.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 1153c173c8..87debaadc7 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -41,6 +41,8 @@ export default defineConfig({ // Error tracking is opt-in at build time. An empty value lets Rollup remove // the guarded dynamic import, keeping the SDK out of self-hosted bundles. 'process.env.REACT_APP_SENTRY_DSN': JSON.stringify(process.env.REACT_APP_SENTRY_DSN || ''), + // Developer apps flag (TASK-674). Unset in CI and deploy, so prod stays off. + 'process.env.REACT_APP_DEVELOPER_APPS': JSON.stringify(process.env.REACT_APP_DEVELOPER_APPS || ''), 'process.env': {}, }, }); From 8a2705a6345ad3b54819ec0df57f7d9426d17149 Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Sat, 31 Oct 2026 01:45:32 -0700 Subject: [PATCH 3/3] test(settings): cover the Developer apps flag when it is set (TASK-674) Read REACT_APP_DEVELOPER_APPS at render time so the test can flip it. Shows the section only for exactly 'true'; any other value keeps it hidden. Written by Rivet, a Commonly agent. Co-Authored-By: Claude Haiku 5.5 Co-authored-by: rivet Agent-Model: claude/claude-haiku-5-5/default --- .../src/v2/__tests__/V2SettingsPage.test.tsx | 18 ++++++++++++++++++ frontend/src/v2/components/V2SettingsPage.tsx | 4 ++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/frontend/src/v2/__tests__/V2SettingsPage.test.tsx b/frontend/src/v2/__tests__/V2SettingsPage.test.tsx index ddb6cef889..04be03b7e0 100644 --- a/frontend/src/v2/__tests__/V2SettingsPage.test.tsx +++ b/frontend/src/v2/__tests__/V2SettingsPage.test.tsx @@ -152,6 +152,24 @@ describe('V2SettingsPage', () => { expect(screen.getByRole('radio', { name: 'δΈ­ζ–‡' })).toBeInTheDocument(); }); + test('shows Developer apps only when REACT_APP_DEVELOPER_APPS is exactly true', () => { + (axios.get as jest.Mock).mockResolvedValue({ data: { hasToken: false } }); + const previous = process.env.REACT_APP_DEVELOPER_APPS; + try { + process.env.REACT_APP_DEVELOPER_APPS = 'yes'; + const off = renderSettings(); + expect(screen.queryByText('Developer apps')).not.toBeInTheDocument(); + off.unmount(); + + process.env.REACT_APP_DEVELOPER_APPS = 'true'; + renderSettings(); + expect(screen.getByText('Developer apps')).toBeInTheDocument(); + } finally { + if (previous === undefined) delete process.env.REACT_APP_DEVELOPER_APPS; + else process.env.REACT_APP_DEVELOPER_APPS = previous; + } + }); + test('uses a left navigation column to target Settings sections', () => { (axios.get as jest.Mock).mockResolvedValue({ data: { hasToken: false } }); renderSettings(); diff --git a/frontend/src/v2/components/V2SettingsPage.tsx b/frontend/src/v2/components/V2SettingsPage.tsx index a118987e69..327a8e8e8f 100644 --- a/frontend/src/v2/components/V2SettingsPage.tsx +++ b/frontend/src/v2/components/V2SettingsPage.tsx @@ -44,7 +44,7 @@ const SETTINGS_SECTIONS = [ // Custom "Developer apps" (AppsManagement) stays hidden until every path // (create, edit, delete, use) is walked live. Default off (TASK-674, D215). -const DEVELOPER_APPS_ENABLED = process.env.REACT_APP_DEVELOPER_APPS === 'true'; +const developerAppsEnabled = () => process.env.REACT_APP_DEVELOPER_APPS === 'true'; // Admin-only: the instance's user list and usage analytics. Their routes // survived the Settings consolidation (#1544) but the way in did not β€” the @@ -558,7 +558,7 @@ const V2SettingsPage: React.FC = () => { label="Connectors" detail="Manage connected services and channels" /> - {DEVELOPER_APPS_ENABLED && ( + {developerAppsEnabled() && (
Developer apps