diff --git a/docs/release-control/v6/internal/subsystems/cloud-paid.md b/docs/release-control/v6/internal/subsystems/cloud-paid.md index 6254576b5d..d62f2ce946 100644 --- a/docs/release-control/v6/internal/subsystems/cloud-paid.md +++ b/docs/release-control/v6/internal/subsystems/cloud-paid.md @@ -463,6 +463,12 @@ instead of wiring product-surface components such as `frontend-modern/src/components/Storage/Storage.tsx` directly into the router, so hosted bootstrap ownership stays at the app boundary rather than leaking route concerns back into feature components. +That same authenticated route shell also owns the canonical post-auth landing +path. `frontend-modern/src/App.tsx` must send `/` through the dashboard page +shell first, so existing operators land on the overview route and first-time +operators hit the governed dashboard empty state that forwards them into +Infrastructure Install, instead of carrying a root-only redirect to the +infrastructure route that bypasses the shared landing contract. The Pro license settings surface now follows the same rule as well. Changes to `frontend-modern/src/components/Settings/ProLicensePanel.tsx` must carry this contract and the dedicated Pro-license proof file instead of remaining an diff --git a/docs/release-control/v6/internal/subsystems/frontend-primitives.md b/docs/release-control/v6/internal/subsystems/frontend-primitives.md index 7c26d4edab..92a6dbf073 100644 --- a/docs/release-control/v6/internal/subsystems/frontend-primitives.md +++ b/docs/release-control/v6/internal/subsystems/frontend-primitives.md @@ -192,6 +192,11 @@ work extends shared components instead of creating new local variants. must name the canonical install workspace explicitly and expose the same first-host next step instead of falling back to passive “nothing here yet” wording. +11. Keep the authenticated app root aligned with that same first-session path. + `frontend-modern/src/App.tsx` must land `/` on the dashboard shell and let + the governed dashboard empty state route first-time operators into + Infrastructure Install, instead of preserving a separate root-only jump to + `/infrastructure` that drifts from the rest of the onboarding contract. ## Current State diff --git a/frontend-modern/src/App.tsx b/frontend-modern/src/App.tsx index 18b8617712..9f4bad9d5f 100644 --- a/frontend-modern/src/App.tsx +++ b/frontend-modern/src/App.tsx @@ -34,6 +34,7 @@ import { aiChatStore } from './stores/aiChat'; import { useKeyboardShortcuts } from './hooks/useKeyboardShortcuts'; import { useKioskMode } from '@/hooks/useKioskMode'; import { + DASHBOARD_PATH, buildRecoveryPath, buildInfrastructurePath, buildStoragePath, @@ -78,7 +79,8 @@ const SetupCompletionPreviewPage = lazy(() => default: module.SetupCompletionPreview, })), ); -const ROOT_INFRASTRUCTURE_PATH = buildInfrastructurePath(); +const ROOT_DASHBOARD_PATH = DASHBOARD_PATH; +const INFRASTRUCTURE_ROUTE_PATH = buildInfrastructurePath(); const ROOT_WORKLOADS_PATH = buildWorkloadsPath(); const STORAGE_PATH = buildStoragePath(); const RECOVERY_ROUTE_PATH = buildRecoveryPath(); @@ -378,13 +380,13 @@ function App() { - - } /> + + } /> - + diff --git a/frontend-modern/src/__tests__/App.architecture.test.ts b/frontend-modern/src/__tests__/App.architecture.test.ts index 7558049938..4e1f417e65 100644 --- a/frontend-modern/src/__tests__/App.architecture.test.ts +++ b/frontend-modern/src/__tests__/App.architecture.test.ts @@ -5,9 +5,13 @@ import appRuntimeStateSource from '@/useAppRuntimeState.ts?raw'; describe('App architecture', () => { it('keeps App as the entry shell that delegates runtime and chrome ownership', () => { + expect(appSource).toContain('DASHBOARD_PATH,'); expect(appSource).toContain("import { AppLayout } from '@/AppLayout';"); expect(appSource).toContain("import { useAppRuntimeState } from '@/useAppRuntimeState';"); expect(appSource).toContain('const runtime = useAppRuntimeState();'); + expect(appSource).toContain('const ROOT_DASHBOARD_PATH = DASHBOARD_PATH;'); + expect(appSource).toContain(''); + expect(appSource).toContain(' } />'); expect(appSource).toContain("const StoragePage = lazy(() => import('./pages/Storage'));"); expect(appSource).toContain("const OperationsPage = lazy(() => import('./pages/Operations'));"); expect(appSource).not.toContain(