docs: Archetyp-gesteuerte UI-Navigation verankern
Some checks failed
Deploy Development / deploy (push) Successful in 50s
Test Suite / pytest-backend (push) Failing after 3m50s
Test Suite / k6 /api/health Baseline (push) Has been skipped
Test Suite / playwright-smoke (push) Has been skipped
Test Suite / lint-backend (push) Successful in 2s
Test Suite / compose-smoke (push) Has been skipped
Some checks failed
Deploy Development / deploy (push) Successful in 50s
Test Suite / pytest-backend (push) Failing after 3m50s
Test Suite / k6 /api/health Baseline (push) Has been skipped
Test Suite / playwright-smoke (push) Has been skipped
Test Suite / lint-backend (push) Successful in 2s
Test Suite / compose-smoke (push) Has been skipped
ARCHETYPE_UI_NAVIGATION_v0.1 plus Verweise in Spec-Template, README und Programm-Dokument.
This commit is contained in:
parent
11b7128204
commit
97fab2c76d
|
|
@ -41,6 +41,7 @@ Kairo_Implementation_Truth_Table_v0.1.md ← Was ist wirklich da?
|
||||||
|---------|----------|--------|
|
|---------|----------|--------|
|
||||||
| Katalog | ADP v0.2 §3–§5 | Inventar, Nutzerbild, Scope Lock |
|
| Katalog | ADP v0.2 §3–§5 | Inventar, Nutzerbild, Scope Lock |
|
||||||
| Plugin | ADP AP2.3 / AP2.4 | Archetyp ↔ Methode, Element-Registry, Operating Context |
|
| Plugin | ADP AP2.3 / AP2.4 | Archetyp ↔ Methode, Element-Registry, Operating Context |
|
||||||
|
| UI-Navigation | `docs/product/archetypes/ARCHETYPE_UI_NAVIGATION_v0.1.md` | Nav, Outline, Default-Routen, Slice-Gating — Spec-§6 |
|
||||||
| Spec | `docs/product/archetypes/` | Implementierbare Detail-Spec |
|
| Spec | `docs/product/archetypes/` | Implementierbare Detail-Spec |
|
||||||
| Auftrag | Sprint Assignments | Scope + DoD für ein AP |
|
| Auftrag | Sprint Assignments | Scope + DoD für ein AP |
|
||||||
| Ist | Truth Table | Living Check |
|
| Ist | Truth Table | Living Check |
|
||||||
|
|
|
||||||
143
docs/product/archetypes/ARCHETYPE_UI_NAVIGATION_v0.1.md
Normal file
143
docs/product/archetypes/ARCHETYPE_UI_NAVIGATION_v0.1.md
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
# Archetyp-gesteuerte UI-Navigation v0.1
|
||||||
|
|
||||||
|
**Status:** verbindlich für Specs und Implementierung
|
||||||
|
**Stand:** 2026-07-26
|
||||||
|
**Technische Guardrails:** `.cursor/rules/kairo-plugin-architecture.mdc`
|
||||||
|
**Code-Seeds:** `backend/entity_archetypes/ui_profiles.py` → `ui_profile_json` (DB)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Leitprinzip
|
||||||
|
|
||||||
|
**Jeder Vorhabentyp (Archetyp) definiert seine eigene Navigations-Hülle** — welche Modi, Outline-Punkte, Default-Routen und Prozessschritte der Nutzer sieht.
|
||||||
|
|
||||||
|
Die **Steuerungsmethode** schärft danach, welche OM-Daten und UI-Bausteine *tatsächlich* aktiv sind — nicht umgekehrt.
|
||||||
|
|
||||||
|
```text
|
||||||
|
Sichtbarkeit (Nav, Outline) → Archetyp ui_profile
|
||||||
|
Erreichbarkeit (Route Gating) → data_slices = om_capabilities ∩ method.data_slices
|
||||||
|
Verhalten (Panels, Strategien) → method.ui_features + steering_elements
|
||||||
|
```
|
||||||
|
|
||||||
|
**Keine Archetyp-Ifs in React-Pages.** Specs beschreiben `ui_profile`-Felder; Code liest `GET /api/initiatives/:id/operating-context`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Schichten zur Laufzeit
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
A[Archetyp ui_profile] --> OC[Operating Context API]
|
||||||
|
M[Methode MethodDefinition] --> OC
|
||||||
|
OC --> R[resolveOperatingProfile.js]
|
||||||
|
R --> NAV[PlanOutlineNav / WorkNav / ProcessStrip]
|
||||||
|
R --> GATE[OperatingRouteGate]
|
||||||
|
R --> OPS[InitiativeOperationsContext / loadOperatingSlices]
|
||||||
|
M --> STEER[steering/kernel evaluate_steering]
|
||||||
|
```
|
||||||
|
|
||||||
|
| Schicht | Quelle | Steuert |
|
||||||
|
|---------|--------|---------|
|
||||||
|
| **Archetyp** | `ui_profile_json` | Plan-Outline, Work-Nav, Prozessleiste, Default-Routen, `dataSlices` / `om_capabilities` |
|
||||||
|
| **Methode** | `steering/methods/registrations/` | Effektive `data_slices`, `ui_features`, `steering_elements`, Graph-Profil, Next-Action-Strategie |
|
||||||
|
| **Runtime** | Operating Context + Steering Kernel | Aufgelöstes Profil pro Initiative |
|
||||||
|
|
||||||
|
Archetyp-Default-Methode ist **Empfehlung** bei Anlage — kompatible Methoden werden über `compatible_archetype_keys` und `om_capabilities` gefiltert.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. `ui_profile`-Felder (Archetyp-Spec §6)
|
||||||
|
|
||||||
|
Diese Felder gehören in jede Archetyp-Spec und in `INITIATIVE_UI_PROFILES` / DB-Seed:
|
||||||
|
|
||||||
|
| Feld | Wirkung | Beispiel Product |
|
||||||
|
|------|---------|------------------|
|
||||||
|
| `planOutlineKeys` | Sichtbare Plan-Sidebar-Knoten (Reihenfolge = Array-Reihenfolge) | `profile`, `inbox`, `sprint`, `gates` |
|
||||||
|
| `workNavKeys` | Ausführen-Navigation | `sprint`, `today`, `mine` |
|
||||||
|
| `processSteps` | Prozessleiste (Quer-Navigation zwischen Modi) | Eingang → Sprint planen → Ausführen → Kontrolle |
|
||||||
|
| `planDefaultRoute` | Redirect bei `/plan` mit Vorhaben-Scope | `/plan/inbox` |
|
||||||
|
| `workDefaultRoute` | Redirect bei `/work` mit Scope | `/work/sprint` |
|
||||||
|
| `workDefaultRouteWithoutActiveWorkCycle` | Work-Default ohne aktiven Sprint | `/work/today` |
|
||||||
|
| `controlDefaultRoute` | Redirect bei `/control` mit Scope | `/control/status` |
|
||||||
|
| `dataSlices` | OM-Fähigkeiten des Archetyps (Superset) | `backlog`, `roadmap`, `work_cycles`, … |
|
||||||
|
| `enabledModes` | Optionale Modus-Einschränkung | `plan`, `work`, `control` |
|
||||||
|
|
||||||
|
**Plan-Outline-Keys** (Referenz): `profile`, `structure`, `gates`, `inbox`, `sprint`, `work` — siehe `frontend/src/plan/planOutlineNodes.js`.
|
||||||
|
|
||||||
|
**Nicht am Archetyp:** `ui_features` und `steering_elements` — ausschließlich Methoden-Vertrag (AP2.4).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Route Gating und Slice-Loader
|
||||||
|
|
||||||
|
Modus-Routen deklarieren `requiredSlices` in `frontend/src/registry/viewRegistry.js` (`MODE_ROUTE_GROUPS`):
|
||||||
|
|
||||||
|
| Route | `requiredSlices` |
|
||||||
|
|-------|------------------|
|
||||||
|
| `/plan/inbox` | `backlog` |
|
||||||
|
| `/plan/gates` | `roadmap` |
|
||||||
|
| `/plan/sprint` | `work_cycles` |
|
||||||
|
| `/plan/structure` | `projects` |
|
||||||
|
| `/work/today`, `/work/mine` | `actions` |
|
||||||
|
|
||||||
|
`OperatingRouteGate` erlaubt eine Route nur, wenn alle `requiredSlices` in den **effektiven** `data_slices` enthalten sind.
|
||||||
|
|
||||||
|
Effektive Slices (Backend):
|
||||||
|
|
||||||
|
```text
|
||||||
|
data_slices = om_capabilities ∩ method.data_slices
|
||||||
|
om_capabilities = ui_profile.omCapabilities || ui_profile.dataSlices
|
||||||
|
```
|
||||||
|
|
||||||
|
Frontend-Fallback (2026-07-26): Ist `data_slices` leer, nutzt `resolveEffectiveDataSlices()` die `ui_profile.dataSlices` — Nav und Gating dürfen nicht auseinanderlaufen.
|
||||||
|
|
||||||
|
Datenladen: `InitiativeOperationsContext` lädt OM-Slices anhand derselben effektiven Liste (`loadOperatingSlices`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Was Spec-Autoren liefern
|
||||||
|
|
||||||
|
In **§6 UI / IA** jeder Archetyp-Spec:
|
||||||
|
|
||||||
|
1. **Plan-Outline** — welche `planOutlineKeys`, in welcher Reihenfolge; was bewusst fehlt (z. B. Dispute Case ohne Gates)
|
||||||
|
2. **Default-Routen** — wohin bei Scope-Wechsel und bei blockierter Route
|
||||||
|
3. **Prozessleiste** — `processSteps` mit Label und Ziel-Route
|
||||||
|
4. **OM-Slices** — vollständige `dataSlices`-Liste (muss zu Plan-/Work-Routen passen)
|
||||||
|
5. **Abgrenzung Methode** — welche `ui_features` / `steering_elements` die Default-Methode liefert (Verweis auf Methoden-Spec)
|
||||||
|
|
||||||
|
**Anti-Pattern in Specs:** „Seite X nur für Archetyp Y anzeigen“ ohne `ui_profile`-Feld — stattdessen Outline-Key weglassen oder Route über Slice-Gating absichern.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Erweiterung (Open/Closed)
|
||||||
|
|
||||||
|
| Änderung | Wo | Nicht |
|
||||||
|
|----------|-----|-------|
|
||||||
|
| Neuer Archetyp | `ui_profiles.py` + Registry-Seed + Spec §6 | React-If in Pages |
|
||||||
|
| Andere Nav für bestehenden Archetyp | `ui_profile_json` (DB-Sync) + Spec-Update | Hardcode in `PlanOutlineNav` |
|
||||||
|
| Neues UI-Panel / Steuerungsbaustein | Methoden-`steering_elements` + Element-Registry | Archetyp-Profil |
|
||||||
|
| Neue Plan-Route | `MODE_ROUTE_GROUPS` + `requiredSlices` + ggf. Outline-Node | Ad-hoc-Redirect in Page |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Abnahme-Checkliste (Archetyp-Slice)
|
||||||
|
|
||||||
|
- [ ] Operating Context liefert `ui_profile` mit Spec-konformen Keys und Routen
|
||||||
|
- [ ] `data_slices` enthält alle für den Archetyp deklarierten Slices, die die Methode unterstützt
|
||||||
|
- [ ] Plan-Outline zeigt nur Spec-Keys; Klick führt nicht zu Profil-Redirect
|
||||||
|
- [ ] Default-Routen (`/plan`, `/work`, `/control` mit Scope) entsprechen Spec
|
||||||
|
- [ ] Keine `archetype_key`-Ifs in betroffenen Pages
|
||||||
|
- [ ] Spec §11 (Ist-Stand) verweist auf Operating Context / Route Gating
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Referenzen
|
||||||
|
|
||||||
|
| Artefakt | Pfad |
|
||||||
|
|----------|------|
|
||||||
|
| UI-Profile Seeds | `backend/entity_archetypes/ui_profiles.py` |
|
||||||
|
| Operating Context | `backend/services/operating_context.py` |
|
||||||
|
| FE Resolver | `frontend/src/registry/resolveOperatingProfile.js` |
|
||||||
|
| Route Gating | `frontend/src/registry/OperatingRouteGate.jsx`, `modeRouteRegistry.js` |
|
||||||
|
| Plugin-ADP | `docs/architecture/ADP_Archetype_Method_Plugin_Architecture_v0.1.md` |
|
||||||
|
| Spec-Template §6 | `docs/product/archetypes/_TEMPLATE_Archetype_Spec_v0.1.md` |
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
**Decision-Lock:** [`../Kairo_Archetype_Decision_Lock_Interview_2026-07-25.md`](../Kairo_Archetype_Decision_Lock_Interview_2026-07-25.md)
|
**Decision-Lock:** [`../Kairo_Archetype_Decision_Lock_Interview_2026-07-25.md`](../Kairo_Archetype_Decision_Lock_Interview_2026-07-25.md)
|
||||||
**Methodenkern:** [`../../architecture/Kairo_Steering_Method_Kernel_v0.1.md`](../../architecture/Kairo_Steering_Method_Kernel_v0.1.md)
|
**Methodenkern:** [`../../architecture/Kairo_Steering_Method_Kernel_v0.1.md`](../../architecture/Kairo_Steering_Method_Kernel_v0.1.md)
|
||||||
**Methoden Spec-D:** [`../../architecture/methods/README.md`](../../architecture/methods/README.md)
|
**Methoden Spec-D:** [`../../architecture/methods/README.md`](../../architecture/methods/README.md)
|
||||||
|
**UI-Navigation (Archetyp):** [`ARCHETYPE_UI_NAVIGATION_v0.1.md`](ARCHETYPE_UI_NAVIGATION_v0.1.md)
|
||||||
|
|
||||||
**PO 2026-07-26:** Specs **vorläufig freigegeben** — Implementierungsausreichendheit noch nicht abschließend bewertet.
|
**PO 2026-07-26:** Specs **vorläufig freigegeben** — Implementierungsausreichendheit noch nicht abschließend bewertet.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,14 +99,19 @@ Filter: `compatible_archetype_keys` + `data_slices ⊆ om_capabilities` (siehe A
|
||||||
|
|
||||||
## 6. UI / IA
|
## 6. UI / IA
|
||||||
|
|
||||||
|
**Architektur:** [`ARCHETYPE_UI_NAVIGATION_v0.1.md`](ARCHETYPE_UI_NAVIGATION_v0.1.md) — Nav, Routen und Slice-Gating über `ui_profile` (Archetyp) + Operating Context. Keine Page-Ifs.
|
||||||
|
|
||||||
| Aspekt | Vorgabe |
|
| Aspekt | Vorgabe |
|
||||||
|--------|---------|
|
|--------|---------|
|
||||||
| Prozessleiste | aus `ui_profile` (Archetyp) |
|
| `planOutlineKeys` | Sichtbare Plan-Sidebar (Reihenfolge = Array) |
|
||||||
| Plan-Default | |
|
| `workNavKeys` | Ausführen-Navigation |
|
||||||
| Work-Default | |
|
| Prozessleiste | `processSteps` aus `ui_profile` (Archetyp) |
|
||||||
| Control-Default | |
|
| Plan-Default | `planDefaultRoute` |
|
||||||
|
| Work-Default | `workDefaultRoute` (+ ggf. `workDefaultRouteWithoutActiveWorkCycle`) |
|
||||||
|
| Control-Default | `controlDefaultRoute` |
|
||||||
|
| `dataSlices` | OM-Fähigkeiten — müssen zu Routen in §6 passen (`backlog` → Eingang, `roadmap` → Gates, …) |
|
||||||
| `ui_features` (Methode) | z. B. recurring_panel — **nicht** am Archetyp |
|
| `ui_features` (Methode) | z. B. recurring_panel — **nicht** am Archetyp |
|
||||||
| Versteckt / sekundär | |
|
| Versteckt / sekundär | Key in Outline weglassen, nicht per If verstecken |
|
||||||
|
|
||||||
Product Language: …
|
Product Language: …
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,61 +1,31 @@
|
||||||
import { useEffect, useMemo, useState } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { getInitiativeOperatingContext } from '../api/initiatives.js'
|
|
||||||
import { getActiveWorkCycle } from '../api/workCycles.js'
|
|
||||||
import { useProgramScope } from '../context/ProgramScopeContext.jsx'
|
import { useProgramScope } from '../context/ProgramScopeContext.jsx'
|
||||||
|
import { useOptionalInitiativeOperations } from '../context/InitiativeOperationsContext.jsx'
|
||||||
import { resolveOperatingProfileFromInput } from '../registry/resolveOperatingProfile.js'
|
import { resolveOperatingProfileFromInput } from '../registry/resolveOperatingProfile.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lädt Operating Context für methodengebundene UI (AP2.3b).
|
* Methoden-/Archetyp-UI aus dem gemeinsamen InitiativeOperationsProvider (AP2.3b).
|
||||||
|
* Kein separater Fetch pro Komponente — verhindert Route-Gate-Races beim Modus-Wechsel.
|
||||||
*/
|
*/
|
||||||
export function useMethodUiContext() {
|
export function useMethodUiContext() {
|
||||||
const { initiativeId } = useProgramScope()
|
const { initiativeId } = useProgramScope()
|
||||||
const [operatingContext, setOperatingContext] = useState(null)
|
const ops = useOptionalInitiativeOperations()
|
||||||
const [hasActiveSprint, setHasActiveSprint] = useState(false)
|
|
||||||
const [loading, setLoading] = useState(false)
|
|
||||||
|
|
||||||
useEffect(() => {
|
const operatingContext = ops?.operatingContext ?? null
|
||||||
if (!initiativeId) {
|
const hasActiveSprint = Boolean(ops?.activeWorkCycle?.id)
|
||||||
setOperatingContext(null)
|
const loading = Boolean(initiativeId && ops?.loading)
|
||||||
setHasActiveSprint(false)
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
let cancelled = false
|
|
||||||
setLoading(true)
|
|
||||||
|
|
||||||
Promise.all([
|
|
||||||
getInitiativeOperatingContext(initiativeId).catch(() => null),
|
|
||||||
getActiveWorkCycle(initiativeId).catch(() => null),
|
|
||||||
])
|
|
||||||
.then(([context, activeCycle]) => {
|
|
||||||
if (cancelled) return
|
|
||||||
setOperatingContext(context)
|
|
||||||
setHasActiveSprint(Boolean(activeCycle?.id))
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
if (!cancelled) {
|
|
||||||
setOperatingContext(null)
|
|
||||||
setHasActiveSprint(false)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
if (!cancelled) setLoading(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
cancelled = true
|
|
||||||
}
|
|
||||||
}, [initiativeId])
|
|
||||||
|
|
||||||
const profileInput = useMemo(
|
const profileInput = useMemo(
|
||||||
() => ({ operatingContext, hasActiveSprint }),
|
() => ({ operatingContext, hasActiveSprint }),
|
||||||
[operatingContext, hasActiveSprint],
|
[operatingContext, hasActiveSprint],
|
||||||
)
|
)
|
||||||
|
|
||||||
const profile = useMemo(
|
const profile = useMemo(() => {
|
||||||
() => resolveOperatingProfileFromInput(profileInput),
|
if (ops?.operatingProfile) {
|
||||||
[profileInput],
|
return ops.operatingProfile
|
||||||
)
|
}
|
||||||
|
return resolveOperatingProfileFromInput(profileInput)
|
||||||
|
}, [ops?.operatingProfile, profileInput])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
initiativeId,
|
initiativeId,
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,11 @@ export function OperatingRouteGate({ children }) {
|
||||||
return children
|
return children
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Context noch nicht da (API-Fehler) → nicht fälschlich auf Profil umleiten
|
||||||
|
if (!operatingContext) {
|
||||||
|
return children
|
||||||
|
}
|
||||||
|
|
||||||
const input = { operatingContext }
|
const input = { operatingContext }
|
||||||
const path = location.pathname
|
const path = location.pathname
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user