AP1.16a-b: Execution-Graph für Arbeitspaket-Abhängigkeiten.
All checks were successful
Deploy Development / deploy (push) Successful in 48s
Test Suite / pytest-backend (push) Successful in 2m20s
Test Suite / lint-backend (push) Successful in 3s
Test Suite / compose-smoke (push) Has been skipped
Test Suite / k6 /api/health Baseline (push) Successful in 19s
Test Suite / playwright-smoke (push) Successful in 14s
All checks were successful
Deploy Development / deploy (push) Successful in 48s
Test Suite / pytest-backend (push) Successful in 2m20s
Test Suite / lint-backend (push) Successful in 3s
Test Suite / compose-smoke (push) Has been skipped
Test Suite / k6 /api/health Baseline (push) Successful in 19s
Test Suite / playwright-smoke (push) Successful in 14s
Migration 023, Engine, API und ADP für Durchführungsplan getrennt vom Gate-Graph; Docs und Sprint-Assignment synchronisiert. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
a1b353fcee
commit
1166a0c7f1
|
|
@ -65,6 +65,7 @@ from routers import ( # noqa: E402
|
||||||
decisions,
|
decisions,
|
||||||
entity_archetypes,
|
entity_archetypes,
|
||||||
evidence,
|
evidence,
|
||||||
|
execution_plan,
|
||||||
features,
|
features,
|
||||||
initiatives,
|
initiatives,
|
||||||
journey,
|
journey,
|
||||||
|
|
@ -100,6 +101,7 @@ app.include_router(roadmap.initiative_router)
|
||||||
app.include_router(roadmap.items_router)
|
app.include_router(roadmap.items_router)
|
||||||
app.include_router(roadmap.deps_router)
|
app.include_router(roadmap.deps_router)
|
||||||
app.include_router(roadmap.criteria_router)
|
app.include_router(roadmap.criteria_router)
|
||||||
|
app.include_router(execution_plan.initiative_router)
|
||||||
app.include_router(evidence.router)
|
app.include_router(evidence.router)
|
||||||
app.include_router(decisions.router)
|
app.include_router(decisions.router)
|
||||||
app.include_router(reviews.router)
|
app.include_router(reviews.router)
|
||||||
|
|
|
||||||
32
backend/migrations/023_action_execution_plan.sql
Normal file
32
backend/migrations/023_action_execution_plan.sql
Normal file
|
|
@ -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);
|
||||||
|
|
@ -29,6 +29,8 @@ class ActionUpdateRequest(BaseModel):
|
||||||
clear_project: bool = False
|
clear_project: bool = False
|
||||||
roadmap_item_id: Optional[str] = None
|
roadmap_item_id: Optional[str] = None
|
||||||
clear_roadmap_item: bool = False
|
clear_roadmap_item: bool = False
|
||||||
|
sort_order: Optional[int] = None
|
||||||
|
action_kind: Optional[Literal["delivery", "planning", "review"]] = None
|
||||||
|
|
||||||
|
|
||||||
class TaskCreateRequest(BaseModel):
|
class TaskCreateRequest(BaseModel):
|
||||||
|
|
@ -135,6 +137,8 @@ def update_action(
|
||||||
clear_project=body.clear_project,
|
clear_project=body.clear_project,
|
||||||
roadmap_item_id=body.roadmap_item_id,
|
roadmap_item_id=body.roadmap_item_id,
|
||||||
clear_roadmap_item=body.clear_roadmap_item,
|
clear_roadmap_item=body.clear_roadmap_item,
|
||||||
|
sort_order=body.sort_order,
|
||||||
|
action_kind=body.action_kind,
|
||||||
)
|
)
|
||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
||||||
|
|
|
||||||
119
backend/routers/execution_plan.py
Normal file
119
backend/routers/execution_plan.py
Normal file
|
|
@ -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
|
||||||
|
|
@ -67,6 +67,8 @@ class ActionCreateRequest(BaseModel):
|
||||||
project_id: Optional[str] = None
|
project_id: Optional[str] = None
|
||||||
roadmap_item_id: Optional[str] = None
|
roadmap_item_id: Optional[str] = None
|
||||||
assigned_actor_ids: list[str] = Field(default_factory=list)
|
assigned_actor_ids: list[str] = Field(default_factory=list)
|
||||||
|
sort_order: int = 0
|
||||||
|
action_kind: Literal["delivery", "planning", "review"] = "delivery"
|
||||||
|
|
||||||
|
|
||||||
class BlockerCreateRequest(BaseModel):
|
class BlockerCreateRequest(BaseModel):
|
||||||
|
|
@ -331,6 +333,8 @@ def create_initiative_action(
|
||||||
project_id=body.project_id,
|
project_id=body.project_id,
|
||||||
roadmap_item_id=body.roadmap_item_id,
|
roadmap_item_id=body.roadmap_item_id,
|
||||||
assigned_actor_ids=body.assigned_actor_ids,
|
assigned_actor_ids=body.assigned_actor_ids,
|
||||||
|
sort_order=body.sort_order,
|
||||||
|
action_kind=body.action_kind,
|
||||||
user_id=ctx.user_id,
|
user_id=ctx.user_id,
|
||||||
)
|
)
|
||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,11 @@ OPEN_ACTION_STATUSES = frozenset(
|
||||||
|
|
||||||
_ACTION_COLUMNS = """
|
_ACTION_COLUMNS = """
|
||||||
id, tenant_id, initiative_id, project_id, roadmap_item_id, title, description,
|
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]:
|
def _serialize_row(row: dict[str, Any]) -> dict[str, Any]:
|
||||||
result = dict(row)
|
result = dict(row)
|
||||||
|
|
@ -43,6 +45,11 @@ def _serialize_row(row: dict[str, Any]) -> dict[str, Any]:
|
||||||
return result
|
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:
|
def _validate_action_status(status: str) -> None:
|
||||||
if status not in ACTION_STATUSES:
|
if status not in ACTION_STATUSES:
|
||||||
raise ValueError(f"Ungültiger Action-Status: {status}")
|
raise ValueError(f"Ungültiger Action-Status: {status}")
|
||||||
|
|
@ -138,6 +145,8 @@ def create_action(
|
||||||
project_id: Optional[str] = None,
|
project_id: Optional[str] = None,
|
||||||
roadmap_item_id: Optional[str] = None,
|
roadmap_item_id: Optional[str] = None,
|
||||||
assigned_actor_ids: Optional[list[str]] = None,
|
assigned_actor_ids: Optional[list[str]] = None,
|
||||||
|
sort_order: int = 0,
|
||||||
|
action_kind: str = "delivery",
|
||||||
user_id: Optional[str] = None,
|
user_id: Optional[str] = None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
title = title.strip()
|
title = title.strip()
|
||||||
|
|
@ -145,6 +154,7 @@ def create_action(
|
||||||
raise ValueError("Titel ist erforderlich")
|
raise ValueError("Titel ist erforderlich")
|
||||||
_validate_action_status(status)
|
_validate_action_status(status)
|
||||||
_validate_priority(priority)
|
_validate_priority(priority)
|
||||||
|
_validate_action_kind(action_kind)
|
||||||
if not get_initiative(tenant_id=tenant_id, initiative_id=initiative_id):
|
if not get_initiative(tenant_id=tenant_id, initiative_id=initiative_id):
|
||||||
raise ValueError("Initiative nicht gefunden")
|
raise ValueError("Initiative nicht gefunden")
|
||||||
_validate_project_in_initiative(
|
_validate_project_in_initiative(
|
||||||
|
|
@ -169,9 +179,9 @@ def create_action(
|
||||||
f"""
|
f"""
|
||||||
INSERT INTO actions (
|
INSERT INTO actions (
|
||||||
tenant_id, initiative_id, project_id, roadmap_item_id, title, description,
|
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}
|
RETURNING {_ACTION_COLUMNS}
|
||||||
""",
|
""",
|
||||||
(
|
(
|
||||||
|
|
@ -184,6 +194,8 @@ def create_action(
|
||||||
status,
|
status,
|
||||||
priority,
|
priority,
|
||||||
due_at,
|
due_at,
|
||||||
|
sort_order,
|
||||||
|
action_kind,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
row = _serialize_row(dict(cur.fetchone()))
|
row = _serialize_row(dict(cur.fetchone()))
|
||||||
|
|
@ -228,7 +240,7 @@ def list_actions_for_initiative(
|
||||||
SELECT {_ACTION_COLUMNS}
|
SELECT {_ACTION_COLUMNS}
|
||||||
FROM actions
|
FROM actions
|
||||||
WHERE tenant_id = %s AND initiative_id = %s
|
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),
|
(tenant_id, initiative_id),
|
||||||
)
|
)
|
||||||
|
|
@ -274,6 +286,8 @@ def update_action(
|
||||||
clear_project: bool = False,
|
clear_project: bool = False,
|
||||||
roadmap_item_id: Optional[str] = None,
|
roadmap_item_id: Optional[str] = None,
|
||||||
clear_roadmap_item: bool = False,
|
clear_roadmap_item: bool = False,
|
||||||
|
sort_order: Optional[int] = None,
|
||||||
|
action_kind: Optional[str] = None,
|
||||||
) -> Optional[dict[str, Any]]:
|
) -> Optional[dict[str, Any]]:
|
||||||
existing = get_action(tenant_id=tenant_id, action_id=action_id)
|
existing = get_action(tenant_id=tenant_id, action_id=action_id)
|
||||||
if not existing:
|
if not existing:
|
||||||
|
|
@ -331,6 +345,13 @@ def update_action(
|
||||||
conn.close()
|
conn.close()
|
||||||
updates.append("roadmap_item_id = %s")
|
updates.append("roadmap_item_id = %s")
|
||||||
params.append(roadmap_item_id)
|
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:
|
if not updates:
|
||||||
return existing
|
return existing
|
||||||
|
|
|
||||||
225
backend/services/execution_plan.py
Normal file
225
backend/services/execution_plan.py
Normal file
|
|
@ -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
|
||||||
246
backend/steering/graph/execution_engine.py
Normal file
246
backend/steering/graph/execution_engine.py
Normal file
|
|
@ -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,
|
||||||
|
)
|
||||||
116
backend/tests/test_ap16_execution_graph.py
Normal file
116
backend/tests/test_ap16_execution_graph.py
Normal file
|
|
@ -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"}
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
APP_VERSION = "0.18.0-ap2.0"
|
APP_VERSION = "0.18.0-ap2.0"
|
||||||
DB_SCHEMA_VERSION = "019"
|
DB_SCHEMA_VERSION = "023"
|
||||||
APP_NAME = "jinkendo-kairo"
|
APP_NAME = "jinkendo-kairo"
|
||||||
|
|
|
||||||
|
|
@ -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`*
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
## Corrected MVP Roadmap v0.2
|
## Corrected MVP Roadmap v0.2
|
||||||
|
|
||||||
**Status:** führende Produkt-Roadmap (AP-Historie + nächste Schritte)
|
**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`
|
**Ersetzt:** `Kairo_Corrected_MVP_Roadmap_v0.1.md`
|
||||||
**Vision:** `Kairo_Vision_and_Product_Direction_v0.2.md`
|
**Vision:** `Kairo_Vision_and_Product_Direction_v0.2.md`
|
||||||
**MVP-Nordstern:** `Kairo_MVP_Definition_v0.3.md` (führend für Abnahfe)
|
**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
|
## 2. Phasenübersicht
|
||||||
|
|
||||||
```text
|
```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 H3 Plan Outline AP1.12 + AP1.10 ◐ 12a–d, 10c
|
||||||
Phase I Gate-Graph AP1.13 + AP1.15 ◐ 13a/b, 15a–c
|
Phase I Gate-Graph AP1.13 + AP1.15 ◐ 13a/b, 15a–c
|
||||||
Phase I2 Plan/Ist Snapshots AP1.14 ✓
|
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 J Portfolio AP1.8 ◐ 8a ✓ / 8b deferred
|
||||||
Phase K Archetyp-Steuerung AP2.0 ◐ 2.0a–c ✓ / → 2.0d–f
|
Phase K Archetyp-Steuerung AP2.0 ◐ 2.0a–c ✓ / → 2.0d–f
|
||||||
Phase L Agent Interface AP1.7 ○
|
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
|
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 |
|
| D0 | DOC-Sync (Truth Table, Gap, Review) | ✓ | eine Wahrheit |
|
||||||
| D1 | Dogfooding R1 — Kairo-Jinkendo in Kairo | **→ nächstes** | B2b-Validation |
|
| D1 | Dogfooding R1 — Kairo-Jinkendo in Kairo | **→ nächstes** | B2b-Validation |
|
||||||
| 1 | AP1.9c Cockpit-Signale | offen | MVP §5.7 |
|
| 1 | AP1.9c Cockpit-Signale | offen | MVP §5.7 |
|
||||||
| 2 | AP2.0d Next-Action-Strategien | offen | MVP Stufe A |
|
| 2 | AP1.16a–b Execution-Graph (Schema + Engine) | **◐ Code** | **vor** AP2.0d; Remote-Verifikation nach Deploy |
|
||||||
| 3 | Dogfooding R2 — Ist + Gitea-Evidence | offen | Fortschritt sichtbar |
|
| 3 | AP2.0d Next-Action-Strategien | offen | MVP Stufe A; nutzt `ready_actions` |
|
||||||
| 4 | AP0.10d / AP2.1 Validation B2b | offen | MVP-Urteil |
|
| 4 | AP1.16c–d Plan-Outline-Kanten + Planning Debt | offen | nach 16a–b |
|
||||||
| 5 | AP2.0f work_cycle (B3 minimal) | offen | Sprint-Zeitbox |
|
| 5 | Dogfooding R2 — Ist + Gitea-Evidence | offen | Fortschritt sichtbar |
|
||||||
| 6 | AP1.7 Operational API | offen | MCP-Voraussetzung |
|
| 6 | AP0.10d / AP2.1 Validation B2b | offen | MVP-Urteil |
|
||||||
| 7 | Gitea-Webhook + MCP | deferred | Schicht 4 |
|
| 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 ✓
|
**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
|
### AP1.5d — Rekursive Tasks & Roll-up
|
||||||
|
|
||||||
**Ziel:** Task-Baum (`parent_task_id`); Mensch UI max. ~3 Ebenen; Agent tiefer.
|
**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.16.x ✓ AP1.9a, AP1.12, AP1.13 (teilweise)
|
||||||
0.17.x ✓ AP1.14, AP1.15 (teilweise)
|
0.17.x ✓ AP1.14, AP1.15 (teilweise)
|
||||||
0.18.0-ap2.0 ✓ Archetyp-Slice AP2.0
|
0.18.0-ap2.0 ✓ Archetyp-Slice AP2.0
|
||||||
0.18.x → AP1.9c, Dogfooding, AP2.0d — NÄCHSTES
|
0.18.x → AP1.9c, Dogfooding R1 — NÄCHSTES
|
||||||
0.19.x → AP2.0f work_cycle, AP2.1 Validation
|
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
|
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`*
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# Kairo — Implementation Truth Table v0.1
|
# Kairo — Implementation Truth Table v0.1
|
||||||
|
|
||||||
**Status:** living document — bei jedem AP aktualisieren
|
**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**
|
**Zweck:** Ehrliche Trennung von **implementiert**, **teilweise**, **nur API/Schema**, **nur Dokumentiert**
|
||||||
|
|
||||||
Verhindert, dass Zielbild-Dokumente als Ist-Stand gelesen werden.
|
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 |
|
| Capabilities | ✓ | Keine Objekt-Sichtbarkeit |
|
||||||
| Auth / Session | ✓ | |
|
| Auth / Session | ✓ | |
|
||||||
| Audit (Auth/Admin) | ◐ | Nicht für alle OM-Events |
|
| 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` |
|
| Initiative | ✓ | inkl. `archetype_key`, `portfolio_rank` |
|
||||||
| Project | ◐ | AP1.5 + AP1.5c: Baum, Blatt-Regel; `archetype_key` Mirror AP2.0b |
|
| 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 | ✓ | |
|
| 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` |
|
| BacklogItem | ✓ | Gate-Zuordnung AP1.6, `sort_order` |
|
||||||
| Blocker | ✓ | `action_id` optional |
|
| Blocker | ✓ | `action_id` optional |
|
||||||
| Milestone | ◐ | Legacy-Tabelle MVP-Brücke; RoadmapItem bevorzugt |
|
| 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 ◐ |
|
| Task (unter Action) | ◐ | AP1.5d: `parent_task_id`, Baum-API, Outline; Roll-up-UI ◐ |
|
||||||
| Roadmap | ◐ | 1 pro Initiative; Migration 010 |
|
| Roadmap | ◐ | 1 pro Initiative; Migration 010 |
|
||||||
| RoadmapItem | ◐ | CRUD + Detail; Verify AP1.4b |
|
| 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 |
|
| RoadmapItem Criteria | ◐ | Checkliste, waive/defer, Reopen AP1.4b/c |
|
||||||
| Plan-Ist-Verknüpfung | ◐ | Links, Kriterien, Journey AP1.6; Snapshots AP1.14 |
|
| Plan-Ist-Verknüpfung | ◐ | Links, Kriterien, Journey AP1.6; Snapshots AP1.14 |
|
||||||
| Plan-Snapshots | ◐ | Migration 021, Diff-View Kontrolle AP1.14 |
|
| Plan-Snapshots | ◐ | Migration 021, Diff-View Kontrolle AP1.14 |
|
||||||
| Entity Field System | ◐ | Migration 017, Seeds AP1.10b/AP2.0b |
|
| Entity Field System | ◐ | Migration 017, Seeds AP1.10b/AP2.0b |
|
||||||
| Initiative Archetypes | ◐ | Migration 016, Registry AP2.0b |
|
| Initiative Archetypes | ◐ | Migration 016, Registry AP2.0b |
|
||||||
| Method Profiles (Code-Seeds) | ◐ | `product.kairo_dev`, Kumite, Buch — 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 |
|
| `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 Map (read-only) | ✓ | AP1.13a |
|
||||||
| Gate Dependencies pflegen | ◐ | AP1.13b Detail |
|
| Gate Dependencies pflegen | ◐ | AP1.13b Detail |
|
||||||
| Zielzustands-Designer | ◐ | AP1.15a–c; OR deferred |
|
| 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 |
|
| Profil-Modal (Archetyp/EFS) | ◐ | AP1.10c |
|
||||||
| Modal-Bearbeitung (Gates/Profil) | ◐ | viele Sektionen noch Inline-CRUD |
|
| Modal-Bearbeitung (Gates/Profil) | ◐ | viele Sektionen noch Inline-CRUD |
|
||||||
| Admin-UI | ✗ | |
|
| 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 |
|
| DoD / prüfbare Kriterien | ◐ | `roadmap_item_criteria`; UI AP1.4c |
|
||||||
| Zieltermin | ◐ | `target_date` |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| AP | Erwartung |
|
||||||
|----|-----------|
|
|----|-----------|
|
||||||
|
| AP1.16a–b | Action deps, Execution Engine ✗→◐ (Deploy) |
|
||||||
|
| AP1.16c–d | Outline-Kanten, Planning Debt ✗→◐ |
|
||||||
| AP1.9c | Cockpit-Signale ◐→✓ |
|
| AP1.9c | Cockpit-Signale ◐→✓ |
|
||||||
| AP2.0d | Strategien ○→◐ |
|
| AP2.0d | Strategien ○→◐ |
|
||||||
| Dogfooding R1 | Referenz-Vorhaben in Alltag ◐ |
|
| Dogfooding R1 | Referenz-Vorhaben in Alltag ◐ |
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
# Kairo — Plan-Modus Design v0.1
|
# Kairo — Plan-Modus Design v0.1
|
||||||
|
|
||||||
**Status:** PO-Arbeitsentwurf (Entscheidung ausstehend)
|
**Status:** PO-Arbeitsentwurf (Execution-Plan §6–7 PO 2026-07-12)
|
||||||
**Stand:** 2026-07-10
|
**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`
|
**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
|
**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 |
|
| Ebene | Typ | Plan-Outline-Knoten | Tiefe |
|
||||||
|-------|-----|---------------------|-------|
|
|-------|-----|---------------------|-------|
|
||||||
| 0 | Initiative | Wurzel | 1 |
|
| 0 | Initiative | Wurzel | 1 |
|
||||||
| 1 | Project | rekursiv | max. 5 (bestehend) |
|
| 1 | RoadmapItem (Gate) | Zielzustände (Graph/Liste) | parallel; grob |
|
||||||
| 2 | RoadmapItem | Zielzustände (Graph/Liste) | parallel zur Struktur |
|
| 2 | Project | rekursiv | max. 5 (bestehend) |
|
||||||
| 3 | BacklogItem | Eingang | flach unter Initiative |
|
| 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 |
|
| 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.
|
**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
|
```text
|
||||||
AP1.9b Scope-Breadcrumb modus-sensitiv; Scope-Sync bei Deep Links härten
|
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.12b Modal-Edit für Project, Backlog, Initiative-Profil (Standardfelder)
|
||||||
AP1.12c Reorder: sort_order API + DnD (Desktop) + ↑↓ (Mobile)
|
AP1.12c Reorder: sort_order API + DnD (Desktop) + ↑↓ (Mobile)
|
||||||
AP1.12d Outline-Knoten „Arbeit“ (Actions listen, Link zu Detail)
|
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.13a GateMapView read-only (Layout aus Dependencies)
|
||||||
AP1.13b Kanten-CRUD auf Gate-Detail (kein Designer)
|
AP1.13b Kanten-CRUD auf Gate-Detail (kein Designer)
|
||||||
AP1.4d Graph Engine: parallel_group, edge_kind, blocked/ready
|
AP1.4d Graph Engine: parallel_group, edge_kind, blocked/ready (Gates)
|
||||||
AP1.14 Plan-Snapshot / Ist-Overlay / Plan-Revision (vorgeschlagen)
|
AP1.14 Plan-Snapshot / Ist-Overlay / Plan-Revision ✓
|
||||||
AP1.15 Zielzustands-Modellierungswerkzeug (Designer, Mitai-Pattern)
|
AP1.15 Zielzustands-Modellierungswerkzeug (Designer, Mitai-Pattern)
|
||||||
AP1.15a Designer MVP (Pan/Zoom, Kanten, Layout)
|
AP1.15a Designer MVP (Pan/Zoom, Kanten, Layout)
|
||||||
AP1.15b Designer UX (Drag-Kanten, Gate anlegen, Klick-Panel)
|
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.5d Tasks in Outline-Knoten „Arbeit“
|
||||||
AP1.10 Archetyp + EFS für Initiative-Profil-Modal (parallel ab 10a)
|
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:**
|
**Empfohlene Reihenfolge:**
|
||||||
|
|
||||||
```text
|
```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 Sprint-/Capacity-Planning in Planen (später Team-Modus-Erweiterung)
|
||||||
- Kein Inline-CRUD aller Gates in der Outline-Liste
|
- Kein Inline-CRUD aller Gates in der Outline-Liste
|
||||||
- Kein Mitai-Workflow-Clone (**auch kein „Workflow-Graph“** — nur Zielzustands-Graph)
|
- Kein Mitai-Workflow-Clone (**auch kein „Workflow-Graph“** — nur Zielzustands-Graph)
|
||||||
- Kein Plan=Ist-Collapse (Status/Freitext ersetzt nicht Plan-Revision + Ist-Historie)
|
- 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)
|
- 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.
|
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.
|
2. Bearbeitung läuft **nur im Modal** — Outline bleibt übersichtlich.
|
||||||
3. Project-Reihenfolge per DnD (Desktop) änderbar.
|
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+).
|
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
|
- [ ] Outline statt drei Tabs (AP1.12) angenommen
|
||||||
- [ ] Modal-Edit + DnD/↑↓ OK
|
- [ ] Modal-Edit + DnD/↑↓ OK
|
||||||
- [ ] GateMap / Zielzustands-Designer (Mitai-**Pattern** only) Scope OK
|
- [ ] GateMap / Zielzustands-Designer (Mitai-**Pattern** only) Scope OK
|
||||||
- [ ] Plan-Graph vs. Ist-Overlay parallel (AP1.14) OK
|
- [ ] Plan-Graph vs. Ist-Overlay parallel (AP1.14) OK
|
||||||
- [ ] AP1.9b Breadcrumb-Regel OK
|
- [ ] AP1.9b Breadcrumb-Regel OK
|
||||||
|
- [x] Progressive Planung + Execution-Graph getrennt vom Gate-Graph (§6, AP1.16) — PO 2026-07-12
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Referenzen
|
## 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_10_Initiative_Archetypes_and_Entity_Field_System_v0.1.md`
|
||||||
- `docs/architecture/ADP_AP1_9_PM_Work_Modes_Frontend_IA_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)
|
- `docs/reference/design-principles/mitai/PROMPT_ENGINE_DESIGN_PRINCIPLES.md` (WorkflowEditor-Referenz)
|
||||||
|
|
|
||||||
|
|
@ -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 |
|
| **D1** | **Dogfooding R1** | Vorhaben „Kairo-Jinkendo“ in Kairo anlegen (Spiegel, manuell) | D0 |
|
||||||
| **1** | **AP1.9c** | Cockpit: Signale auf Portfolio-Kacheln | — |
|
| **1** | **AP1.9c** | Cockpit: Signale auf Portfolio-Kacheln | — |
|
||||||
| **2** | **AP1.9b/d** | Scope-Breadcrumb, Kontrolle-Polish | 1 optional parallel |
|
| **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 |
|
| **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 |
|
| **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 |
|
| **6** | **AP2.0f** | `work_cycle` / Sprint auf Product (B3 minimal) | 5 optional |
|
||||||
| **7** | **AP1.5d Rest** | Task-Detail, Roll-up-UI | parallel möglich |
|
| **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 |
|
| **10** | **MCP-Adapter** | Cursor-Agent auf Operational API | 8 |
|
||||||
|
|
||||||
```text
|
```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
|
└──► 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_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_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_Vision_and_Product_Direction_v0.2.md` | §10 Verweis auf Truth Table (Kurzstand) |
|
||||||
| `Kairo_Dogfooding_Mirror_Steering_v0.1.md` | **neu** — Spiegel-Steuerung + Gitea |
|
| `Kairo_Dogfooding_Mirror_Steering_v0.1.md` | **neu** — Spiegel-Steuerung + Gitea |
|
||||||
|
|
||||||
|
|
|
||||||
129
docs/sprints/Sprint1_AP1_16_Execution_Plan_v0.1.md
Normal file
129
docs/sprints/Sprint1_AP1_16_Execution_Plan_v0.1.md
Normal file
|
|
@ -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*
|
||||||
Loading…
Reference in New Issue
Block a user