From 1166a0c7f1fb57ca62158a3ea7761b99f8ad1e0a Mon Sep 17 00:00:00 2001 From: Lars Date: Sun, 12 Jul 2026 10:38:01 +0200 Subject: [PATCH] =?UTF-8?q?AP1.16a-b:=20Execution-Graph=20f=C3=BCr=20Arbei?= =?UTF-8?q?tspaket-Abh=C3=A4ngigkeiten.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migration 023, Engine, API und ADP für Durchführungsplan getrennt vom Gate-Graph; Docs und Sprint-Assignment synchronisiert. Co-authored-by: Cursor --- backend/main.py | 2 + .../migrations/023_action_execution_plan.sql | 32 ++ backend/routers/actions.py | 4 + backend/routers/execution_plan.py | 119 ++++++ backend/routers/initiatives.py | 4 + backend/services/actions.py | 29 +- backend/services/execution_plan.py | 225 +++++++++++ backend/steering/graph/execution_engine.py | 246 ++++++++++++ backend/tests/test_ap16_execution_graph.py | 116 ++++++ backend/version.py | 2 +- ...Plan_and_Work_Package_Dependencies_v0.1.md | 354 ++++++++++++++++++ .../Kairo_Corrected_MVP_Roadmap_v0.2.md | 67 +++- .../Kairo_Implementation_Truth_Table_v0.1.md | 21 +- docs/product/Kairo_Plan_Mode_Design_v0.1.md | 104 ++++- ...Kairo_Status_Review_and_Next_Steps_v0.1.md | 10 +- .../Sprint1_AP1_16_Execution_Plan_v0.1.md | 129 +++++++ 16 files changed, 1421 insertions(+), 43 deletions(-) create mode 100644 backend/migrations/023_action_execution_plan.sql create mode 100644 backend/routers/execution_plan.py create mode 100644 backend/services/execution_plan.py create mode 100644 backend/steering/graph/execution_engine.py create mode 100644 backend/tests/test_ap16_execution_graph.py create mode 100644 docs/architecture/ADP_Execution_Plan_and_Work_Package_Dependencies_v0.1.md create mode 100644 docs/sprints/Sprint1_AP1_16_Execution_Plan_v0.1.md diff --git a/backend/main.py b/backend/main.py index de5a481..0b85aef 100644 --- a/backend/main.py +++ b/backend/main.py @@ -65,6 +65,7 @@ from routers import ( # noqa: E402 decisions, entity_archetypes, evidence, + execution_plan, features, initiatives, journey, @@ -100,6 +101,7 @@ app.include_router(roadmap.initiative_router) app.include_router(roadmap.items_router) app.include_router(roadmap.deps_router) app.include_router(roadmap.criteria_router) +app.include_router(execution_plan.initiative_router) app.include_router(evidence.router) app.include_router(decisions.router) app.include_router(reviews.router) diff --git a/backend/migrations/023_action_execution_plan.sql b/backend/migrations/023_action_execution_plan.sql new file mode 100644 index 0000000..39bcf39 --- /dev/null +++ b/backend/migrations/023_action_execution_plan.sql @@ -0,0 +1,32 @@ +-- AP1.16a: Durchführungsplan — Action-Reihenfolge, kind, Abhängigkeiten + +ALTER TABLE actions + ADD COLUMN IF NOT EXISTS sort_order INT NOT NULL DEFAULT 0; + +ALTER TABLE actions + ADD COLUMN IF NOT EXISTS action_kind VARCHAR(32) NOT NULL DEFAULT 'delivery' + CHECK (action_kind IN ('delivery', 'planning', 'review')); + +CREATE INDEX idx_actions_initiative_sort + ON actions(tenant_id, initiative_id, sort_order); + +CREATE TABLE action_dependencies ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, + initiative_id UUID NOT NULL REFERENCES initiatives(id) ON DELETE CASCADE, + predecessor_action_id UUID NOT NULL REFERENCES actions(id) ON DELETE CASCADE, + successor_action_id UUID NOT NULL REFERENCES actions(id) ON DELETE CASCADE, + dependency_kind VARCHAR(32) NOT NULL DEFAULT 'requires' + CHECK (dependency_kind IN ('requires', 'blocks', 'relates')), + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + UNIQUE (tenant_id, predecessor_action_id, successor_action_id, dependency_kind) +); + +CREATE INDEX idx_action_deps_predecessor + ON action_dependencies(tenant_id, predecessor_action_id); + +CREATE INDEX idx_action_deps_successor + ON action_dependencies(tenant_id, successor_action_id); + +CREATE INDEX idx_action_deps_initiative + ON action_dependencies(tenant_id, initiative_id); diff --git a/backend/routers/actions.py b/backend/routers/actions.py index e68b246..6a1344e 100644 --- a/backend/routers/actions.py +++ b/backend/routers/actions.py @@ -29,6 +29,8 @@ class ActionUpdateRequest(BaseModel): clear_project: bool = False roadmap_item_id: Optional[str] = None clear_roadmap_item: bool = False + sort_order: Optional[int] = None + action_kind: Optional[Literal["delivery", "planning", "review"]] = None class TaskCreateRequest(BaseModel): @@ -135,6 +137,8 @@ def update_action( clear_project=body.clear_project, roadmap_item_id=body.roadmap_item_id, clear_roadmap_item=body.clear_roadmap_item, + sort_order=body.sort_order, + action_kind=body.action_kind, ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc diff --git a/backend/routers/execution_plan.py b/backend/routers/execution_plan.py new file mode 100644 index 0000000..6a5015d --- /dev/null +++ b/backend/routers/execution_plan.py @@ -0,0 +1,119 @@ +"""Execution plan API — AP1.16b.""" + +from __future__ import annotations + +from typing import Literal, Optional + +from capabilities import require_capability +from fastapi import APIRouter, Depends, HTTPException, Query +from pydantic import BaseModel +from services import actions as action_service +from services import execution_plan as execution_plan_service +from steering.graph.execution_engine import load_initiative_execution_graph_state +from tenant_context import TenantContext + +initiative_router = APIRouter(prefix="/api/initiatives", tags=["execution-plan"]) + + +class ActionDependencyCreateRequest(BaseModel): + predecessor_action_id: str + successor_action_id: str + dependency_kind: Literal["requires", "blocks", "relates"] = "requires" + + +@initiative_router.get("/{initiative_id}/execution/graph-state") +def get_initiative_execution_graph_state( + initiative_id: str, + scope_roadmap_item_id: Optional[str] = Query(default=None), + ctx: TenantContext = Depends(require_capability("kairo.action.read")), +): + try: + state = load_initiative_execution_graph_state( + tenant_id=ctx.tenant_id, + initiative_id=initiative_id, + scope_roadmap_item_id=scope_roadmap_item_id, + ) + if scope_roadmap_item_id is None: + from services import actions as action_service + from services import roadmap as roadmap_service + + all_actions = action_service.list_actions_for_initiative( + tenant_id=ctx.tenant_id, initiative_id=initiative_id + ) + roadmap_items = roadmap_service.list_roadmap_items_for_initiative( + tenant_id=ctx.tenant_id, initiative_id=initiative_id + ) + from steering.graph.execution_engine import compute_planning_debt + + state["planning_debt"] = compute_planning_debt( + actions=all_actions, + roadmap_items=roadmap_items, + ) + return state + except ValueError as exc: + raise HTTPException(status_code=400, detail=str(exc)) from exc + + +@initiative_router.get("/{initiative_id}/execution/dependencies") +def list_initiative_action_dependencies( + initiative_id: str, + ctx: TenantContext = Depends(require_capability("kairo.action.read")), +): + try: + return execution_plan_service.list_dependencies_for_initiative( + tenant_id=ctx.tenant_id, initiative_id=initiative_id + ) + except ValueError as exc: + raise HTTPException(status_code=400, detail=str(exc)) from exc + + +@initiative_router.post("/{initiative_id}/execution/dependencies", status_code=201) +def create_initiative_action_dependency( + initiative_id: str, + body: ActionDependencyCreateRequest, + ctx: TenantContext = Depends(require_capability("kairo.action.manage")), +): + try: + predecessor = action_service.get_action( + tenant_id=ctx.tenant_id, action_id=body.predecessor_action_id + ) + successor = action_service.get_action( + tenant_id=ctx.tenant_id, action_id=body.successor_action_id + ) + if not predecessor or not successor: + raise ValueError("Action nicht gefunden") + if ( + predecessor["initiative_id"] != initiative_id + or successor["initiative_id"] != initiative_id + ): + raise ValueError("Actions gehören nicht zum angegebenen Vorhaben") + + return execution_plan_service.add_dependency( + tenant_id=ctx.tenant_id, + predecessor_action_id=body.predecessor_action_id, + successor_action_id=body.successor_action_id, + dependency_kind=body.dependency_kind, + user_id=ctx.user_id, + ) + except ValueError as exc: + raise HTTPException(status_code=400, detail=str(exc)) from exc + + +@initiative_router.delete( + "/{initiative_id}/execution/dependencies/{dependency_id}", + status_code=204, +) +def delete_initiative_action_dependency( + initiative_id: str, + dependency_id: str, + ctx: TenantContext = Depends(require_capability("kairo.action.manage")), +): + del initiative_id # scope validated via tenant + dependency row + try: + execution_plan_service.delete_dependency( + tenant_id=ctx.tenant_id, + dependency_id=dependency_id, + user_id=ctx.user_id, + ) + except ValueError as exc: + raise HTTPException(status_code=400, detail=str(exc)) from exc diff --git a/backend/routers/initiatives.py b/backend/routers/initiatives.py index 30a1585..2e8615a 100644 --- a/backend/routers/initiatives.py +++ b/backend/routers/initiatives.py @@ -67,6 +67,8 @@ class ActionCreateRequest(BaseModel): project_id: Optional[str] = None roadmap_item_id: Optional[str] = None assigned_actor_ids: list[str] = Field(default_factory=list) + sort_order: int = 0 + action_kind: Literal["delivery", "planning", "review"] = "delivery" class BlockerCreateRequest(BaseModel): @@ -331,6 +333,8 @@ def create_initiative_action( project_id=body.project_id, roadmap_item_id=body.roadmap_item_id, assigned_actor_ids=body.assigned_actor_ids, + sort_order=body.sort_order, + action_kind=body.action_kind, user_id=ctx.user_id, ) except ValueError as exc: diff --git a/backend/services/actions.py b/backend/services/actions.py index b1195a8..f0336d1 100644 --- a/backend/services/actions.py +++ b/backend/services/actions.py @@ -25,9 +25,11 @@ OPEN_ACTION_STATUSES = frozenset( _ACTION_COLUMNS = """ id, tenant_id, initiative_id, project_id, roadmap_item_id, title, description, - status, priority, due_at, created_at, updated_at + status, priority, due_at, sort_order, action_kind, created_at, updated_at """ +ACTION_KINDS = frozenset({"delivery", "planning", "review"}) + def _serialize_row(row: dict[str, Any]) -> dict[str, Any]: result = dict(row) @@ -43,6 +45,11 @@ def _serialize_row(row: dict[str, Any]) -> dict[str, Any]: return result +def _validate_action_kind(action_kind: str) -> None: + if action_kind not in ACTION_KINDS: + raise ValueError(f"Ungültiger action_kind: {action_kind}") + + def _validate_action_status(status: str) -> None: if status not in ACTION_STATUSES: raise ValueError(f"Ungültiger Action-Status: {status}") @@ -138,6 +145,8 @@ def create_action( project_id: Optional[str] = None, roadmap_item_id: Optional[str] = None, assigned_actor_ids: Optional[list[str]] = None, + sort_order: int = 0, + action_kind: str = "delivery", user_id: Optional[str] = None, ) -> dict[str, Any]: title = title.strip() @@ -145,6 +154,7 @@ def create_action( raise ValueError("Titel ist erforderlich") _validate_action_status(status) _validate_priority(priority) + _validate_action_kind(action_kind) if not get_initiative(tenant_id=tenant_id, initiative_id=initiative_id): raise ValueError("Initiative nicht gefunden") _validate_project_in_initiative( @@ -169,9 +179,9 @@ def create_action( f""" INSERT INTO actions ( tenant_id, initiative_id, project_id, roadmap_item_id, title, description, - status, priority, due_at + status, priority, due_at, sort_order, action_kind ) - VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s) + VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING {_ACTION_COLUMNS} """, ( @@ -184,6 +194,8 @@ def create_action( status, priority, due_at, + sort_order, + action_kind, ), ) row = _serialize_row(dict(cur.fetchone())) @@ -228,7 +240,7 @@ def list_actions_for_initiative( SELECT {_ACTION_COLUMNS} FROM actions WHERE tenant_id = %s AND initiative_id = %s - ORDER BY updated_at DESC, title + ORDER BY sort_order ASC, title ASC, created_at DESC """, (tenant_id, initiative_id), ) @@ -274,6 +286,8 @@ def update_action( clear_project: bool = False, roadmap_item_id: Optional[str] = None, clear_roadmap_item: bool = False, + sort_order: Optional[int] = None, + action_kind: Optional[str] = None, ) -> Optional[dict[str, Any]]: existing = get_action(tenant_id=tenant_id, action_id=action_id) if not existing: @@ -331,6 +345,13 @@ def update_action( conn.close() updates.append("roadmap_item_id = %s") params.append(roadmap_item_id) + if sort_order is not None: + updates.append("sort_order = %s") + params.append(sort_order) + if action_kind is not None: + _validate_action_kind(action_kind) + updates.append("action_kind = %s") + params.append(action_kind) if not updates: return existing diff --git a/backend/services/execution_plan.py b/backend/services/execution_plan.py new file mode 100644 index 0000000..c80ee8f --- /dev/null +++ b/backend/services/execution_plan.py @@ -0,0 +1,225 @@ +"""Execution plan — Action dependencies CRUD (AP1.16a/b).""" + +from __future__ import annotations + +from typing import Any, Literal, Optional + +from psycopg2.extras import RealDictCursor + +from db import get_connection +from services.audit import log_audit +from services.actions import get_action + +DependencyKind = Literal["requires", "blocks", "relates"] + +DEPENDENCY_KINDS = frozenset({"requires", "blocks", "relates"}) + + +def _validate_dependency_kind(kind: str) -> None: + if kind not in DEPENDENCY_KINDS: + raise ValueError(f"Ungültiger dependency_kind: {kind}") + + +def _serialize_dependency(row: dict[str, Any]) -> dict[str, Any]: + result = dict(row) + for key in ( + "id", + "tenant_id", + "initiative_id", + "predecessor_action_id", + "successor_action_id", + ): + if result.get(key): + result[key] = str(result[key]) + if result.get("created_at"): + result["created_at"] = result["created_at"].isoformat() + return result + + +def _would_create_cycle( + *, + dependencies: list[dict[str, Any]], + predecessor_id: str, + successor_id: str, +) -> bool: + """Prüft Zyklus wenn successor transitiv predecessor requires.""" + graph: dict[str, list[str]] = {} + for dep in dependencies: + if dep.get("dependency_kind", "requires") != "requires": + continue + pred = str(dep["predecessor_action_id"]) + succ = str(dep["successor_action_id"]) + graph.setdefault(succ, []).append(pred) + + graph.setdefault(successor_id, []).append(predecessor_id) + + visiting: set[str] = set() + visited: set[str] = set() + + def dfs(node: str) -> bool: + if node in visiting: + return True + if node in visited: + return False + visiting.add(node) + for upstream in graph.get(node, []): + if dfs(upstream): + return True + visiting.remove(node) + visited.add(node) + return False + + return dfs(successor_id) + + +def list_dependencies_for_initiative( + *, tenant_id: str, initiative_id: str +) -> list[dict[str, Any]]: + conn = get_connection() + try: + with conn.cursor(cursor_factory=RealDictCursor) as cur: + cur.execute( + """ + SELECT id, tenant_id, initiative_id, + predecessor_action_id, successor_action_id, + dependency_kind, created_at + FROM action_dependencies + WHERE tenant_id = %s AND initiative_id = %s + ORDER BY created_at + """, + (tenant_id, initiative_id), + ) + return [_serialize_dependency(dict(row)) for row in cur.fetchall()] + finally: + conn.close() + + +def add_dependency( + *, + tenant_id: str, + predecessor_action_id: str, + successor_action_id: str, + dependency_kind: DependencyKind = "requires", + user_id: Optional[str] = None, +) -> dict[str, Any]: + _validate_dependency_kind(dependency_kind) + if predecessor_action_id == successor_action_id: + raise ValueError("Abhängigkeit auf sich selbst nicht erlaubt") + + predecessor = get_action(tenant_id=tenant_id, action_id=predecessor_action_id) + successor = get_action(tenant_id=tenant_id, action_id=successor_action_id) + if not predecessor or not successor: + raise ValueError("Action nicht gefunden") + if predecessor["initiative_id"] != successor["initiative_id"]: + raise ValueError("Abhängigkeiten nur innerhalb eines Vorhabens") + + initiative_id = predecessor["initiative_id"] + existing = list_dependencies_for_initiative( + tenant_id=tenant_id, initiative_id=initiative_id + ) + if _would_create_cycle( + dependencies=existing, + predecessor_id=predecessor_action_id, + successor_id=successor_action_id, + ): + raise ValueError("Abhängigkeit würde Zyklus erzeugen") + + conn = get_connection() + try: + with conn.cursor(cursor_factory=RealDictCursor) as cur: + cur.execute( + """ + SELECT 1 FROM action_dependencies + WHERE tenant_id = %s + AND predecessor_action_id = %s + AND successor_action_id = %s + AND dependency_kind = %s + """, + ( + tenant_id, + predecessor_action_id, + successor_action_id, + dependency_kind, + ), + ) + if cur.fetchone(): + raise ValueError("Abhängigkeit existiert bereits") + + cur.execute( + """ + INSERT INTO action_dependencies ( + tenant_id, initiative_id, + predecessor_action_id, successor_action_id, + dependency_kind + ) + VALUES (%s, %s, %s, %s, %s) + RETURNING id, tenant_id, initiative_id, + predecessor_action_id, successor_action_id, + dependency_kind, created_at + """, + ( + tenant_id, + initiative_id, + predecessor_action_id, + successor_action_id, + dependency_kind, + ), + ) + row = _serialize_dependency(dict(cur.fetchone())) + conn.commit() + finally: + conn.close() + + log_audit( + "action.dependency_added", + user_id=user_id, + tenant_id=tenant_id, + details={ + "predecessor_action_id": predecessor_action_id, + "successor_action_id": successor_action_id, + "dependency_kind": dependency_kind, + }, + ) + return row + + +def delete_dependency( + *, tenant_id: str, dependency_id: str, user_id: Optional[str] = None +) -> bool: + conn = get_connection() + try: + with conn.cursor(cursor_factory=RealDictCursor) as cur: + cur.execute( + """ + SELECT id, predecessor_action_id, successor_action_id, dependency_kind + FROM action_dependencies + WHERE id = %s AND tenant_id = %s + """, + (dependency_id, tenant_id), + ) + row = cur.fetchone() + if not row: + raise ValueError("Abhängigkeit nicht gefunden") + + cur.execute( + """ + DELETE FROM action_dependencies + WHERE id = %s AND tenant_id = %s + """, + (dependency_id, tenant_id), + ) + conn.commit() + finally: + conn.close() + + log_audit( + "action.dependency_removed", + user_id=user_id, + tenant_id=tenant_id, + details={ + "dependency_id": dependency_id, + "predecessor_action_id": str(row["predecessor_action_id"]), + "successor_action_id": str(row["successor_action_id"]), + }, + ) + return True diff --git a/backend/steering/graph/execution_engine.py b/backend/steering/graph/execution_engine.py new file mode 100644 index 0000000..de95f95 --- /dev/null +++ b/backend/steering/graph/execution_engine.py @@ -0,0 +1,246 @@ +"""Execution plan read models — AP1.16b (Action dependency graph).""" + +from __future__ import annotations + +from typing import Any, Optional + +DONE_STATUSES = frozenset({"done"}) +OPEN_STATUSES = frozenset( + {"open", "ready", "in_progress", "blocked", "review_required"} +) +TERMINAL_SKIP = frozenset({"done", "discarded"}) + + +def _is_predecessor_satisfied(status: str) -> bool: + return status in DONE_STATUSES + + +def _sort_actions(actions: list[dict[str, Any]]) -> list[dict[str, Any]]: + return sorted( + actions, + key=lambda action: ( + action.get("sort_order", 0), + action.get("title") or "", + str(action.get("id", "")), + ), + ) + + +def _sequential_order_blocked_by( + action: dict[str, Any], + sorted_actions: list[dict[str, Any]], +) -> list[str]: + """Fallback ohne Kanten: sequenzielle Actions nach sort_order.""" + blocked_by: list[str] = [] + action_id = str(action["id"]) + for prev in sorted_actions: + if str(prev["id"]) == action_id: + break + if not _is_predecessor_satisfied(prev.get("status", "open")): + blocked_by.append(str(prev["id"])) + return blocked_by + + +def _compute_critical_path( + *, + action_by_id: dict[str, dict[str, Any]], + requires_predecessors: dict[str, list[str]], +) -> list[str]: + """Längste Kette über requires-Kanten (Memoization).""" + memo: dict[str, list[str]] = {} + + def chain_for(action_id: str) -> list[str]: + if action_id in memo: + return memo[action_id] + preds = requires_predecessors.get(action_id, []) + if not preds: + memo[action_id] = [action_id] + return memo[action_id] + best: list[str] = [] + for pred_id in preds: + if pred_id not in action_by_id: + continue + upstream = chain_for(pred_id) + if len(upstream) > len(best): + best = upstream + memo[action_id] = best + [action_id] + return memo[action_id] + + longest: list[str] = [] + for action_id in action_by_id: + candidate = chain_for(action_id) + if len(candidate) > len(longest): + longest = candidate + return longest + + +def compute_planning_debt( + *, + actions: list[dict[str, Any]], + roadmap_items: list[dict[str, Any]], +) -> list[dict[str, Any]]: + """Aktive Gates ohne verknüpfte Actions → Planning Debt (AP1.16d light).""" + active_gates = [ + item + for item in roadmap_items + if item.get("status") == "active" + ] + if not active_gates: + return [] + + actions_by_gate: dict[str, int] = {} + for action in actions: + gate_id = action.get("roadmap_item_id") + if not gate_id: + continue + gid = str(gate_id) + actions_by_gate[gid] = actions_by_gate.get(gid, 0) + 1 + + debts: list[dict[str, Any]] = [] + for gate in active_gates: + gid = str(gate["id"]) + if actions_by_gate.get(gid, 0) == 0: + debts.append( + { + "kind": "missing_execution_plan", + "roadmap_item_id": gid, + "title": gate.get("title"), + "message": "Aktiver Zielzustand ohne Durchführungsplan", + } + ) + return debts + + +def compute_execution_graph_state( + *, + actions: list[dict[str, Any]], + dependencies: list[dict[str, Any]], + scope_roadmap_item_id: Optional[str] = None, +) -> dict[str, Any]: + """ + Read Model pro Initiative (optional Gate-Scope). + + DB-Kante: predecessor → successor + - requires: successor blocked bis predecessor done + - blocks: successor blocked solange predecessor offen + - relates: keine Blockade + """ + if scope_roadmap_item_id: + scope = str(scope_roadmap_item_id) + actions = [ + a + for a in actions + if a.get("roadmap_item_id") and str(a["roadmap_item_id"]) == scope + ] + action_ids = {str(a["id"]) for a in actions} + dependencies = [ + dep + for dep in dependencies + if str(dep["predecessor_action_id"]) in action_ids + and str(dep["successor_action_id"]) in action_ids + ] + + sorted_actions = _sort_actions(actions) + action_by_id = {str(action["id"]): action for action in actions} + has_any_deps = len(dependencies) > 0 + + blocked_by_map: dict[str, list[str]] = { + action_id: [] for action_id in action_by_id + } + requires_predecessors: dict[str, list[str]] = { + action_id: [] for action_id in action_by_id + } + + for dep in dependencies: + kind = dep.get("dependency_kind") or "requires" + pred_id = str(dep["predecessor_action_id"]) + succ_id = str(dep["successor_action_id"]) + if pred_id not in action_by_id or succ_id not in action_by_id: + continue + + if kind == "requires": + requires_predecessors.setdefault(succ_id, []).append(pred_id) + predecessor = action_by_id[pred_id] + if not _is_predecessor_satisfied(predecessor.get("status", "open")): + if pred_id not in blocked_by_map[succ_id]: + blocked_by_map[succ_id].append(pred_id) + elif kind == "blocks": + blocker = action_by_id[pred_id] + if blocker.get("status", "open") in OPEN_STATUSES: + if pred_id not in blocked_by_map[succ_id]: + blocked_by_map[succ_id].append(pred_id) + # relates: no block + + if not has_any_deps: + for action in sorted_actions: + action_id = str(action["id"]) + for prereq_id in _sequential_order_blocked_by(action, sorted_actions): + if prereq_id not in blocked_by_map[action_id]: + blocked_by_map[action_id].append(prereq_id) + if prereq_id not in requires_predecessors.get(action_id, []): + requires_predecessors.setdefault(action_id, []).append(prereq_id) + + action_states: dict[str, dict[str, Any]] = {} + blocked_actions: list[str] = [] + ready_actions: list[str] = [] + + for action_id, action in action_by_id.items(): + status = action.get("status", "open") + blocked_by = blocked_by_map.get(action_id, []) + blocked = len(blocked_by) > 0 and status not in TERMINAL_SKIP + ready = status in OPEN_STATUSES and not blocked + + action_states[action_id] = { + "status": status, + "action_kind": action.get("action_kind", "delivery"), + "sort_order": action.get("sort_order", 0), + "roadmap_item_id": ( + str(action["roadmap_item_id"]) if action.get("roadmap_item_id") else None + ), + "blocked": blocked, + "ready": ready, + "blocked_by": blocked_by, + } + if blocked: + blocked_actions.append(action_id) + if ready: + ready_actions.append(action_id) + + critical_path = _compute_critical_path( + action_by_id=action_by_id, + requires_predecessors=requires_predecessors, + ) + + result: dict[str, Any] = { + "items": action_states, + "blocked_actions": blocked_actions, + "ready_actions": ready_actions, + "critical_path": critical_path, + "scope_roadmap_item_id": scope_roadmap_item_id, + } + + return result + + +def load_initiative_execution_graph_state( + *, + tenant_id: str, + initiative_id: str, + scope_roadmap_item_id: Optional[str] = None, +) -> dict[str, Any]: + """Lädt Actions, Kanten und optional Planning Debt.""" + from services import actions as action_service + from services import execution_plan as execution_plan_service + + actions = action_service.list_actions_for_initiative( + tenant_id=tenant_id, initiative_id=initiative_id + ) + dependencies = execution_plan_service.list_dependencies_for_initiative( + tenant_id=tenant_id, initiative_id=initiative_id + ) + + return compute_execution_graph_state( + actions=actions, + dependencies=dependencies, + scope_roadmap_item_id=scope_roadmap_item_id, + ) diff --git a/backend/tests/test_ap16_execution_graph.py b/backend/tests/test_ap16_execution_graph.py new file mode 100644 index 0000000..e16669e --- /dev/null +++ b/backend/tests/test_ap16_execution_graph.py @@ -0,0 +1,116 @@ +"""Unit tests for execution graph engine (AP1.16b).""" + +from steering.graph.execution_engine import ( + compute_execution_graph_state, + compute_planning_debt, +) + + +def _action(action_id: str, status: str = "open", **kwargs): + return { + "id": action_id, + "status": status, + "sort_order": kwargs.get("sort_order", 0), + "title": kwargs.get("title", action_id), + "action_kind": kwargs.get("action_kind", "delivery"), + "roadmap_item_id": kwargs.get("roadmap_item_id"), + } + + +def test_requires_blocks_until_predecessor_done(): + actions = [_action("b", "open"), _action("a", "open")] + deps = [ + { + "predecessor_action_id": "b", + "successor_action_id": "a", + "dependency_kind": "requires", + } + ] + state = compute_execution_graph_state(actions=actions, dependencies=deps) + assert state["items"]["a"]["blocked"] is True + assert state["items"]["a"]["blocked_by"] == ["b"] + assert "a" in state["blocked_actions"] + assert "a" not in state["ready_actions"] + + actions[0]["status"] = "done" + state = compute_execution_graph_state(actions=actions, dependencies=deps) + assert state["items"]["a"]["ready"] is True + assert "a" in state["ready_actions"] + + +def test_sequential_fallback_without_edges(): + actions = [ + _action("first", "open", sort_order=0), + _action("second", "open", sort_order=1), + ] + state = compute_execution_graph_state(actions=actions, dependencies=[]) + assert state["items"]["first"]["ready"] is True + assert state["items"]["second"]["blocked"] is True + assert state["items"]["second"]["blocked_by"] == ["first"] + + actions[0]["status"] = "done" + state = compute_execution_graph_state(actions=actions, dependencies=[]) + assert state["items"]["second"]["ready"] is True + + +def test_blocks_edge(): + actions = [_action("blocker", "in_progress"), _action("blocked", "open")] + deps = [ + { + "predecessor_action_id": "blocker", + "successor_action_id": "blocked", + "dependency_kind": "blocks", + } + ] + state = compute_execution_graph_state(actions=actions, dependencies=deps) + assert state["items"]["blocked"]["blocked"] is True + + actions[0]["status"] = "done" + state = compute_execution_graph_state(actions=actions, dependencies=deps) + assert state["items"]["blocked"]["ready"] is True + + +def test_critical_path(): + actions = [ + _action("a", "done", sort_order=0), + _action("b", "open", sort_order=1), + _action("c", "open", sort_order=2), + ] + deps = [ + { + "predecessor_action_id": "a", + "successor_action_id": "b", + "dependency_kind": "requires", + }, + { + "predecessor_action_id": "b", + "successor_action_id": "c", + "dependency_kind": "requires", + }, + ] + state = compute_execution_graph_state(actions=actions, dependencies=deps) + assert state["critical_path"] == ["a", "b", "c"] + + +def test_planning_debt_active_gate_without_actions(): + actions = [_action("x", roadmap_item_id="gate-2")] + roadmap_items = [ + {"id": "gate-1", "status": "active", "title": "G5"}, + {"id": "gate-2", "status": "active", "title": "G6"}, + ] + debts = compute_planning_debt(actions=actions, roadmap_items=roadmap_items) + assert len(debts) == 1 + assert debts[0]["roadmap_item_id"] == "gate-1" + + +def test_scope_filters_to_gate(): + actions = [ + _action("a", roadmap_item_id="g1", sort_order=0), + _action("b", roadmap_item_id="g2", sort_order=0), + ] + state = compute_execution_graph_state( + actions=actions, + dependencies=[], + scope_roadmap_item_id="g1", + ) + assert set(state["items"].keys()) == {"a"} diff --git a/backend/version.py b/backend/version.py index 028726c..834bf98 100644 --- a/backend/version.py +++ b/backend/version.py @@ -1,3 +1,3 @@ APP_VERSION = "0.18.0-ap2.0" -DB_SCHEMA_VERSION = "019" +DB_SCHEMA_VERSION = "023" APP_NAME = "jinkendo-kairo" diff --git a/docs/architecture/ADP_Execution_Plan_and_Work_Package_Dependencies_v0.1.md b/docs/architecture/ADP_Execution_Plan_and_Work_Package_Dependencies_v0.1.md new file mode 100644 index 0000000..4208294 --- /dev/null +++ b/docs/architecture/ADP_Execution_Plan_and_Work_Package_Dependencies_v0.1.md @@ -0,0 +1,354 @@ +# ADP — Durchführungsplan, Work-Package-Abhängigkeiten & Progressive Planung v0.1 + +**Status:** PO-Freigabe (2026-07-12) +**Stand:** 2026-07-12 +**Autor:** PO/Architektur (Dogfooding-Erkenntnis Kairo-Jinkendo) +**Auslöser:** Gate-Graph modelliert **Zielzustände**, nicht **Ausführungsreihenfolge** von Arbeitspaketen; Verfeinerung über Micro-Gates würde den Zielzustands-Designer entwerten +**Bezug:** `ADP_PO_Product_Layering_and_IA_Reset_v0.1.md`, `ADP_Roadmap_Graph_and_Gate_Checklist_v0.1.md`, `ADP_Recursive_Containers_and_Execution_Granularity_v0.1.md`, `ADP_Archetype_and_Method_Catalog_v0.2.md`, `Kairo_Plan_Mode_Design_v0.1.md`, `Kairo_Method_Design_Principles_v0.1.md` + +--- + +## Problem + +Für einen **Durchführungsplan** (Projektplan) müssen Arbeitspakete in eine **sinnvolle Reihenfolge** gebracht werden — inklusive Abhängigkeiten wie „Foundation vor Feature“, „AP1.4 vor AP1.7“. Heute existiert dafür praktisch nur der **Zielzustands-Designer** (Gate-Graph auf `RoadmapItem`). + +Das führt zu zwei Fehlmustern: + +| Fehlmuster | Folge | +|------------|--------| +| **Gates als Micro-APs** | Unendlich viele Detail-Gates; Plan und Ist vermischen sich | +| **Flache Action-Listen** | Keine enforced Abhängigkeiten; Reihenfolge nur in Köpfen/Docs | + +Gleichzeitig gilt produktlich: + +- **Nicht alles vorab ausplanen** — je nach Fortschritt wird die **nächste Planungsebene** erst relevant (rollende Planung). +- **Granularität steigt** mit Tiefe: Vorhaben → Phase/Stream → Arbeitspaket/Sprint → ToDos — optional auch **zeitlich** (Monat/Woche/Tag) statt nur strukturell. +- **Archetyp bestimmt**, welche Ebenen im **Basisstrukturplan** vorgesehen sind und ob fehlende Planung **automatisch** (Structure Builder) oder als **explizites ToDo** (Planning Action) ausgelöst wird. + +**Konkrete Lücke (Ist):** + +| Fähigkeit | Gate-Graph | Execution-Plan | +|-----------|------------|----------------| +| Kanten zwischen Plan-Knoten | ✓ `roadmap_item_dependencies` | ✗ | +| `ready` / `blocked` Read Model | ✓ Gates | ✗ Actions | +| Designer-UI | ✓ AP1.15 | ✗ | +| `sort_order` | ✓ Gates, Projects | ✗ Actions | +| Abhängigkeit AP → AP | ✗ | ✗ | + +--- + +## Betroffene Regeln + +| Dokument | Regel | +|----------|--------| +| Vision v0.2 §5–7 | Plan (Roadmap/Gates) vs. Ist (Action/Task) — getrennt halten | +| ADP PO Layering | „Plan zwischen Gates“ = Project → Action → Task; **getrennt** vom Gate-Graph | +| ADP Roadmap Graph | Graph = **Zielzustands-Modell**, kein Workflow, keine Ausführungs-Engine | +| Method Design §14.4 | `wbs_driven`: Work Package Items + **Dependencies** — Konzept, nicht implementiert | +| Recursive Containers | Project-Baum + Task-Baum — **Organisation**, nicht automatisch Abhängigkeitsgraph | +| Archetype Catalog §3 | Archetyp liefert Default-Methode und Plan-Ebenen — heute ohne Execution-Graph | + +--- + +## Leitentscheidung (Empfehlung) + +### 1. Drei getrennte Planungsdimensionen + +```text +┌─────────────────────────────────────────────────────────────────────────┐ +│ A. ZIEL-HORIZONT (Plan, grob, optional) │ +│ RoadmapItem: milestone, review_gate, maturity_stage, phase, … │ +│ Kanten: Gate-Graph — „Foundation ✓ vor IA ✓ vor Release …“ │ +│ UI: Zielzustands-Designer, Kontrolle, Journey │ +├─────────────────────────────────────────────────────────────────────────┤ +│ B. STRUKTUR-HORIZONT (Organisation, optional) │ +│ Project-Baum (parent_project_id), container_kind: phase/stream/… │ +│ Sequenz zwischen Projects: sort_order + optional project_dependencies │ +│ UI: Plan-Outline „Struktur“ │ +├─────────────────────────────────────────────────────────────────────────┤ +│ C. DURCHFÜHRUNGSPLAN (Ist-Plan, rollend verfeinert) │ +│ Action (Arbeitspaket) + Task (ToDo) │ +│ Kanten: action_dependencies — „AP X requires AP Y“ │ +│ Zeitbox: work_cycle (Sprint/Woche) — Priorisierung, kein Ersatz für C │ +│ UI: Plan-Outline „Arbeit“, Ausführen, Steering Surface │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +**PO-Regel:** Dimension **A** beantwortet *Was muss erreicht sein?* — **C** beantwortet *In welcher Reihenfolge committen wir Arbeit?* — **B** beantwortet *Wo im Programm hängt die Arbeit?* + +Der Gate-Designer bleibt **ausschließlich** für **A** (und ggf. grobe `feature`-Knoten als Zwischenziele, nicht als AP-Ersatz). + +### 2. Progressive Planungsgranularität („Rollende Planung“) + +Planung ist **kein einmaliger Vollplan**, sondern eine **Kette von Planungsschritten**, die ausgelöst werden, wenn der aktuelle Horizont **aktiv** oder **erreicht** ist und die nächste Ebene noch **leer** ist. + +```text +Ebene 0 Initiative — Ziel benennen, Methode wählen + │ + ▼ [on_method_selected] +Ebene 1 Ziel-Horizont — Gates / Phasen grob (0–15 Knoten) + │ (optional, archetypabhängig) + ▼ [on_roadmap_required] — wenn Gate aktiv & Unterplan fehlt +Ebene 2 Struktur — Phase / Stream / Project-Baum + │ (optional) + ▼ [on_structure_required] +Ebene 3 Durchführung — Arbeitspakete + Abhängigkeiten + │ (Action-Graph unter aktivem Gate/Project) + ▼ [on_plan_required | on_wbs_required | on_dependency_analysis_required] +Ebene 4 Zeitbox — Sprint / Woche / Iteration (work_cycle) + │ (optional, B3) + ▼ [cycle_structure_builder | on_replan_required] +Ebene 5 Ausführung — Tasks / ToDos unter Action + │ (AP1.5d) + ▼ [bei Bedarf: explizites „Zerlegen“-ToDo oder Agent-Step] +``` + +**Horizont-Regel:** Unter einem **aktiven Gate G** sind nur Actions sinnvoll planbar, die **auf G einzahlen** (`action.roadmap_item_id`) oder einem **Project unter G** zugeordnet sind. Der Execution-Graph gilt **pro Horizont-Scope** (Initiative, Gate oder Project), nicht global über alle Gates. + +**Nicht alles vorplanen:** Ebenen 3–5 dürfen für spätere Gates **bewusst leer** bleiben. Steuerung erzeugt dann **Planning Debt** (Attention), nicht stille Lücken. + +### 3. Zeitgesteuerte vs. strukturgetriebene Ebenen + +| Modus | Primäre Achse | Typische Archetypen | Beispiel-Ebenen | +|-------|---------------|---------------------|-----------------| +| **Strukturgetrieben** | Project → WP → Task | program, product, linear_project | Phase → AP → ToDo | +| **Zeitgetrieben** | work_cycle / Recurring | recurring_program, agile_iteration | Monat → Woche → Tag | +| **Hybrid** | Gates + Sprint | product + B3 | Release-Gate + Sprint-Backlog | +| **Minimal** | flach | generic | Vorhaben → Action/Task | + +Zeitplanung nutzt **`RoadmapItem(type=work_cycle)`** und **`actions.work_cycle_id`** (AP2.0f) — **kein** zweites Sprint-OM. Persönliche Monats-/Wochenpläne = `work_cycle` mit `cycle_kind` im Profil (Tag/Woche/Monat) oder `RecurringElement` für Routinen. + +--- + +## Planungsschritte: automatisch vs. explizites ToDo + +Ein **Planungsschritt** ist **Meta-Arbeit**: Struktur anlegen, WPs schätzen, Abhängigkeiten klären — **nicht** die fachliche Ausführung selbst. + +### Auslösung + +| Mechanismus | Wann | Ergebnis | +|-------------|------|----------| +| **Structure Builder** (automatisch) | Hook `on_*_required` + Profil `planning_mode=auto` | Seed-Struktur (Projects, Gates, initiale Actions) | +| **Planning Action** (explizit) | Profil `planning_mode=explicit` | Committetes AP z. B. „Phase 2 detaillieren“, „Sprint 12 planen“ | +| **Signal / Attention** | Horizont erreicht, nächste Ebene leer | „Planung für G5 ausstehend“ — kein stiller Zustand | +| **Skip** | Profil `planning_mode=skip` für Ebene | Ebene entfällt (z. B. generic: kein Gate-Graph) | + +### Hook-Zuordnung (bestehend, hier semantisch geschärft) + +| Hook | Planungsebene | Typisches Deliverable | +|------|---------------|------------------------| +| `on_structure_required` | 2 Struktur | Project-Baum, container_kind | +| `on_roadmap_required` | 1 Ziel-Horizont | Gate-Graph grob | +| `on_plan_required` | 3 Durchführung | AP-Liste + Reihenfolge | +| `on_wbs_required` | 3 Durchführung (WBS) | WP-Hierarchie + Dependencies | +| `on_dependency_analysis_required` | 3 | Kanten validieren, kritischer Pfad | +| `on_replan_required` | 4+ | Sprint-/Zyklus-Neuplanung | +| `on_dod_definition_required` | 1 | Gate-Kriterien | + +**PO-Regel:** Structure Builder **dürfen** initiale Actions vorschlagen, **dürfen aber nicht** ungeprüft produktive APs committen, wenn ein Gate mit Verify vorgesehen ist (Method Design §8). + +### Planning Action — Kennzeichnung + +MVP ohne neue Tabelle: Action mit **`action_kind`** (Registry/Enum, additiv): + +| `action_kind` | Bedeutung | +|---------------|-----------| +| `delivery` | Default — fachliches Arbeitspaket | +| `planning` | Meta — nächste Planungsebene ausfüllen | +| `review` | Meta — Review/Retrospektive (optional, später) | + +Zusätzlich optional: `planning_target_level` (int 1–5) und `planning_scope_id` (UUID Gate/Project) — rein deklarativ für UI und Next-Action. + +--- + +## Archetyp — welche Ebenen, welcher Modus + +Referenz-Matrix (PO-Vorschlag; Feinjustierung pro Method Profile): + +| Archetyp | Ebenen im Basisplan | Gate-Graph | Project-Baum | Action-Deps | Zeitbox | Planungsschritte | +|----------|---------------------|------------|--------------|-------------|---------|------------------| +| `generic` | 0 → 5 (minimal) | optional | optional | soft (sort_order) | — | meist **skip** bis Action | +| `linear_project` (A2) | 1 → 3 → 5 | grob (Phasen) | optional flach | **hard** (Graph) | — | **explicit** WP-Planung | +| `program` (B2a) | 1 → 2 → 3 → 5 | **ja** | **ja** | **hard** | optional | Mix auto/explicit | +| `product` (B2b) | 1 → 3 → 4 → 5 | **ja** (Orientierung) | optional | **hard** (Foundation) | Sprint (B3) | explicit für Sprint | +| `recurring_program` (A3) | 0 → 4 → 5 | selten | Domains | soft | **Woche/Monat** | auto Zyklen | +| `maturity_journey` (A1) | 1 → 2 → 5 | Stufen-Graph | pro Fähigkeit | Stufen-Kanten | — | auto Pfad + explicit Übung | +| `content_project` (D1) | 1 → 3 → 5 | Kapitel-Gates | Lanes | Kapitel-Reihenfolge | — | explicit pro Kapitel | + +**Kairo-Jinkendo (`product.kairo_dev`):** Ebenen 1 (G1–G8 grob) + 3 (AP-Graph unter aktivem Gate) + 4 (optional Sprint) — **nicht** jedes Foundation-AP als Gate. + +--- + +## Zielmodell Execution-Graph (Dimension C) + +### Semantik + +| Relation | Bedeutung | Verify? | +|----------|-----------|---------| +| `requires` | Nachfolger startet erst, wenn Vorgänger **done** (oder waived per Decision) | nein | +| `blocks` | Vorgänger offen → Nachfolger **blocked** (Attention) | nein | +| `relates` | Weiche Abhängigkeit — nur Anzeige, kein Block | nein | + +**Abgrenzung Gate-Graph:** + +| | Gate-Graph | Execution-Graph | +|---|------------|-----------------| +| Knoten | `RoadmapItem` | `Action` (später optional `Task`) | +| Status | planned/active/reached/… | open/in_progress/done/cancelled | +| Zweck | Quality / Zielzustand | Ausführungsreihenfolge | +| Verify | Kriterien + Evidence | Completion der Action | +| Granularität | grob (5–15) | fein (10–100+) | + +### Schema (additiv, Scope Lock Erweiterung) + +**Tabelle `action_dependencies`** (empfohlen): + +| Feld | Typ | Beschreibung | +|------|-----|--------------| +| id | uuid | | +| tenant_id | uuid | | +| initiative_id | uuid | Denormalisiert für Scope-Queries | +| predecessor_action_id | uuid | FK actions | +| successor_action_id | uuid | FK actions | +| dependency_kind | enum | `requires`, `blocks`, `relates` | +| created_at, created_by_actor_id | | Audit | + +Constraints: kein Selbstbezug; Zyklen verboten (gleiche Engine-Pattern wie Gate-Graph); beide Actions **gleicher Initiative**; optional gleicher `roadmap_item_id`-Horizont. + +**Ergänzung `actions`:** + +| Feld | Typ | Beschreibung | +|------|-----|--------------| +| sort_order | int | Manuelle Reihenfolge innerhalb Project/Gate (Fallback ohne Kanten) | +| action_kind | enum | `delivery`, `planning`, `review` (Default delivery) | + +**Optional später (nicht MVP):** `project_dependencies` analog für Struktur-Ebene B; `RoadmapItem(type=feature)` als Plan-Knoten zwischen Gate und AP (Zuordnung Action → feature). + +### Engine & Read Models + +Neues Modul **`backend/steering/graph/execution_engine.py`** (Analog `roadmap_engine.py`): + +| Read Model | Bedeutung | +|------------|-----------| +| `ready_actions(scope)` | Keine offenen `requires`-Vorgänger | +| `blocked_actions(scope)` | Mindestens ein Vorgänger nicht done | +| `critical_path(scope)` | Längste Kette — für `sequential_dependency` | +| `planning_debt(scope)` | Aktiver Horizont ohne Actions oder ohne Planungs-AP | + +Exposure: Steering-Snapshot, Plan-Outline API, Next-Action-Strategien (AP2.0d). + +**Scope Lock:** Execution Engine steuert **nicht** Lifecycle-Übergänge automatisch — nur Read Models + Attention. (Gleiche Leitlinie wie Gate-Graph ≠ Workflow.) + +--- + +## UI & IA + +| Ort | Inhalt | Nicht | +|-----|--------|-------| +| **Plan-Outline „Arbeit“** | AP-Baum, Vorgänger/Kanten, `ready/blocked`, Planning Actions | Gate-Topologie | +| **Zielzustands-Designer** | Gate-Graph, Joins, Kriterien | AP-Abhängigkeiten | +| **Steering Surface / Ausführen** | 1–3 **ready** Next Actions im aktiven Horizont | Vollständige Roadmap | +| **Kontrolle Plan/Ist** | Gate-Diff (AP1.14) + optional „Execution vs. Plan“ (AP geplant vs. done) | — | + +**Planning Debt** in Attention: z. B. „Gate G5 aktiv — Durchführungsplan leer — Planungsschritt ausstehend“. + +--- + +## Optionen (Entscheidung) + +| Option | Kurz | Pro | Contra | +|--------|------|-----|--------| +| **A — Nur sort_order auf Actions** | Liste + manuelles DnD | Minimal, schnell | Keine echten Deps, kein blocked | +| **B — Execution-Graph auf Actions** (Empfehlung) | `action_dependencies` + Engine | Foundation-Deps, ready/blocked, Archetyp A2/B2 | Schema + AP; UI-Kanten | +| **C — Alles über Gate-Graph** | Jedes AP = Gate | Ein Graph | Semantik-Bruch, Explosion, Verify-Unsinn | +| **D — Nur WBS-RoadmapItems** | WP als RoadmapItem(type=feature) | Plan/Ist an einem Ort | Schwere Pflege; Ist-Status an Plan-Knoten | +| **E — Hybrid B + D** | Gates grob + feature optional + Action-Graph | Skaliert für Mega-Programme | Zwei Plan-Knotentypen + Actions | + +### Empfehlung + +**Option E (stufenweise):** + +1. **MVP-Slice:** **B** — `action_dependencies`, `sort_order`, `action_kind`, Execution Engine, Plan-Outline-Kantenansicht, Planning Debt Signal. +2. **Post-MVP:** **D** selektiv — `feature`-RoadmapItems als Zwischenziele unter Gates, wenn Programm > ~30 APs. +3. **Nicht:** **C**. + +--- + +## Implementierungspakete (Vorschlag) + +| Paket | Inhalt | Abhängigkeit | +|-------|--------|--------------| +| **AP1.16a** | Migration `action_dependencies`, `actions.sort_order`, `action_kind` | AP1.5 | +| **AP1.16b** | `execution_engine`, API, pytest | AP1.16a | +| **AP1.16c** | Plan-Outline: Kanten + blocked/ready; Planning Action UX | AP1.12, AP1.16b | +| **AP1.16d** | Method Profile: `planning_levels`, `planning_mode` pro Ebene; Planning Debt in Attention | AP2.0a | +| **AP2.0f** | `work_cycle` + `actions.work_cycle_id` — Zeitbox-Ebene | unverändert | +| **AP2.0d** | Next-Action-Strategien nutzen `ready_actions` | AP1.16b | + +**Parallel dokumentieren:** `Kairo_Plan_Mode_Design_v0.1.md` § „Arbeit“ um Execution-Graph ergänzen; Truth Table Zeile Action-Dependencies. + +--- + +## Risiko + +| Risiko | Mitigation | +|--------|------------| +| Zwei Graphen verwirren Nutzer | Klare IA: Designer = Ziele, Outline = Durchführung; Begriffe in UI | +| Graph-Pflege-Overhead | Rollende Planung — nur aktiver Horizont; sort_order als Fallback | +| Planning Actions als Ballast | Nur bei `planning_mode=explicit`; auto-Profile nutzen Builder | +| Scope Creep WBS | MVP = Action-Kanten; feature-Knoten später | +| Agent erzeugt ungültige Deps | Cycle-Detection API; Audit | + +--- + +## Rückbaubarkeit + +- `action_dependencies` optional — leere Tabelle, sort_order allein reicht für flache Vorhaben. +- `action_kind` Default `delivery` — bestehende Actions unverändert. +- Execution Engine isoliert in `backend/steering/` — abschaltbar per Method Profile. +- Gate-Graph unberührt. + +--- + +## Auswirkung auf Sprint / Scope Lock + +- **Erweitert Scope Lock** für **eine** neue OM-Relation (`action_dependencies`) + additive Action-Felder — analog AP1.4 Gate-Dependencies. +- **Kein** Ersatz für AP2.0f (`work_cycle`) oder Structure Builder — ergänzt die Ist-Schicht. +- **Blockiert nicht** Dogfooding R1: bis AP1.16 manuelle Gate-Zuordnung + Docs-Reihenfolge; danach AP-Graph im Seed/API. + +--- + +## Freigabe-Checkliste PO + +- [x] Drei Planungsdimensionen (Ziel / Struktur / Durchführung) akzeptiert +- [x] Progressive Planung: nicht alles vorab, Planning Debt sichtbar +- [x] Planungsschritte: auto (Builder) vs. explicit (Planning Action) vs. skip — archetypgesteuert +- [x] Gate-Designer **nicht** für AP-Abhängigkeiten +- [x] MVP = Action-Execution-Graph (Option B/E Phase 1) +- [x] Zeitplanung über `work_cycle` / Recurring, nicht über Gate-Microplanung +- [x] AP1.16a–d in Roadmap v0.2 aufnehmen + +--- + +## Referenz — Kairo-Jinkendo (Dogfooding) + +```text +ZIEL-HORIZONT (A) — grob, 8 Gates + G1 Foundation ──requires──► G2 IA ──requires──► G3 Gates ── … + +Unter G5 „Plan/Ist & Cockpit“ (aktiv): + DURCHFÜHRUNGSPLAN (C) + AP1.14 Plan-Snapshot ──requires──► AP1.9c Cockpit + AP1.9c ──requires──► AP2.0d Next-Action + AP2.0d ──requires──► AP1.7 Operational API + + ZEITBOX (D) optional + work_cycle „Iteration R2“ → 1–3 ready Actions + + PLANUNGSSCHRITT (explicit) + Action(kind=planning): „G6 Durchführungsplan detaillieren“ — erst wenn G5 reached +``` + +--- + +*Siehe auch: `ADP_Roadmap_Graph_and_Gate_Checklist_v0.1.md`, `Sprint1_AP1_5_Hierarchy_Assignment_v0.1.md`, `Kairo_Corrected_MVP_Roadmap_v0.2.md`* diff --git a/docs/product/Kairo_Corrected_MVP_Roadmap_v0.2.md b/docs/product/Kairo_Corrected_MVP_Roadmap_v0.2.md index d6bf636..1f379b8 100644 --- a/docs/product/Kairo_Corrected_MVP_Roadmap_v0.2.md +++ b/docs/product/Kairo_Corrected_MVP_Roadmap_v0.2.md @@ -2,7 +2,7 @@ ## Corrected MVP Roadmap v0.2 **Status:** führende Produkt-Roadmap (AP-Historie + nächste Schritte) -**Stand:** 2026-07-11 (Review + DOC-Sync) +**Stand:** 2026-07-12 (Execution-Plan ADP + AP1.16) **Ersetzt:** `Kairo_Corrected_MVP_Roadmap_v0.1.md` **Vision:** `Kairo_Vision_and_Product_Direction_v0.2.md` **MVP-Nordstern:** `Kairo_MVP_Definition_v0.3.md` (führend für Abnahfe) @@ -92,6 +92,25 @@ Siehe `Kairo_Plan_Mode_Design_v0.1.md`, `ADP_AP1_10_Initiative_Archetypes_and_En --- +## 1.5 PO-Entscheidung 2026-07-12 — Durchführungsplan & Progressive Planung + +**Auslöser:** Dogfooding — AP-Reihenfolge (Foundation vor Features) nicht über Gate-Graph abbildbar; Micro-Gates würden Zielzustands-Designer entwerten. + +| Entscheidung | | +|--------------|--| +| **Drei Planungsdimensionen** | Ziel-Horizont (Gate-Graph) ∥ Struktur (Project-Baum) ∥ **Durchführungsplan** (Action-Graph) | +| **Rollende Planung** | Nicht alles vorab — nächste Ebene planen, wenn Horizont aktiv und Unterplan leer | +| **Planungsschritte** | Archetyp: auto (Structure Builder) / explicit (`action_kind=planning`) / skip | +| **Gate-Designer** | **nur** Zielzustände — **keine** AP-Abhängigkeiten | +| **MVP-Slice** | `action_dependencies` + Execution Engine + Plan-Outline-Kanten (AP1.16) | +| **Zeitbox** | weiter über `work_cycle` (AP2.0f), nicht Gate-Microplanung | + +Siehe `ADP_Execution_Plan_and_Work_Package_Dependencies_v0.1.md`, `Kairo_Plan_Mode_Design_v0.1.md` §6–7. + +**Reihenfolge Code:** AP1.16a–b **vor** AP2.0d (Strategien nutzen `ready_actions`); AP1.16c–d parallel zu AP2.0a. + +--- + ## 2. Phasenübersicht ```text @@ -109,6 +128,7 @@ Phase H2 PM Work Modes AP1.9 ✓ 9a / → 9b–9e Phase H3 Plan Outline AP1.12 + AP1.10 ◐ 12a–d, 10c Phase I Gate-Graph AP1.13 + AP1.15 ◐ 13a/b, 15a–c Phase I2 Plan/Ist Snapshots AP1.14 ✓ +Phase I3 Execution-Plan AP1.16 ◐ 16a–b Code, 16c–d offen Phase J Portfolio AP1.8 ◐ 8a ✓ / 8b deferred Phase K Archetyp-Steuerung AP2.0 ◐ 2.0a–c ✓ / → 2.0d–f Phase L Agent Interface AP1.7 ○ @@ -116,7 +136,7 @@ Phase M Validation AP0.10d + Dogfooding ← **NÄCHSTES (PO 2026-07-11) Phase N Integration Gitea/MCP nach AP1.7 ``` -**Frontend-Version:** `0.18.0-ap2.0` · **Schema:** Migration 022 +**Frontend-Version:** `0.18.0-ap2.0` · **Schema:** Migration 023 (AP1.16a) --- @@ -161,12 +181,14 @@ Siehe **`Kairo_Status_Review_and_Next_Steps_v0.1.md` §5** für vollständige Ro | D0 | DOC-Sync (Truth Table, Gap, Review) | ✓ | eine Wahrheit | | D1 | Dogfooding R1 — Kairo-Jinkendo in Kairo | **→ nächstes** | B2b-Validation | | 1 | AP1.9c Cockpit-Signale | offen | MVP §5.7 | -| 2 | AP2.0d Next-Action-Strategien | offen | MVP Stufe A | -| 3 | Dogfooding R2 — Ist + Gitea-Evidence | offen | Fortschritt sichtbar | -| 4 | AP0.10d / AP2.1 Validation B2b | offen | MVP-Urteil | -| 5 | AP2.0f work_cycle (B3 minimal) | offen | Sprint-Zeitbox | -| 6 | AP1.7 Operational API | offen | MCP-Voraussetzung | -| 7 | Gitea-Webhook + MCP | deferred | Schicht 4 | +| 2 | AP1.16a–b Execution-Graph (Schema + Engine) | **◐ Code** | **vor** AP2.0d; Remote-Verifikation nach Deploy | +| 3 | AP2.0d Next-Action-Strategien | offen | MVP Stufe A; nutzt `ready_actions` | +| 4 | AP1.16c–d Plan-Outline-Kanten + Planning Debt | offen | nach 16a–b | +| 5 | Dogfooding R2 — Ist + Gitea-Evidence | offen | Fortschritt sichtbar | +| 6 | AP0.10d / AP2.1 Validation B2b | offen | MVP-Urteil | +| 7 | AP2.0f work_cycle (B3 minimal) | offen | Sprint-Zeitbox | +| 8 | AP1.7 Operational API | offen | MCP-Voraussetzung | +| 9 | Gitea-Webhook + MCP | deferred | Schicht 4 | **Erledigt (Review-Queue):** AP1.6b ✓ @@ -275,6 +297,28 @@ Graph-UI für Gate-Abhängigkeiten (Mitai-**Pattern**, Kairo Zielzustands-Semant --- +### AP1.16 — Durchführungsplan & Work-Package-Abhängigkeiten ← **NÄCHSTES (nach 9c, vor 2.0d)** + +**Ziel:** Ausführungsreihenfolge von Arbeitspaketen modellieren — **getrennt** vom Gate-Graph; rollende Planungsgranularität. + +| Teil | Scope | +|------|--------| +| **16a** | Migration `action_dependencies`; `actions.sort_order`, `action_kind` | +| **16b** | `backend/steering/graph/execution_engine.py`, API, pytest (`ready`/`blocked`/critical path) | +| **16c** | Plan-Outline „Arbeit“: Vorgänger-Kanten, blocked/ready; **nicht** im Zielzustands-Designer | +| **16d** | Method Profile: `planning_levels`, `planning_mode`; Planning Debt in Attention | + +**ADP:** `ADP_Execution_Plan_and_Work_Package_Dependencies_v0.1.md` (PO 2026-07-12) +**Assignment:** `Sprint1_AP1_16_Execution_Plan_v0.1.md` +**Design:** `Kairo_Plan_Mode_Design_v0.1.md` §6–7 +**Abhängigkeit:** AP1.5, AP1.12d; **16b vor AP2.0d** +**Version (Ziel):** `0.19.0-ap1.16` +**Stand Code:** 16a–b implementiert (Migration 023), 16c–d offen + +**Explizit nicht:** AP-Abhängigkeiten als Micro-Gates; Lifecycle aus Execution-Graph ableiten. + +--- + ### AP1.5d — Rekursive Tasks & Roll-up **Ziel:** Task-Baum (`parent_task_id`); Mensch UI max. ~3 Ebenen; Agent tiefer. @@ -419,10 +463,11 @@ MVP-nah wenn: 0.16.x ✓ AP1.9a, AP1.12, AP1.13 (teilweise) 0.17.x ✓ AP1.14, AP1.15 (teilweise) 0.18.0-ap2.0 ✓ Archetyp-Slice AP2.0 -0.18.x → AP1.9c, Dogfooding, AP2.0d — NÄCHSTES -0.19.x → AP2.0f work_cycle, AP2.1 Validation +0.18.x → AP1.9c, Dogfooding R1 — NÄCHSTES +0.19.0-ap1.16 → Execution-Graph (Schema, Engine, Outline-Kanten) +0.19.x → AP2.0d Strategien, AP2.0f work_cycle, AP2.1 Validation 0.20.0-ap1.7 → Operational Actor Interface ``` --- -*Siehe auch: `ADP_Recursive_Containers_and_Execution_Granularity_v0.1.md`, `ADP_PO_Product_Layering_and_IA_Reset_v0.1.md`, `Sprint1_AP1_5_Hierarchy_Assignment_v0.1.md`, `Kairo_MVP_Usability_Recovery_Plan_v0.2.md`* +*Siehe auch: `ADP_Execution_Plan_and_Work_Package_Dependencies_v0.1.md`, `ADP_Recursive_Containers_and_Execution_Granularity_v0.1.md`, `ADP_PO_Product_Layering_and_IA_Reset_v0.1.md`, `Sprint1_AP1_5_Hierarchy_Assignment_v0.1.md`, `Kairo_MVP_Usability_Recovery_Plan_v0.2.md`* diff --git a/docs/product/Kairo_Implementation_Truth_Table_v0.1.md b/docs/product/Kairo_Implementation_Truth_Table_v0.1.md index f941c14..149c16a 100644 --- a/docs/product/Kairo_Implementation_Truth_Table_v0.1.md +++ b/docs/product/Kairo_Implementation_Truth_Table_v0.1.md @@ -1,7 +1,7 @@ # Kairo — Implementation Truth Table v0.1 **Status:** living document — bei jedem AP aktualisieren -**Stand:** 2026-07-11 (Review + DOC-Sync; Code `develop` @ `b85a6ec`, Frontend `0.18.0-ap2.0`) +**Stand:** 2026-07-12 (Execution-Plan ADP, AP1.16 in Roadmap) **Zweck:** Ehrliche Trennung von **implementiert**, **teilweise**, **nur API/Schema**, **nur Dokumentiert** Verhindert, dass Zielbild-Dokumente als Ist-Stand gelesen werden. @@ -32,7 +32,7 @@ Verhindert, dass Zielbild-Dokumente als Ist-Stand gelesen werden. | Capabilities | ✓ | Keine Objekt-Sichtbarkeit | | Auth / Session | ✓ | | | Audit (Auth/Admin) | ◐ | Nicht für alle OM-Events | -| Migrationen nummeriert | ✓ | Schema bis **022** (`portfolio_rank`) | +| Migrationen nummeriert | ✓ | Schema bis **023** (`action_execution_plan`) | --- @@ -42,8 +42,11 @@ Verhindert, dass Zielbild-Dokumente als Ist-Stand gelesen werden. |---------|-------|-----------| | Initiative | ✓ | inkl. `archetype_key`, `portfolio_rank` | | Project | ◐ | AP1.5 + AP1.5c: Baum, Blatt-Regel; `archetype_key` Mirror AP2.0b | -| Action | ✓ | Gate-Zuordnung AP1.6, `due_at` | +| Action | ✓ | Gate-Zuordnung AP1.6, `due_at`; `sort_order` / Deps AP1.16 geplant | | ActionAssignment | ✓ | | +| Action Dependencies (Execution-Graph) | ◐ | Migration 023, Engine AP1.16b; GUI AP1.16c offen | +| `action_kind` (planning/delivery) | ◐ | Migration 023, API AP1.16a | +| `actions.sort_order` | ◐ | Migration 023 | | BacklogItem | ✓ | Gate-Zuordnung AP1.6, `sort_order` | | Blocker | ✓ | `action_id` optional | | Milestone | ◐ | Legacy-Tabelle MVP-Brücke; RoadmapItem bevorzugt | @@ -54,13 +57,15 @@ Verhindert, dass Zielbild-Dokumente als Ist-Stand gelesen werden. | Task (unter Action) | ◐ | AP1.5d: `parent_task_id`, Baum-API, Outline; Roll-up-UI ◐ | | Roadmap | ◐ | 1 pro Initiative; Migration 010 | | RoadmapItem | ◐ | CRUD + Detail; Verify AP1.4b | -| RoadmapItem Dependencies | ◐ | requires/blocks/related; Graph AP1.4d | +| RoadmapItem Dependencies | ◐ | requires/blocks/related; Graph AP1.4d — **nur Gates** | | RoadmapItem Criteria | ◐ | Checkliste, waive/defer, Reopen AP1.4b/c | | Plan-Ist-Verknüpfung | ◐ | Links, Kriterien, Journey AP1.6; Snapshots AP1.14 | | Plan-Snapshots | ◐ | Migration 021, Diff-View Kontrolle AP1.14 | | Entity Field System | ◐ | Migration 017, Seeds AP1.10b/AP2.0b | | Initiative Archetypes | ◐ | Migration 016, Registry AP2.0b | | Method Profiles (Code-Seeds) | ◐ | `product.kairo_dev`, Kumite, Buch — AP2.0b | +| Execution Graph Engine | ◐ | AP1.16b; API `/execution/graph-state` | +| Planning Debt (Attention) | ◐ | Read Model AP1.16b; Cockpit AP1.16d offen | | `work_cycle` / Sprint | ✗ | 📄 MVP v0.3 B3; Migration AP2.0f geplant | --- @@ -109,7 +114,8 @@ Verhindert, dass Zielbild-Dokumente als Ist-Stand gelesen werden. | Gate Map (read-only) | ✓ | AP1.13a | | Gate Dependencies pflegen | ◐ | AP1.13b Detail | | Zielzustands-Designer | ◐ | AP1.15a–c; OR deferred | -| Plan-Outline | ◐ | AP1.12a–d: Baum, Modal, Reorder, Actions | +| Plan-Outline | ◐ | AP1.12a–d: Baum, Modal, Reorder, Actions; AP-Kanten AP1.16c offen | +| Execution-Plan (AP-Graph) | ✗ | 📄 AP1.16; Outline „Arbeit“, nicht Gate-Designer | | Profil-Modal (Archetyp/EFS) | ◐ | AP1.10c | | Modal-Bearbeitung (Gates/Profil) | ◐ | viele Sektionen noch Inline-CRUD | | Admin-UI | ✗ | | @@ -123,7 +129,8 @@ Verhindert, dass Zielbild-Dokumente als Ist-Stand gelesen werden. |---------------------------|-----| | DoD / prüfbare Kriterien | ◐ | `roadmap_item_criteria`; UI AP1.4c | | Zieltermin | ◐ | `target_date` | -| Abhängigkeiten | ◐ | API + Graph Engine AP1.4d; Designer AP1.15 | +| Abhängigkeiten (Gates) | ◐ | API + Graph Engine AP1.4d; Designer AP1.15 | +| Abhängigkeiten (Actions) | ✗ | 📄 AP1.16; Execution Engine | | parallel vs. sequenziell | ◐ | Kanten + `parallel_group`; `sequencing_mode` deprecated | | Plan-Graph vs. Ist-Overlay | ◐ | Designer AP1.15; Journey Ist-Graph AP1.6b; Diff AP1.14 | | Zielzustands-Designer (Joint/Parallel) | ◐ | AP1.15a–c; Join/OR AP1.15d deferred | @@ -195,6 +202,8 @@ Details: `Kairo_Status_Review_and_Next_Steps_v0.1.md` §2.1 | AP | Erwartung | |----|-----------| +| AP1.16a–b | Action deps, Execution Engine ✗→◐ (Deploy) | +| AP1.16c–d | Outline-Kanten, Planning Debt ✗→◐ | | AP1.9c | Cockpit-Signale ◐→✓ | | AP2.0d | Strategien ○→◐ | | Dogfooding R1 | Referenz-Vorhaben in Alltag ◐ | diff --git a/docs/product/Kairo_Plan_Mode_Design_v0.1.md b/docs/product/Kairo_Plan_Mode_Design_v0.1.md index c4b510f..3efecbb 100644 --- a/docs/product/Kairo_Plan_Mode_Design_v0.1.md +++ b/docs/product/Kairo_Plan_Mode_Design_v0.1.md @@ -1,8 +1,8 @@ # Kairo — Plan-Modus Design v0.1 -**Status:** PO-Arbeitsentwurf (Entscheidung ausstehend) -**Stand:** 2026-07-10 -**Bezug:** `Kairo_PM_Frontend_UI_Concept_v0.1.md` §4.3, `ADP_AP1_10_Initiative_Archetypes_and_Entity_Field_System_v0.1.md` +**Status:** PO-Arbeitsentwurf (Execution-Plan §6–7 PO 2026-07-12) +**Stand:** 2026-07-12 +**Bezug:** `Kairo_PM_Frontend_UI_Concept_v0.1.md` §4.3, `ADP_AP1_10_Initiative_Archetypes_and_Entity_Field_System_v0.1.md`, `ADP_Execution_Plan_and_Work_Package_Dependencies_v0.1.md` **Auslöser:** Planen soll von Tab-Fragmenten zu durchgängiger Programmgestaltung werden — bis Struktur, Gates, Eingang, Arbeitspakete und Tasks --- @@ -196,22 +196,78 @@ Baut auf AP1.4d (Graph Engine read models) auf — UI AP1.13 **nach** 4d oder pa --- -## 6. Ebenen bis Workpackage & Task +## 6. Progressive Planung — aufsteigende Granularität + +Planen ist **kein einmaliger Vollplan**. Je nach Archetyp existieren unterschiedlich viele Ebenen; die **nächste Ebene** wird erst relevant, wenn der aktuelle Horizont **aktiv** ist und der Unterplan noch **leer** ist (rollende Planung). + +### 6.1 Drei Planungsdimensionen (verbindlich) + +| Dimension | Frage | OM / UI | +|-----------|--------|---------| +| **A — Ziel-Horizont** | Was muss erreicht sein? | `RoadmapItem`, Gate-Graph — §5, Zielzustands-Designer | +| **B — Struktur** | Wo hängt die Arbeit? | Project-Baum — Outline „Struktur“ | +| **C — Durchführung** | In welcher Reihenfolge committen wir APs? | `Action` + `action_dependencies` — Outline „Arbeit“ §6.3 | +| **D — Zeitbox** (optional) | Was in dieser Iteration/Woche? | `work_cycle` (AP2.0f), Recurring — nicht Gate-Microplan | + +**PO-Regel:** Dimension **A** ≠ **C**. AP-Abhängigkeiten (z. B. „Foundation vor Feature“) gehören in **C**, nicht in den Gate-Designer. + +### 6.2 Planungsebenen (Referenzleiter) + +```text +Ebene 0 Initiative / Vorhaben — Methode, Archetyp, Profil +Ebene 1 Ziel-Horizont — Gates, Phasen grob (optional) +Ebene 2 Struktur — Phase / Stream / Project-Baum (optional) +Ebene 3 Durchführungsplan — Arbeitspakete + Kanten (rollend unter aktivem Gate) +Ebene 4 Zeitbox — Sprint / Woche / Monat (optional, archetypabhängig) +Ebene 5 Ausführung — Tasks / ToDos unter Action +``` + +**Minimal-Vorhaben** (`generic_operating`): oft nur Ebene 0 → 3 → 5 (Vorhaben → Actions → Tasks). +**Product-Programm** (Kairo-Jinkendo): 1 (G1–G8 grob) + 3 + optional 4 + 5. +**Persönliches Rhythmus-Vorhaben**: eher Ebene 4 zeitgetrieben (Woche/Monat) statt WBS-Tiefe. + +### 6.3 Planungsschritte — auto vs. explizites ToDo + +Meta-Arbeit (Struktur anlegen, WPs schätzen, Deps klären) wird archetypgesteuert ausgelöst: + +| Modus | Mechanismus | Beispiel | +|-------|-------------|----------| +| **auto** | Structure Builder + Hook (`on_plan_required`, `on_wbs_required`) | Kumite-Pfad seeden | +| **explicit** | Committetes AP mit `action_kind=planning` | „G6 Durchführungsplan detaillieren“ | +| **skip** | Ebene entfällt | Generic ohne Gate-Graph | + +**Planning Debt:** Wenn Gate aktiv und Durchführungsplan leer → Attention (AP1.16d), kein stiller Leerzustand. + +### 6.4 Outline-Knoten „Arbeit“ — Durchführungsplan (Dimension C) + +| Aspekt | Regel | +|--------|--------| +| Scope | Actions unter Initiative / Project / **aktivem Gate-Horizont** | +| Reihenfolge | `sort_order` (Fallback) + `action_dependencies` (requires/blocks) | +| Anzeige | Liste + optional Kanten-Ansicht; `ready` / `blocked` Badges | +| Bearbeitung | Modal (ActionForm); Kanten auf Detail oder Mini-Graph in Kontext | +| **Nicht** | Gate-Topologie, Verify-Kriterien, Join-Knoten | + +**Plan-Baum „Arbeit“ (heute AP1.12d):** committete Actions + Tasks; **AP1.16c** ergänzt Vorgänger und blocked/ready. + +--- + +## 7. Ebenen bis Workpackage & Task (OM-Referenz) | Ebene | Typ | Plan-Outline-Knoten | Tiefe | |-------|-----|---------------------|-------| | 0 | Initiative | Wurzel | 1 | -| 1 | Project | rekursiv | max. 5 (bestehend) | -| 2 | RoadmapItem | Zielzustände (Graph/Liste) | parallel zur Struktur | +| 1 | RoadmapItem (Gate) | Zielzustände (Graph/Liste) | parallel; grob | +| 2 | Project | rekursiv | max. 5 (bestehend) | | 3 | BacklogItem | Eingang | flach unter Initiative | -| 4 | Action | Arbeit | unter Project **oder** Initiative | +| 4 | Action | Arbeit / Durchführungsplan | unter Project, Gate oder Initiative | | 5 | Task | (AP1.5d) | unter Action, UI max. ~3 | -**Plan-Baum „Arbeit“:** Zeigt committete Actions + Tasks (read-heavy); Anlage öffnet Modal. Filter: optional nur „ohne Project“ / „unter gewähltem Project“ (Scope `?project=`). +**Filter „Arbeit“:** optional nur „unter gewähltem Project“ / „unter aktivem Gate“ (`?project=`, Gate-Scope aus Kontrolle). --- -## 7. Scope-Breadcrumb (AP1.9b — Zuverlässigkeit) +## 8. Scope-Breadcrumb (AP1.9b — Zuverlässigkeit) **Problem:** Klick auf Vorhaben im Breadcrumb führt immer nach `/control/status` — in Planen falsch. @@ -230,7 +286,7 @@ Portfolio-Klick: Scope leeren, Modus beibehalten. --- -## 8. Implementierungspakete +## 9. Implementierungspakete ```text AP1.9b Scope-Breadcrumb modus-sensitiv; Scope-Sync bei Deep Links härten @@ -238,58 +294,70 @@ AP1.12a Plan-Outline Shell (Desktop Split / Mobile Drill-down) AP1.12b Modal-Edit für Project, Backlog, Initiative-Profil (Standardfelder) AP1.12c Reorder: sort_order API + DnD (Desktop) + ↑↓ (Mobile) AP1.12d Outline-Knoten „Arbeit“ (Actions listen, Link zu Detail) +AP1.16a action_dependencies, actions.sort_order, action_kind +AP1.16b execution_engine (ready/blocked/critical_path) +AP1.16c Plan-Outline: AP-Kanten, blocked/ready (nicht Gate-Designer) +AP1.16d planning_levels / planning_mode; Planning Debt Attention AP1.13a GateMapView read-only (Layout aus Dependencies) AP1.13b Kanten-CRUD auf Gate-Detail (kein Designer) -AP1.4d Graph Engine: parallel_group, edge_kind, blocked/ready -AP1.14 Plan-Snapshot / Ist-Overlay / Plan-Revision (vorgeschlagen) +AP1.4d Graph Engine: parallel_group, edge_kind, blocked/ready (Gates) +AP1.14 Plan-Snapshot / Ist-Overlay / Plan-Revision ✓ AP1.15 Zielzustands-Modellierungswerkzeug (Designer, Mitai-Pattern) AP1.15a Designer MVP (Pan/Zoom, Kanten, Layout) AP1.15b Designer UX (Drag-Kanten, Gate anlegen, Klick-Panel) -AP1.15c Join/Branch-Topologie (parallel_group, optional_branch, Engine) — deferred, vorbereitet +AP1.15c Join/Branch-Topologie (parallel_group, optional_branch, Engine) — deferred AP1.5d Tasks in Outline-Knoten „Arbeit“ AP1.10 Archetyp + EFS für Initiative-Profil-Modal (parallel ab 10a) +AP2.0f work_cycle — Zeitbox-Ebene (nach Execution-Graph sinnvoll kombinierbar) ``` **Empfohlene Reihenfolge:** ```text -9b → 12a → 12b → 10a/10c (Profil) → 12c → 12d → 5d → 13a → 13b +9b → 12a → 12b → 10a/10c (Profil) → 12c → 12d → 16a → 16b → 16c → 2.0d → 5d → 13a → 13b ``` --- -## 9. Nicht-Ziele (Scope Lock) +## 10. Nicht-Ziele (Scope Lock) - Kein Sprint-/Capacity-Planning in Planen (später Team-Modus-Erweiterung) - Kein Inline-CRUD aller Gates in der Outline-Liste - Kein Mitai-Workflow-Clone (**auch kein „Workflow-Graph“** — nur Zielzustands-Graph) - Kein Plan=Ist-Collapse (Status/Freitext ersetzt nicht Plan-Revision + Ist-Historie) +- Kein AP-Dependency-Graph im Zielzustands-Designer (→ AP1.16c in Outline „Arbeit“) +- Kein Micro-Gate pro Foundation-AP (→ Action-Graph unter grobem Gate) + - Kein Steering pro Project-Ebene (ADP Recursive Containers) --- -## 10. Erfolgskriterien (PO) +## 11. Erfolgskriterien (PO) 1. PO kann ein Vorhaben wählen und **in einer Ansicht** Struktur, Gates, Eingang und Arbeit sehen. 2. Bearbeitung läuft **nur im Modal** — Outline bleibt übersichtlich. 3. Project-Reihenfolge per DnD (Desktop) änderbar. 4. Gates mit Abhängigkeiten im **Zielzustands-Graph** darstellbar (AP1.13); Designer + Plan/Ist-Parallelität folgen (AP1.4d, AP1.14+). -5. Breadcrumb-Klick auf Vorhaben bleibt im Modus Planen. +5. Breadcrumb-Klick auf Vorhaben bleibt im Modus Planen. +6. Unter aktivem Gate: Durchführungsplan mit Vorgängern planbar — ohne Gate-Graph zu verfeinern (AP1.16). +7. Planning Debt sichtbar, wenn nächste Planungsebene fehlt (AP1.16d). --- -## 11. PO-Freigabe +## 12. PO-Freigabe - [ ] Outline statt drei Tabs (AP1.12) angenommen - [ ] Modal-Edit + DnD/↑↓ OK - [ ] GateMap / Zielzustands-Designer (Mitai-**Pattern** only) Scope OK - [ ] Plan-Graph vs. Ist-Overlay parallel (AP1.14) OK - [ ] AP1.9b Breadcrumb-Regel OK +- [x] Progressive Planung + Execution-Graph getrennt vom Gate-Graph (§6, AP1.16) — PO 2026-07-12 --- ## Referenzen +- `docs/architecture/ADP_Execution_Plan_and_Work_Package_Dependencies_v0.1.md` - `docs/architecture/ADP_AP1_10_Initiative_Archetypes_and_Entity_Field_System_v0.1.md` - `docs/architecture/ADP_AP1_9_PM_Work_Modes_Frontend_IA_v0.1.md` - `docs/reference/design-principles/mitai/PROMPT_ENGINE_DESIGN_PRINCIPLES.md` (WorkflowEditor-Referenz) diff --git a/docs/product/Kairo_Status_Review_and_Next_Steps_v0.1.md b/docs/product/Kairo_Status_Review_and_Next_Steps_v0.1.md index 65dc921..1c8a40b 100644 --- a/docs/product/Kairo_Status_Review_and_Next_Steps_v0.1.md +++ b/docs/product/Kairo_Status_Review_and_Next_Steps_v0.1.md @@ -113,8 +113,10 @@ Priorität nach MVP v0.3 §9, Review 2026-07-11 und Dogfooding-Vorbereitung: | **D1** | **Dogfooding R1** | Vorhaben „Kairo-Jinkendo“ in Kairo anlegen (Spiegel, manuell) | D0 | | **1** | **AP1.9c** | Cockpit: Signale auf Portfolio-Kacheln | — | | **2** | **AP1.9b/d** | Scope-Breadcrumb, Kontrolle-Polish | 1 optional parallel | -| **3** | **AP2.0d** | Next-Action-Strategien: maturity, sequential, recurring, queue | — | +| **2b** | **AP1.16a–b** | Execution-Graph Schema + Engine (`ready_actions`) | **◐ Code** — Deploy | +| **3** | **AP2.0d** | Next-Action-Strategien: maturity, sequential, recurring, queue | 2b | | **4** | **Dogfooding R2** | Ist pflegen, Evidence mit Gitea-Links, Gate-Fortschritt | D1 | +| **4b** | **AP1.16c–d** | Plan-Outline AP-Kanten, Planning Debt | 2b | | **5** | **AP0.10d / AP2.1** | Validation B2b: Leitfrage-Test auf Pi | 3, 4 | | **6** | **AP2.0f** | `work_cycle` / Sprint auf Product (B3 minimal) | 5 optional | | **7** | **AP1.5d Rest** | Task-Detail, Roll-up-UI | parallel möglich | @@ -123,7 +125,7 @@ Priorität nach MVP v0.3 §9, Review 2026-07-11 und Dogfooding-Vorbereitung: | **10** | **MCP-Adapter** | Cursor-Agent auf Operational API | 8 | ```text -D0 DOC ──► D1 Dogfooding anlegen ──► 1 AP1.9c ──► 3 AP2.0d ──► 4 Ist spiegeln +D0 DOC ──► D1 Dogfooding anlegen ──► 1 AP1.9c ──► 2b AP1.16 ──► 3 AP2.0d ──► 4 Ist spiegeln └──► 5 Validation ──► 8 AP1.7 ──► 10 MCP ``` @@ -153,7 +155,9 @@ Minimum für **Stufe-A-Abnahfe B2b**: |----------|----------| | `Kairo_Implementation_Truth_Table_v0.1.md` | Vollständig auf Stand 2026-07-11 | | `Kairo_Current_State_Gap_Analysis_v0.2.md` | → v0.3, Gap-Matrix aktualisiert | -| `Kairo_Corrected_MVP_Roadmap_v0.2.md` | Phasen §2, Erledigt §3, Nächste §4, Versionen §7 | +| `Kairo_Corrected_MVP_Roadmap_v0.2.md` | Phasen §2, AP1.16 §1.5/§4, Versionen §7 | +| `ADP_Execution_Plan_and_Work_Package_Dependencies_v0.1.md` | **neu** — Durchführungsplan, progressive Planung | +| `Kairo_Plan_Mode_Design_v0.1.md` | §6–7 Progressive Planung, AP1.16 Pakete | | `Kairo_Vision_and_Product_Direction_v0.2.md` | §10 Verweis auf Truth Table (Kurzstand) | | `Kairo_Dogfooding_Mirror_Steering_v0.1.md` | **neu** — Spiegel-Steuerung + Gitea | diff --git a/docs/sprints/Sprint1_AP1_16_Execution_Plan_v0.1.md b/docs/sprints/Sprint1_AP1_16_Execution_Plan_v0.1.md new file mode 100644 index 0000000..3e1ede8 --- /dev/null +++ b/docs/sprints/Sprint1_AP1_16_Execution_Plan_v0.1.md @@ -0,0 +1,129 @@ +# AP1.16 — Durchführungsplan & Work-Package-Abhängigkeiten + +**Stand:** 2026-07-12 +**PO-Freigabe:** `ADP_Execution_Plan_and_Work_Package_Dependencies_v0.1.md` (2026-07-12) +**Version (Ziel):** `0.19.0-ap1.16` +**Voraussetzung:** AP1.5 (Actions), AP1.6 (Gate-Link), AP1.12d (Outline „Arbeit“) + +--- + +## Ziel + +Ausführungsreihenfolge von **Arbeitspaketen** modellieren — **getrennt** vom Gate-Graph. Rollende Planung: unter aktivem Gate-Horizont APs mit Vorgängern, `ready`/`blocked` Read Models, optional Planning Debt. + +--- + +## PO-Regeln (verbindlich) + +### Abgrenzung + +| | Gate-Graph (AP1.4d/15) | Execution-Graph (AP1.16) | +|---|------------------------|---------------------------| +| Knoten | `RoadmapItem` | `Action` | +| UI | Zielzustands-Designer | Plan-Outline „Arbeit“ | +| Verify | Kriterien + Evidence | Action `done` | +| Granularität | grob (5–15) | fein (10–100+) | + +### Kanten-Semantik (`action_dependencies`) + +| `dependency_kind` | Bedeutung | +|-------------------|-----------| +| `requires` | `successor` startet erst, wenn `predecessor` **done** | +| `blocks` | `predecessor` offen → `successor` **blocked** | +| `relates` | nur Anzeige, kein Block | + +Kanten-Richtung in DB: `predecessor_action_id` → `successor_action_id` (Vorgänger muss erfüllt sein, damit Nachfolger ready). + +### Scope + +- Beide Actions **gleiche Initiative** +- Zyklen verboten (API lehnt ab) +- Execution Engine steuert **keinen** Lifecycle automatisch — nur Read Models + +### Planning Actions + +- `action_kind`: `delivery` (Default), `planning`, `review` +- Meta-APs für explizite Planungsschritte (Archetyp `planning_mode=explicit`) + +--- + +## Lieferungen + +### AP1.16a — Schema + +| # | Inhalt | +|---|--------| +| 1 | Migration `023_action_execution_plan.sql` | +| 2 | `actions.sort_order INT NOT NULL DEFAULT 0` | +| 3 | `actions.action_kind` CHECK (`delivery`, `planning`, `review`) | +| 4 | Tabelle `action_dependencies` + Indizes | +| 5 | Action-Service: Felder in CRUD/SELECT | + +### AP1.16b — Engine & API + +| # | Inhalt | +|---|--------| +| 6 | `backend/steering/graph/execution_engine.py` | +| 7 | `services/execution_plan.py` — Deps CRUD, Cycle-Check | +| 8 | `GET /api/initiatives/{id}/execution/graph-state` | +| 9 | `GET/POST/DELETE …/execution/dependencies` | +| 10 | pytest: Engine + Cycle-Detection (Unit) | + +### AP1.16c — Frontend (folgt) + +| # | Inhalt | +|---|--------| +| 11 | Plan-Outline „Arbeit“: blocked/ready Badges | +| 12 | Vorgänger-Kanten (Liste oder Mini-Graph) | +| 13 | Dependency anlegen/löschen im Action-Kontext | + +### AP1.16d — Steuerung (folgt) + +| # | Inhalt | +|---|--------| +| 14 | Method Profile: `planning_levels`, `planning_mode` | +| 15 | Planning Debt in Attention / Cockpit | +| 16 | AP2.0d: Next-Action nutzt `ready_actions` | + +--- + +## Explizit nicht in AP1.16a–b + +- Gate-Designer-Änderungen +- `project_dependencies` (Struktur-Ebene B) +- `RoadmapItem(type=feature)` als WP-Knoten (Post-MVP) +- Lifecycle-Automatik aus Graph +- Gantt / Kalender + +--- + +## Abnahme + +### 16a–b (Backend) + +- [ ] Migration 023 auf Pi angewendet +- [ ] Zwei Actions, `requires`-Kante: Nachfolger blocked bis Vorgänger `done` +- [ ] Zyklus A→B→A wird abgelehnt +- [ ] `sort_order`-Fallback ohne Kanten (sequenziell wie Gate-Graph) +- [ ] `graph-state` liefert `ready_actions`, `blocked_actions`, `critical_path` +- [ ] `planning_debt` wenn aktives Gate ohne verknüpfte Actions +- [ ] pytest grün (Pi) + +### 16c–d (später) + +- [ ] Outline zeigt blocked/ready +- [ ] Planning Debt sichtbar in Attention + +--- + +## Reihenfolge + +```text +16a (Schema) → 16b (Engine + API) → 16c (UI) → 16d (Profile + Attention) + ↓ + AP2.0d (Strategien) +``` + +--- + +*Siehe: `ADP_Execution_Plan_and_Work_Package_Dependencies_v0.1.md`, `Kairo_Plan_Mode_Design_v0.1.md` §6*