diff --git a/frontend/src/pages/initiative/InitiativeInboxPage.jsx b/frontend/src/pages/initiative/InitiativeInboxPage.jsx
index cfa4484..32a1f9a 100644
--- a/frontend/src/pages/initiative/InitiativeInboxPage.jsx
+++ b/frontend/src/pages/initiative/InitiativeInboxPage.jsx
@@ -7,6 +7,7 @@ export function InitiativeInboxPage() {
initiative,
initiativeId,
backlogItems,
+ dataSlices,
roadmapItems,
featureParentActions,
workCycles,
@@ -31,8 +32,6 @@ export function InitiativeInboxPage() {
return
}
- const isProductArchetype = initiative?.archetype_key === 'initiative.product'
-
return (
<>
{error &&
{error}
}
@@ -43,7 +42,7 @@ export function InitiativeInboxPage() {
featureActions={featureParentActions}
workCycles={workCycles}
activeWorkCycle={activeWorkCycle}
- sprintPlanningEnabled={isProductArchetype}
+ sprintPlanningEnabled={dataSlices.includes('work_cycles')}
canManage={capabilities.has('kairo.backlog.manage')}
onCreate={handleCreateBacklog}
onUpdate={handleUpdateBacklog}
diff --git a/frontend/src/pages/modes/WorkSprintPage.jsx b/frontend/src/pages/modes/WorkSprintPage.jsx
index e168de1..1014c85 100644
--- a/frontend/src/pages/modes/WorkSprintPage.jsx
+++ b/frontend/src/pages/modes/WorkSprintPage.jsx
@@ -1,4 +1,4 @@
-import { useMemo } from 'react'
+import { useEffect, useMemo } from 'react'
import { Link } from 'react-router-dom'
import { RequireInitiativeScope } from '../../components/RequireInitiativeScope.jsx'
import { ScopedInitiativeProvider } from '../../layout/ScopedInitiativeProvider.jsx'
@@ -41,11 +41,23 @@ function WorkSprintInner() {
handleMoveActionToSprint,
handleUnplanAction,
initiative,
+ operatingContext,
+ dataSlices,
steeringSnapshot,
steeringSnapshotLoading,
visibleActions,
+ reloadSlices,
} = ops
+ const supportsWorkCycles = dataSlices.includes('work_cycles')
+ const isContinuousProduct = operatingContext?.method_key === 'continuous_product'
+
+ useEffect(() => {
+ if (initiativeId && !steeringSnapshot && !steeringSnapshotLoading) {
+ reloadSlices(['steering_snapshot'])
+ }
+ }, [initiativeId, steeringSnapshot, steeringSnapshotLoading, reloadSlices])
+
const defaultWorkCycleId = activeWorkCycle?.id || ''
const createAction = useMemo(
@@ -62,7 +74,7 @@ function WorkSprintInner() {
return
}
- const isProductArchetype = initiative?.archetype_key === 'initiative.product'
+ const isProductArchetype = isContinuousProduct && supportsWorkCycles
return (
<>
@@ -187,8 +199,8 @@ function WorkSprintInner() {
>
)}
- {!activeWorkCycle && !isProductArchetype && (
-
+ {!activeWorkCycle && !supportsWorkCycles && (
+
)}
>
)
diff --git a/frontend/src/registry/OperatingRouteGate.jsx b/frontend/src/registry/OperatingRouteGate.jsx
new file mode 100644
index 0000000..37a152c
--- /dev/null
+++ b/frontend/src/registry/OperatingRouteGate.jsx
@@ -0,0 +1,30 @@
+import { Navigate, useLocation } from 'react-router-dom'
+import { useProgramScope } from '../context/ProgramScopeContext.jsx'
+import { useMethodUiContext } from '../hooks/useMethodUiContext.js'
+import {
+ isRouteAllowedForProfile,
+ resolveRedirectForDisallowedRoute,
+} from './resolveOperatingProfile.js'
+
+/**
+ * AP2.3d — Redirect wenn Route für Archetyp/Slice unpassend.
+ */
+export function OperatingRouteGate({ children }) {
+ const location = useLocation()
+ const { hrefWithScope, initiativeId } = useProgramScope()
+ const { operatingContext, loading } = useMethodUiContext()
+
+ if (!initiativeId || loading) {
+ return children
+ }
+
+ const input = { operatingContext }
+ const path = location.pathname
+
+ if (isRouteAllowedForProfile(path, input)) {
+ return children
+ }
+
+ const redirect = resolveRedirectForDisallowedRoute(path, input)
+ return
+}
diff --git a/frontend/src/registry/viewRegistry.js b/frontend/src/registry/viewRegistry.js
index 4d11663..a298333 100644
--- a/frontend/src/registry/viewRegistry.js
+++ b/frontend/src/registry/viewRegistry.js
@@ -144,6 +144,38 @@ export const MODE_ROUTE_COMPONENTS = {
controlJourney: ControlJourneyPage,
}
+/** Modus-Routen mit Slice-Gating (AP2.3d). */
+export const MODE_ROUTES = [
+ { path: 'today', componentKey: 'workToday', requiredSlices: ['actions'] },
+ { path: 'mine', componentKey: 'workMine', requiredSlices: ['actions'] },
+ {
+ path: 'sprint',
+ componentKey: 'workSprint',
+ requiredSlices: ['work_cycles'],
+ routePath: '/work/sprint',
+ },
+ { path: 'portfolio', componentKey: 'planPortfolio', requiredSlices: [] },
+ { path: 'profile', componentKey: 'planProfile', requiredSlices: [] },
+ { path: 'structure', componentKey: 'planStructure', requiredSlices: ['projects'] },
+ { path: 'gates', componentKey: 'planGates', requiredSlices: ['roadmap'] },
+ { path: 'inbox', componentKey: 'planInbox', requiredSlices: ['backlog'] },
+ {
+ path: 'sprint',
+ componentKey: 'planSprint',
+ requiredSlices: ['work_cycles'],
+ routePath: '/plan/sprint',
+ },
+ { path: 'work', componentKey: 'planWork', requiredSlices: ['actions'] },
+ { path: 'status', componentKey: 'controlStatus', requiredSlices: [] },
+ { path: 'plan-ist', componentKey: 'controlPlanIst', requiredSlices: ['roadmap'] },
+ {
+ path: 'journey',
+ componentKey: 'controlJourney',
+ requiredSlices: ['roadmap'],
+ routePath: '/control/journey',
+ },
+]
+
export {
WorkLayout,
WorkIndexRedirect,