diff --git a/docs/product/Kairo_MVP_Execution_Plan_v0.2.md b/docs/product/Kairo_MVP_Execution_Plan_v0.2.md
index fd1d1ca..fb30047 100644
--- a/docs/product/Kairo_MVP_Execution_Plan_v0.2.md
+++ b/docs/product/Kairo_MVP_Execution_Plan_v0.2.md
@@ -159,4 +159,4 @@ Bei Konflikten:
---
-*Siehe auch: `Kairo_Implementation_Truth_Table_v0.1.md` § Referenz-Archetypen, `Kairo_Status_Review_and_Next_Steps_v0.2.md`*
+*Siehe auch: `Kairo_Implementation_Truth_Table_v0.1.md` § Referenz-Archetypen, `Kairo_Status_Review_and_Next_Steps_v0.2.md`, **`Kairo_Session_Handover_2026-07-12.md`** (Session-Feedback & Stop-Regeln)*
diff --git a/docs/product/Kairo_Session_Handover_2026-07-12.md b/docs/product/Kairo_Session_Handover_2026-07-12.md
new file mode 100644
index 0000000..4e2facd
--- /dev/null
+++ b/docs/product/Kairo_Session_Handover_2026-07-12.md
@@ -0,0 +1,195 @@
+# Jinkendo Kairo
+## Session-Handover — 2026-07-12
+
+**Status:** Übergabe an neuen Chat / nächsten Agenten
+**Stand:** 2026-07-12 (Abend)
+**Branch:** `develop` @ `82a5b9a` (gepusht auf Remote)
+**Version (Deploy):** `0.20.2-ap2.2d`
+**Vorgänger-Chat:** [MVP-Plan AP2.2 Session](c5b5c664-9bf9-486a-acf9-f20bb005b32e)
+
+---
+
+## 1. Wichtigste Klarstellung
+
+**Es gibt keinen neuen „Plan v0.3“-Bedarf.** Zu Beginn dieser Session wurde der Kurs bereits in **`Kairo_MVP_Execution_Plan_v0.2.md`** festgezogen (PO-Kurskorrektur: Referenz-Archetypen statt Dogfooding-Seed).
+
+Was in der Session schief lief: **Implementierung lief schneller als das Zielbild** — AP2.2b/c/d wurden als Code-Pakete abgearbeitet, während **End-to-End-Flows und methodengebundene Default-UI (AP1.9d)** fehlen. Der Agent schlug am Ende fälschlich vor, den Plan *noch einmal* neu aufzustellen. **Das lehnt der PO ab.**
+
+**Für den nächsten Chat gilt:**
+
+1. **`Kairo_MVP_Execution_Plan_v0.2.md` bleibt führend** — nicht ersetzen, nicht parallel neu erfinden.
+2. **Dieses Handover** präzisiert Interpretation, PO-Feedback und **Stop-Regeln** für Implementierung.
+3. Nächste Arbeit = **Zielbild-Flows schließen**, nicht weitere Nav-/Hint-Patches.
+
+---
+
+## 2. Session-Verlauf (Kurz)
+
+| Phase | Inhalt |
+|-------|--------|
+| Start | MVP-Plan fortsetzen; Kurskorrektur: mehrere Referenz-Archetypen anleg- und pflegbar (nicht Dogfooding spiegeln) |
+| AP2.2a | Gepusht (`2b0d5dd`): Starter-Kits, geführte Anlage, `seed_004` deprecated |
+| AP2.2b/c | Committed + gepusht (`0b57e2e`): A1-Stufenwechsel, Team-Fix, Agent-POST, Steuerungs-Hinweise |
+| AP2.2d | Committed + gepusht (`82a5b9a`): Sprint-UI, work_cycle-API im Frontend, Backlog→Sprint-Autoassign |
+| PO-Test | UX-Probleme: zu viele Archetypen, UUIDs im Team, fehlende Actor-Anlage, Reifegrad schwer findbar |
+| PO-Agile | Zeitbox nicht als Scrum-Kern; Sprint-Planung aus Backlog fehlt; Begriff „Zeitbox“ verwirrend |
+| Abschluss | Session dokumentieren, sauberer Handover — **kein weiterer Plan-Reset** |
+
+---
+
+## 3. Was auf `develop` liegt (Commits)
+
+```
+82a5b9a AP2.2d: B2b Product-Backlog und B3 Sprint-Backlog in UI.
+0b57e2e AP2.2b/c: A1-Stufenwechsel, Team-Fix, Agent-Anlage, Steuerungs-Hinweise.
+2b0d5dd AP2.2a: Archetyp-Starter-Kits und MVP Execution Plan v0.2.
+```
+
+### AP2.2a (Infrastruktur + Anlage)
+- `backend/services/archetype_starter_kit.py` — Kits A1, A2, B2b, B2a
+- `GET /api/entity-archetypes/{key}/starter-preview`
+- `POST /api/initiatives` mit `apply_starter_kit`
+- Frontend: Vorschau in `InitiativeForm.jsx`
+
+### AP2.2b/c (Teil-UI, Backend teils fertig)
+- AP2.0e: `maturity_stage_transition.py` — Recurring rotiert bei Stufen-Verify
+- Team: `display_name`, `POST /api/actors`, Agent-Formular
+- `ArchetypeSteeringHints.jsx`, Optgroups „MVP Stufe A“
+
+### AP2.2d (Teil-UI — **PO: unzureichend für Scrum-Zielbild**)
+- `frontend/src/api/workCycles.js`, `WorkCyclesPanel.jsx`
+- Plan → **Sprint**, Ausführen → **Sprint**
+- Backlog-Convert setzt `work_cycle_id` der aktiven Zeitbox
+- `agile_iteration`-Strategie bei aktivem Sprint + `continuous_product`
+- **Fehlt:** Sprint-Planung (Backlog auswählen → in Sprint legen), Sprint-Abschluss, optionaler Sprint ohne Zeit-Druck
+
+---
+
+## 4. PO-Feedback (verbindlich für nächste Implementierung)
+
+### 4.1 Allgemein
+- **Keine Shortcuts:** Keine Hints/Nav/Redirects als Ersatz für echte methodengebundene Flows.
+- **Keine Seeds/Vorbefüllung** als Produkt-Erfahrung; Starter-Kits nur optional beim Anlegen, sonst leeres Vorhaben.
+- **GUI muss Prozess zeigen:** Archetyp → Methode → Plan vs. Ist → Kontrolle — nicht sechs lose Plan-Tabs.
+- **Abhängigkeiten (Gate-Graph, kritischer Pfad)** bleiben Pflicht (A2, auch unabhängig von Agile).
+
+### 4.2 Agile / Scrum
+- **Agile ist Methode** (`agile_iteration`), **kein Archetyp**. Basis: `initiative.product` + `continuous_product`.
+- **Sprint ist Planungs-Container**, kein Kalender-Zwang. `work_cycle` im Modell, in UI: **„Sprint“** (nicht „Zeitbox“).
+- Gewünschter Flow:
+ 1. Priorisierbares Product Backlog (Eingang)
+ 2. Sprint-Planung anstoßen
+ 3. Items aus Backlog in Sprint legen (Commit)
+ 4. Sprint ausführen
+ 5. Sprint abschließen → Review/Carryover → nächste Planung
+- **Aktueller Ist-Stand:** nur „In Maßnahme umwandeln“ ohne Sprint-Auswahl; Ausführen→Sprint leer ohne aktiven Sprint.
+
+### 4.3 Verwirrung in der GUI (Ist)
+- Zu viele gleichwertige Plan-Unterpunkte ohne Prozessleiste
+- Begriffe: Zeitbox, Maßnahme, Arbeit, Sprint, Eingang — nicht konsistent mit Vision §4 Product Language
+- AP1.9d (Methoden-Default-UI) **offen** — überall Listen statt Next Action / methodenspezifischer Default
+
+---
+
+## 5. Execution Plan v0.2 — Interpretations-Korrektur
+
+**Plan bleibt gültig.** Folgende Zeilen sind **überzogen als „✓“ markiert** und sollten im nächsten Chat so gelesen werden:
+
+| Paket | Plan-Status | Tatsächlicher Stand |
+|-------|-------------|---------------------|
+| AP2.2a | ✓ | Anlage + Starter-Kit — OK als Basis |
+| AP2.2b | ◐ | Graph/Strategie da; Kontrolle A2 End-to-End fehlt |
+| AP2.2c | ◐ | AP2.0e Backend; Journey/Recurring-UX unvollständig |
+| AP2.2d | ✓ (Plan) | **Nur Infrastruktur + Teil-UI** — Sprint-Planungs-Flow fehlt (ADP § Sprint Planning = ○) |
+| AP1.9d | offen | **Blocker** für verständliche GUI — vor weiteren AP2.2-Patches priorisieren |
+
+**Nächste Priorität laut Plan (unverändert):** AP1.9d → AP2.2b/c/d **Flows schließen** → AP1.7b → AP2.1 Validation.
+
+**Nicht tun:** Execution Plan v0.3 schreiben, es sei denn PO fordert es explizit.
+
+---
+
+## 6. Zielbild — ein Bild für alle Archetypen
+
+Drei Schichten (aus Vision v0.2 + MVP v0.3):
+
+```text
+Archetyp (Was?) → Methode (Wie steuern?) → Plan vs. Ist (Was ist wann?)
+```
+
+| Archetyp | Methode | Plan | Ist | Kontrolle |
+|----------|---------|------|-----|-----------|
+| A2 Linear | `sequential_dependency` | Gate-Graph, Abhängigkeiten | APs an Gates | Nächster ready Schritt am kritischen Pfad |
+| A1 Reifegrad | `maturity_progression` | Stufen + Graph | Recurring + APs | Aktive Stufe + heutige Übung |
+| B2b Product | `continuous_product` | Eingang (Product Backlog) | Committete APs | Wirkungsvollster Schritt |
+| + Agile (Profil) | `agile_iteration` | Sprint-Planung | Sprint-Backlog | Nächster Schritt im Sprint |
+
+Referenz-UI-Konzept (noch PO-Entwurf): `Kairo_PM_Frontend_UI_Concept_v0.1.md` — Modi + Scope + Objekt; **Prozessleiste pro Methode fehlt noch in Implementierung**.
+
+---
+
+## 7. Stop-Regeln für nächsten Agenten
+
+1. **Kein neuer Execution Plan** ohne PO-Freigabe.
+2. **Keine weiteren Subnav-Einträge** ohne abgeschlossenen End-to-End-Flow.
+3. **Keine Seeds / Dogfooding / Demo-Vorhaben** als Implementierungs-Hebel.
+4. **Kein „✓“** in Truth Table / Execution Plan ohne PO-Durchspielbarkeit (Leitfrage ≤2 Min).
+5. **Product Language in UI:** Sprint (nicht Zeitbox), Arbeitspaket (nicht Maßnahme wo möglich), Eingang = Product Backlog.
+6. **Backend-Tests lokal** oft ohne DB — Verifikation über Push auf `develop` → Pi-Deploy (siehe `.cursor/rules/kairo-deployment-testing.mdc`).
+7. **Nicht proaktiv committen/pushen** ohne Nutzeranfrage.
+
+---
+
+## 8. Empfohlener nächster Arbeitsauftrag (für PO-Freigabe)
+
+**Ein zusammenhängendes Paket** — nicht AP2.2x-Fragmente:
+
+### Option A (PO-Priorität GUI-Klarheit): AP1.9d + Product Language
+- Methoden-Default pro Modus (Kontrolle/Ausführen/Plan)
+- Umbenennung Zeitbox → Sprint in der GUI
+- Prozessleiste im Scope bei Product-Vorhaben: Eingang → Sprint planen → Ausführen → Kontrolle
+
+### Option B (Agile-Lücke schließen): Sprint-Planung (Erweiterung AP2.2d, kein neuer Plan)
+- Backlog-Items markieren → „In Sprint [R2] planen“
+- Sprint-Status: geplant → aktiv → abgeschlossen
+- Ausführen ohne aktiven Sprint: Fallback `continuous_product`, nicht leere Sprint-Seite
+
+### Option C (Abhängigkeiten): AP2.2b A2 End-to-End
+- Kontrolle: kritischer Pfad + Begründung aus Execution Graph
+
+**PO sollte eine Option wählen** — nicht alles parallel.
+
+---
+
+## 9. Dokumente — Lesereihenfolge nächster Chat
+
+1. **Dieses Handover**
+2. `docs/product/Kairo_MVP_Execution_Plan_v0.2.md` (führend, unverändert)
+3. `docs/product/Kairo_MVP_Definition_v0.3.md`
+4. `docs/product/Kairo_Vision_and_Product_Direction_v0.2.md` §4–5 (Product Language, Plan vs. Ist)
+5. `docs/architecture/ADP_Archetype_and_Method_Catalog_v0.2.md` §2.3, § Sprint-Semantik
+6. `docs/product/Kairo_PM_Frontend_UI_Concept_v0.1.md` (UI-Zielbild)
+7. `docs/product/Kairo_Implementation_Truth_Table_v0.1.md` (Ist-Check, mit Skepsis gegenüber ✓)
+
+---
+
+## 10. Remote-Verifikation (Pi)
+
+Nach Deploy auf `develop`:
+- pytest: `test_ap22a_starter_kit`, `test_ap20e_maturity_transition`, `test_ap22_actor_create`, `test_ap22d_sprint_backlog`, `test_ap20f_work_cycle`
+- Manuell: Product anlegen → Eingang → Sprint anlegen → Convert → Ausführen/Sprint
+
+---
+
+## 11. Prompt-Vorschlag für neuen Chat
+
+```text
+Lies docs/product/Kairo_Session_Handover_2026-07-12.md und arbeite strikt nach
+Kairo_MVP_Execution_Plan_v0.2.md. Kein neuer Plan, keine Seeds, keine Shortcuts.
+PO-Priorität: [Option A / B / C aus Handover §8 eintragen].
+Zuerst kurz bestätigen, welchen End-to-End-Flow du schließt, dann implementieren.
+```
+
+---
+
+*Erstellt am Abschluss der Session 2026-07-12. Execution Plan v0.2 bleibt maßgeblich; dieses Dokument ist Interpretations- und Feedback-Anhang.*
diff --git a/frontend/src/components/ActionForm.jsx b/frontend/src/components/ActionForm.jsx
index fb0f9e0..6731545 100644
--- a/frontend/src/components/ActionForm.jsx
+++ b/frontend/src/components/ActionForm.jsx
@@ -108,7 +108,7 @@ export function ActionForm({
/>
{cycleOptions.length > 0 && (
)}
{workCycles.length === 0 ? (
-
+
) : (
{workCycles.map((cycle) => (
diff --git a/frontend/src/config/methodUiDefaults.js b/frontend/src/config/methodUiDefaults.js
new file mode 100644
index 0000000..f0db98a
--- /dev/null
+++ b/frontend/src/config/methodUiDefaults.js
@@ -0,0 +1,180 @@
+/**
+ * AP1.9d — Methoden-Default-UI: Archetyp/Methode steuert Prozessleiste,
+ * Modus-Defaults und sichtbare Nav-Knoten (Anti-Todo-Wand).
+ *
+ * @typedef {{ key: string, to: string, label: string, mode: 'plan'|'work'|'control' }} ProcessStep
+ * @typedef {{
+ * archetypeKey: string | null,
+ * methodKey: string | null,
+ * hasActiveSprint: boolean,
+ * }} MethodUiInput
+ * @typedef {{
+ * processSteps: ProcessStep[],
+ * planDefaultRoute: string,
+ * workDefaultRoute: string,
+ * controlDefaultRoute: string,
+ * planOutlineKeys: string[] | null,
+ * workNavKeys: string[] | null,
+ * }} MethodUiDefaults
+ */
+
+import { PLAN_OUTLINE_NODES } from '../plan/planOutlineNodes.js'
+import { WORK_NAV_ITEMS } from './workNav.js'
+
+const PRODUCT_PROCESS = [
+ { key: 'inbox', to: '/plan/inbox', label: 'Eingang', mode: 'plan' },
+ { key: 'sprint-plan', to: '/plan/sprint', label: 'Sprint planen', mode: 'plan' },
+ { key: 'sprint-work', to: '/work/sprint', label: 'Ausführen', mode: 'work' },
+ { key: 'control', to: '/control/status', label: 'Kontrolle', mode: 'control' },
+]
+
+const LINEAR_PROCESS = [
+ { key: 'gates', to: '/plan/gates', label: 'Zielzustände', mode: 'plan' },
+ { key: 'work', to: '/work/today', label: 'Ausführen', mode: 'work' },
+ { key: 'control', to: '/control/status', label: 'Kontrolle', mode: 'control' },
+]
+
+const MATURITY_PROCESS = [
+ { key: 'gates', to: '/plan/gates', label: 'Stufen', mode: 'plan' },
+ { key: 'journey', to: '/control/journey', label: 'Rhythmen', mode: 'control' },
+ { key: 'work', to: '/work/today', label: 'Ausführen', mode: 'work' },
+ { key: 'control', to: '/control/status', label: 'Kontrolle', mode: 'control' },
+]
+
+const PROGRAM_PROCESS = [
+ { key: 'gates', to: '/plan/gates', label: 'Phasen', mode: 'plan' },
+ { key: 'inbox', to: '/plan/inbox', label: 'Eingang', mode: 'plan' },
+ { key: 'work', to: '/work/today', label: 'Ausführen', mode: 'work' },
+ { key: 'control', to: '/control/status', label: 'Kontrolle', mode: 'control' },
+]
+
+const ARCHETYPE_UI = {
+ 'initiative.product': {
+ processSteps: PRODUCT_PROCESS,
+ planDefaultRoute: '/plan/inbox',
+ workDefaultRoute: '/work/sprint',
+ controlDefaultRoute: '/control/status',
+ planOutlineKeys: ['profile', 'inbox', 'sprint', 'gates'],
+ workNavKeys: ['sprint', 'today', 'mine'],
+ },
+ 'initiative.linear_project': {
+ processSteps: LINEAR_PROCESS,
+ planDefaultRoute: '/plan/gates',
+ workDefaultRoute: '/work/today',
+ controlDefaultRoute: '/control/status',
+ planOutlineKeys: ['profile', 'structure', 'gates', 'inbox', 'work'],
+ workNavKeys: ['today', 'mine', 'sprint'],
+ },
+ 'initiative.maturity_journey': {
+ processSteps: MATURITY_PROCESS,
+ planDefaultRoute: '/plan/gates',
+ workDefaultRoute: '/work/today',
+ controlDefaultRoute: '/control/status',
+ planOutlineKeys: ['profile', 'gates', 'inbox', 'work'],
+ workNavKeys: ['today', 'mine', 'sprint'],
+ },
+ 'initiative.program': {
+ processSteps: PROGRAM_PROCESS,
+ planDefaultRoute: '/plan/gates',
+ workDefaultRoute: '/work/today',
+ controlDefaultRoute: '/control/status',
+ planOutlineKeys: ['profile', 'structure', 'gates', 'inbox', 'sprint', 'work'],
+ workNavKeys: ['today', 'mine', 'sprint'],
+ },
+}
+
+const FALLBACK_DEFAULTS = {
+ processSteps: [],
+ planDefaultRoute: '/plan/profile',
+ workDefaultRoute: '/work/today',
+ controlDefaultRoute: '/control/status',
+ planOutlineKeys: null,
+ workNavKeys: null,
+}
+
+/**
+ * @param {MethodUiInput} input
+ * @returns {MethodUiDefaults}
+ */
+export function resolveMethodUiDefaults(input = {}) {
+ const archetypeKey = input.archetypeKey || null
+ const base = ARCHETYPE_UI[archetypeKey] || FALLBACK_DEFAULTS
+
+ let workDefaultRoute = base.workDefaultRoute
+ if (archetypeKey === 'initiative.product' && !input.hasActiveSprint) {
+ workDefaultRoute = '/work/today'
+ }
+
+ return {
+ processSteps: base.processSteps,
+ planDefaultRoute: base.planDefaultRoute,
+ workDefaultRoute,
+ controlDefaultRoute: base.controlDefaultRoute,
+ planOutlineKeys: base.planOutlineKeys,
+ workNavKeys: base.workNavKeys,
+ }
+}
+
+/**
+ * @param {MethodUiInput} input
+ * @returns {ProcessStep[]}
+ */
+export function resolveProcessSteps(input = {}) {
+ return resolveMethodUiDefaults(input).processSteps
+}
+
+/**
+ * @param {MethodUiInput} input
+ * @returns {typeof PLAN_OUTLINE_NODES}
+ */
+export function resolvePlanOutlineNodes(input = {}) {
+ const { planOutlineKeys } = resolveMethodUiDefaults(input)
+ if (!planOutlineKeys) return PLAN_OUTLINE_NODES
+ const allowed = new Set(planOutlineKeys)
+ return PLAN_OUTLINE_NODES.filter((node) => allowed.has(node.key))
+}
+
+/**
+ * @param {MethodUiInput} input
+ * @returns {typeof WORK_NAV_ITEMS}
+ */
+export function resolveWorkNavItems(input = {}) {
+ const { workNavKeys } = resolveMethodUiDefaults(input)
+ if (!workNavKeys) return WORK_NAV_ITEMS
+ const order = new Map(workNavKeys.map((key, index) => [key, index]))
+ return WORK_NAV_ITEMS.filter((item) => order.has(item.key)).sort(
+ (a, b) => (order.get(a.key) ?? 0) - (order.get(b.key) ?? 0),
+ )
+}
+
+/**
+ * Aktiven Prozessschritt aus Modus + Pfad ableiten.
+ * @param {string} pathname
+ * @param {ProcessStep[]} steps
+ */
+export function resolveActiveProcessStepKey(pathname, steps) {
+ const path = (pathname || '').replace(/\/$/, '') || pathname
+ if (!steps.length) return null
+
+ if (path.startsWith('/control/')) {
+ const journey = steps.find((s) => s.key === 'journey')
+ if (journey && path.startsWith('/control/journey')) return 'journey'
+ return steps.find((s) => s.mode === 'control')?.key ?? null
+ }
+ if (path.startsWith('/work/')) {
+ if (path.startsWith('/work/sprint')) {
+ return steps.find((s) => s.key === 'sprint-work')?.key ?? null
+ }
+ return steps.find((s) => s.key === 'work')?.key ?? null
+ }
+ if (path.startsWith('/plan/')) {
+ if (path.startsWith('/plan/inbox')) return 'inbox'
+ if (path.startsWith('/plan/sprint')) return 'sprint-plan'
+ if (path.startsWith('/plan/gates')) return 'gates'
+ }
+ if (path.startsWith('/actions/')) {
+ return steps.find((s) => s.mode === 'work')?.key ?? null
+ }
+
+ return null
+}
diff --git a/frontend/src/config/methodUiDefaults.test.js b/frontend/src/config/methodUiDefaults.test.js
new file mode 100644
index 0000000..cf26d43
--- /dev/null
+++ b/frontend/src/config/methodUiDefaults.test.js
@@ -0,0 +1,68 @@
+import { describe, expect, it } from 'vitest'
+import {
+ resolveActiveProcessStepKey,
+ resolveMethodUiDefaults,
+ resolvePlanOutlineNodes,
+ resolveProcessSteps,
+ resolveWorkNavItems,
+} from './methodUiDefaults.js'
+
+describe('methodUiDefaults', () => {
+ it('product archetype defaults to Eingang and sprint-centric work nav', () => {
+ const ui = resolveMethodUiDefaults({
+ archetypeKey: 'initiative.product',
+ hasActiveSprint: true,
+ })
+ expect(ui.planDefaultRoute).toBe('/plan/inbox')
+ expect(ui.workDefaultRoute).toBe('/work/sprint')
+ expect(ui.planOutlineKeys).toEqual(['profile', 'inbox', 'sprint', 'gates'])
+ expect(resolveWorkNavItems({ archetypeKey: 'initiative.product' }).map((i) => i.key)).toEqual([
+ 'sprint',
+ 'today',
+ 'mine',
+ ])
+ })
+
+ it('product without active sprint falls back work default to today', () => {
+ const ui = resolveMethodUiDefaults({
+ archetypeKey: 'initiative.product',
+ hasActiveSprint: false,
+ })
+ expect(ui.workDefaultRoute).toBe('/work/today')
+ })
+
+ it('exposes product process bar steps', () => {
+ const steps = resolveProcessSteps({ archetypeKey: 'initiative.product' })
+ expect(steps.map((s) => s.label)).toEqual([
+ 'Eingang',
+ 'Sprint planen',
+ 'Ausführen',
+ 'Kontrolle',
+ ])
+ })
+
+ it('linear archetype defaults to gates and filters plan outline', () => {
+ const ui = resolveMethodUiDefaults({ archetypeKey: 'initiative.linear_project' })
+ expect(ui.planDefaultRoute).toBe('/plan/gates')
+ expect(resolvePlanOutlineNodes({ archetypeKey: 'initiative.linear_project' }).map((n) => n.key)).toEqual([
+ 'profile',
+ 'structure',
+ 'gates',
+ 'inbox',
+ 'work',
+ ])
+ })
+
+ it('generic archetype keeps full plan outline and no process steps', () => {
+ expect(resolveMethodUiDefaults({}).processSteps).toEqual([])
+ expect(resolvePlanOutlineNodes({}).length).toBe(6)
+ })
+
+ it('resolves active process step from pathname', () => {
+ const steps = resolveProcessSteps({ archetypeKey: 'initiative.product' })
+ expect(resolveActiveProcessStepKey('/plan/inbox', steps)).toBe('inbox')
+ expect(resolveActiveProcessStepKey('/plan/sprint', steps)).toBe('sprint-plan')
+ expect(resolveActiveProcessStepKey('/work/sprint', steps)).toBe('sprint-work')
+ expect(resolveActiveProcessStepKey('/control/status', steps)).toBe('control')
+ })
+})
diff --git a/frontend/src/constants/operating.js b/frontend/src/constants/operating.js
index 878192f..66410d4 100644
--- a/frontend/src/constants/operating.js
+++ b/frontend/src/constants/operating.js
@@ -39,23 +39,23 @@ export const PHASE_LABELS = {
export const PHASE_DESCRIPTIONS = {
capture: 'Ideen und Eingänge erfassen — noch wenig strukturierte Arbeit.',
- triage: 'Backlog wartet auf Commit — noch keine offene nächste Maßnahme.',
- structure: 'Vorhaben braucht Struktur — keine offene Maßnahme, Backlog leer.',
- commit: 'Maßnahmen committen und zuweisen.',
- execute: 'Aktive Umsetzung — offene Maßnahmen oder Blocker bearbeiten.',
+ triage: 'Backlog wartet auf Commit — noch kein nächster Schritt.',
+ structure: 'Vorhaben braucht Struktur — kein offenes Arbeitspaket, Backlog leer.',
+ commit: 'Arbeitspakete committen und zuweisen.',
+ execute: 'Aktive Umsetzung — offene Arbeitspakete oder Blocker bearbeiten.',
verify: 'Nachweise und Ergebnisse belegen.',
- review: 'Reviews oder Maßnahmen mit Review-Status klären.',
+ review: 'Reviews oder Arbeitspakete mit Review-Status klären.',
adapt: 'Anpassen — Backlog, Meilensteine oder Prioritäten prüfen.',
closure: 'Vorhaben abgeschlossen oder archiviert.',
}
export const SIGNAL_LABELS = {
initiative_terminal: 'Vorhaben abgeschlossen',
- backlog_awaiting_commit: 'Backlog wartet auf Maßnahme',
+ backlog_awaiting_commit: 'Backlog wartet auf Commit',
no_open_work: 'Keine offene Arbeit',
- actions_blocked: 'Maßnahmen blockiert',
+ actions_blocked: 'Arbeitspakete blockiert',
open_blockers: 'Offene Blocker',
- actions_need_review: 'Maßnahmen brauchen Review',
+ actions_need_review: 'Arbeitspakete brauchen Review',
reviews_due: 'Reviews fällig',
work_in_progress: 'Arbeit in Umsetzung',
}
@@ -63,19 +63,19 @@ export const SIGNAL_LABELS = {
export const NEXT_ACTION_KIND_LABELS = {
action: 'Arbeitspaket',
resolve_blocker: 'Blocker klären',
- assign_action: 'Maßnahme zuweisen',
+ assign_action: 'Arbeitspaket zuweisen',
convert_backlog: 'Backlog umwandeln',
- create_action: 'Maßnahme anlegen',
+ create_action: 'Arbeitspaket anlegen',
review_milestone: 'Meilenstein prüfen',
recurring_due: 'Rhythmus fällig',
}
export const ATTENTION_KIND_LABELS = {
- blocked_action: 'Blockierte Maßnahme',
+ blocked_action: 'Blockiertes Arbeitspaket',
open_blocker: 'Offener Blocker',
high_priority_action: 'High Priority',
unassigned_action: 'Ohne Zuweisung',
- initiative_without_next_action: 'Keine nächste Maßnahme',
+ initiative_without_next_action: 'Kein nächster Schritt',
stale_initiative: 'Inaktives Vorhaben',
milestone_at_risk: 'Meilenstein gefährdet',
gate_graph_blocked: 'Gate blockiert (Graph)',
diff --git a/frontend/src/hooks/useMethodUiContext.js b/frontend/src/hooks/useMethodUiContext.js
new file mode 100644
index 0000000..2b8fb7a
--- /dev/null
+++ b/frontend/src/hooks/useMethodUiContext.js
@@ -0,0 +1,71 @@
+import { useEffect, useMemo, useState } from 'react'
+import { getInitiative } from '../api/initiatives.js'
+import { getActiveWorkCycle } from '../api/workCycles.js'
+import { getInitiativeSteeringSnapshot } from '../api/initiatives.js'
+import { useProgramScope } from '../context/ProgramScopeContext.jsx'
+import { resolveMethodUiDefaults } from '../config/methodUiDefaults.js'
+
+/**
+ * Lädt Archetyp/Methode für methodengebundene UI (AP1.9d).
+ */
+export function useMethodUiContext() {
+ const { initiativeId } = useProgramScope()
+ const [archetypeKey, setArchetypeKey] = useState(null)
+ const [methodKey, setMethodKey] = useState(null)
+ const [hasActiveSprint, setHasActiveSprint] = useState(false)
+ const [loading, setLoading] = useState(false)
+
+ useEffect(() => {
+ if (!initiativeId) {
+ setArchetypeKey(null)
+ setMethodKey(null)
+ setHasActiveSprint(false)
+ return undefined
+ }
+
+ let cancelled = false
+ setLoading(true)
+
+ Promise.all([
+ getInitiative(initiativeId),
+ getActiveWorkCycle(initiativeId).catch(() => null),
+ getInitiativeSteeringSnapshot(initiativeId).catch(() => null),
+ ])
+ .then(([initiative, activeCycle, snapshot]) => {
+ if (cancelled) return
+ setArchetypeKey(initiative?.archetype_key || null)
+ setMethodKey(snapshot?.method_key || null)
+ setHasActiveSprint(Boolean(activeCycle?.id))
+ })
+ .catch(() => {
+ if (!cancelled) {
+ setArchetypeKey(null)
+ setMethodKey(null)
+ setHasActiveSprint(false)
+ }
+ })
+ .finally(() => {
+ if (!cancelled) setLoading(false)
+ })
+
+ return () => {
+ cancelled = true
+ }
+ }, [initiativeId])
+
+ const input = useMemo(
+ () => ({ archetypeKey, methodKey, hasActiveSprint }),
+ [archetypeKey, methodKey, hasActiveSprint],
+ )
+
+ const defaults = useMemo(() => resolveMethodUiDefaults(input), [input])
+
+ return {
+ initiativeId,
+ archetypeKey,
+ methodKey,
+ hasActiveSprint,
+ loading,
+ ...defaults,
+ }
+}
diff --git a/frontend/src/layout/AppLayout.jsx b/frontend/src/layout/AppLayout.jsx
index 00c04ee..f6372d8 100644
--- a/frontend/src/layout/AppLayout.jsx
+++ b/frontend/src/layout/AppLayout.jsx
@@ -6,6 +6,7 @@ import { DesktopSidebar } from '../components/DesktopSidebar.jsx'
import { BottomNav } from '../components/BottomNav.jsx'
import { ContextIndicator } from '../components/ContextIndicator.jsx'
import { ProgramScopeBar } from '../components/ProgramScopeBar.jsx'
+import { MethodProcessBar } from '../components/MethodProcessBar.jsx'
import { QuickJump } from '../components/QuickJump.jsx'
import { RecentlyOpened } from '../components/RecentlyOpened.jsx'
@@ -43,10 +44,12 @@ export function AppLayout() {
+
diff --git a/frontend/src/pages/modes/PlanIndexRedirect.jsx b/frontend/src/pages/modes/PlanIndexRedirect.jsx
index 3a6d855..4e9e2f7 100644
--- a/frontend/src/pages/modes/PlanIndexRedirect.jsx
+++ b/frontend/src/pages/modes/PlanIndexRedirect.jsx
@@ -1,13 +1,48 @@
import { Navigate } from 'react-router-dom'
+import { useEffect, useState } from 'react'
import { useProgramScope } from '../../context/ProgramScopeContext.jsx'
+import { getInitiative } from '../../api/initiatives.js'
+import { getActiveWorkCycle } from '../../api/workCycles.js'
+import { resolveMethodUiDefaults } from '../../config/methodUiDefaults.js'
-/** Portfolio ohne Scope → Übersicht; mit Vorhaben → Profil als Einstieg. */
+/** Portfolio ohne Scope → Übersicht; mit Vorhaben → methoden-Default (AP1.9d). */
export function PlanIndexRedirect() {
const { initiativeId, hrefWithScope } = useProgramScope()
+ const [target, setTarget] = useState(null)
- if (initiativeId) {
- return
+ useEffect(() => {
+ if (!initiativeId) {
+ setTarget('/plan/portfolio')
+ return undefined
+ }
+ let cancelled = false
+ Promise.all([
+ getInitiative(initiativeId),
+ getActiveWorkCycle(initiativeId).catch(() => null),
+ ])
+ .then(([initiative, activeCycle]) => {
+ if (cancelled) return
+ const defaults = resolveMethodUiDefaults({
+ archetypeKey: initiative?.archetype_key,
+ hasActiveSprint: Boolean(activeCycle?.id),
+ })
+ setTarget(hrefWithScope(defaults.planDefaultRoute))
+ })
+ .catch(() => {
+ if (!cancelled) setTarget(hrefWithScope('/plan/profile'))
+ })
+ return () => {
+ cancelled = true
+ }
+ }, [initiativeId, hrefWithScope])
+
+ if (!initiativeId) {
+ return
}
- return
+ if (!target) {
+ return null
+ }
+
+ return
}
diff --git a/frontend/src/pages/modes/PlanSprintPage.jsx b/frontend/src/pages/modes/PlanSprintPage.jsx
index 7d1c9a9..0c8e404 100644
--- a/frontend/src/pages/modes/PlanSprintPage.jsx
+++ b/frontend/src/pages/modes/PlanSprintPage.jsx
@@ -93,7 +93,7 @@ function PlanSprintInner() {
workCycles={workCycles}
activeWorkCycle={activeWorkCycle}
sectionTitle="Sprint-Backlog"
- sectionLead="Arbeitspakete in der aktiven Zeitbox — committet aus dem Product Backlog (Eingang) oder direkt hier."
+ sectionLead="Arbeitspakete im aktiven Sprint — committet aus dem Product Backlog (Eingang) oder direkt hier."
/>
)}
>
@@ -102,7 +102,7 @@ function PlanSprintInner() {
export function PlanSprintPage() {
return (
-
+
diff --git a/frontend/src/pages/modes/WorkLayout.jsx b/frontend/src/pages/modes/WorkLayout.jsx
index d81ccb5..4a005c2 100644
--- a/frontend/src/pages/modes/WorkLayout.jsx
+++ b/frontend/src/pages/modes/WorkLayout.jsx
@@ -4,23 +4,57 @@ import { ModeAreaShell } from '../../components/ModeAreaShell.jsx'
import { ModeAreaNav } from '../../components/ModeAreaNav.jsx'
import { ModeShell } from '../../components/ModeShell.jsx'
import { WORK_NAV_ITEMS, resolveWorkNavActiveKey } from '../../config/workNav.js'
+import { resolveWorkNavItems } from '../../config/methodUiDefaults.js'
import { useProgramScope } from '../../context/ProgramScopeContext.jsx'
-import { getActiveWorkCycle } from '../../api/workCycles.js'
import { getInitiative } from '../../api/initiatives.js'
+import { getActiveWorkCycle } from '../../api/workCycles.js'
+import { resolveMethodUiDefaults } from '../../config/methodUiDefaults.js'
+
+function WorkAreaNav() {
+ const { initiativeId } = useProgramScope()
+ const [navItems, setNavItems] = useState(WORK_NAV_ITEMS)
+
+ useEffect(() => {
+ if (!initiativeId) {
+ setNavItems(WORK_NAV_ITEMS)
+ return undefined
+ }
+ let cancelled = false
+ Promise.all([
+ getInitiative(initiativeId),
+ getActiveWorkCycle(initiativeId).catch(() => null),
+ ])
+ .then(([initiative, activeCycle]) => {
+ if (cancelled) return
+ setNavItems(
+ resolveWorkNavItems({
+ archetypeKey: initiative?.archetype_key,
+ hasActiveSprint: Boolean(activeCycle?.id),
+ }),
+ )
+ })
+ .catch(() => {
+ if (!cancelled) setNavItems(WORK_NAV_ITEMS)
+ })
+ return () => {
+ cancelled = true
+ }
+ }, [initiativeId])
+
+ return (
+
+ )
+}
export function WorkLayout() {
return (
-
- }
- >
+ }>
@@ -28,7 +62,7 @@ export function WorkLayout() {
}
export function WorkIndexRedirect() {
- const { initiativeId } = useProgramScope()
+ const { initiativeId, hrefWithScope } = useProgramScope()
const [target, setTarget] = useState('/work/today')
useEffect(() => {
@@ -37,25 +71,25 @@ export function WorkIndexRedirect() {
return undefined
}
let cancelled = false
- Promise.all([getInitiative(initiativeId), getActiveWorkCycle(initiativeId)])
+ Promise.all([
+ getInitiative(initiativeId),
+ getActiveWorkCycle(initiativeId).catch(() => null),
+ ])
.then(([initiative, activeCycle]) => {
if (cancelled) return
- if (
- initiative?.archetype_key === 'initiative.product' &&
- activeCycle?.id
- ) {
- setTarget(`/work/sprint?initiative=${initiativeId}`)
- } else {
- setTarget(`/work/today?initiative=${initiativeId}`)
- }
+ const defaults = resolveMethodUiDefaults({
+ archetypeKey: initiative?.archetype_key,
+ hasActiveSprint: Boolean(activeCycle?.id),
+ })
+ setTarget(hrefWithScope(defaults.workDefaultRoute))
})
.catch(() => {
- if (!cancelled) setTarget('/work/today')
+ if (!cancelled) setTarget(hrefWithScope('/work/today'))
})
return () => {
cancelled = true
}
- }, [initiativeId])
+ }, [initiativeId, hrefWithScope])
return
}
diff --git a/frontend/src/pages/modes/WorkSprintPage.jsx b/frontend/src/pages/modes/WorkSprintPage.jsx
index e1a739c..0e613ad 100644
--- a/frontend/src/pages/modes/WorkSprintPage.jsx
+++ b/frontend/src/pages/modes/WorkSprintPage.jsx
@@ -68,7 +68,7 @@ function WorkSprintInner() {
{!activeWorkCycle && isProductArchetype && (
diff --git a/frontend/src/styles/program-chrome.css b/frontend/src/styles/program-chrome.css
index d1bd38f..59ea586 100644
--- a/frontend/src/styles/program-chrome.css
+++ b/frontend/src/styles/program-chrome.css
@@ -607,3 +607,65 @@
margin: 0 0 12px;
font-size: 13px;
}
+
+/* AP1.9d — methodengebundene Prozessleiste */
+.method-process-bar {
+ width: 100%;
+ padding: 0 16px 10px;
+ overflow-x: auto;
+}
+
+@media (min-width: 1024px) {
+ .method-process-bar {
+ width: auto;
+ flex: 1 1 100%;
+ padding: 0;
+ }
+}
+
+.method-process-bar__steps {
+ display: flex;
+ align-items: center;
+ flex-wrap: nowrap;
+ gap: 4px;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ font-size: 13px;
+}
+
+.method-process-bar__step {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ white-space: nowrap;
+}
+
+.method-process-bar__sep {
+ color: var(--jk-text-subtle);
+ font-size: 12px;
+ margin: 0 2px;
+}
+
+.method-process-bar__link {
+ display: inline-block;
+ padding: 4px 10px;
+ border-radius: 999px;
+ color: var(--jk-text-muted);
+ text-decoration: none;
+ font-weight: 500;
+ border: 1px solid transparent;
+}
+
+.method-process-bar__link:hover {
+ color: var(--jk-primary);
+ background: var(--jk-surface-muted, rgba(0, 0, 0, 0.04));
+}
+
+.method-process-bar__link--active,
+.method-process-bar__step--active .method-process-bar__link {
+ color: var(--jk-primary);
+ font-weight: 600;
+ border-color: var(--jk-primary);
+ background: var(--jk-surface);
+}
diff --git a/frontend/src/utils/scopeNavigation.js b/frontend/src/utils/scopeNavigation.js
index e2822a8..fa92a17 100644
--- a/frontend/src/utils/scopeNavigation.js
+++ b/frontend/src/utils/scopeNavigation.js
@@ -6,7 +6,7 @@
const MODE_DEFAULTS = {
cockpit: { portfolioHub: '/cockpit', initiativeHub: '/cockpit' },
work: { portfolioHub: '/work/today', initiativeHub: '/work/today' },
- plan: { portfolioHub: '/plan/portfolio', initiativeHub: '/plan/profile' },
+ plan: { portfolioHub: '/plan/portfolio', initiativeHub: '/plan/inbox' },
control: { portfolioHub: '/control/status', initiativeHub: '/control/status' },
team: { portfolioHub: '/team', initiativeHub: '/team' },
}