Kairo-Jinkendo/backend/steering/read_models/tech_debt.py
Lars a5154cbd3b
Some checks failed
Deploy Development / deploy (push) Successful in 47s
Test Suite / pytest-backend (push) Failing after 4m15s
Test Suite / k6 /api/health Baseline (push) Has been skipped
Test Suite / playwright-smoke (push) Has been skipped
Test Suite / lint-backend (push) Successful in 2s
Test Suite / compose-smoke (push) Has been skipped
feat(steering): Kernel v0.4 Agent-Slots, Tech Debt und Review-Attention
K-Ext-4/P8 deklarative Agent-Slots; P7 tech_debt Read Model und Vokabular; Review-Attention in den Kernel verlagert.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-27 11:54:48 +02:00

89 lines
2.9 KiB
Python

"""Tech debt summary read model — P7."""
from __future__ import annotations
from datetime import datetime, timedelta, timezone
from typing import Any
from steering.eval_context import SteeringEvalContext
_OPEN_BACKLOG = frozenset({"new", "triaged", "accepted"})
_OPEN_ACTION = frozenset({"open", "ready", "in_progress", "blocked", "review_required"})
def _parse_ts(value: Any) -> datetime | None:
if not value:
return None
if isinstance(value, datetime):
return value if value.tzinfo else value.replace(tzinfo=timezone.utc)
try:
text = str(value).replace("Z", "+00:00")
parsed = datetime.fromisoformat(text)
return parsed if parsed.tzinfo else parsed.replace(tzinfo=timezone.utc)
except ValueError:
return None
def compute_tech_debt_summary(ctx: SteeringEvalContext) -> dict[str, Any]:
vocabulary = ctx.backlog_vocabulary
if "tech_debt" not in (vocabulary.get("kinds") or []):
return {
"enabled": False,
"backlog_items": [],
"actions": [],
"total_open": 0,
"stale_count": 0,
}
stale_days = int(ctx.agent_slot_config.get("stale_debt_days") or 30)
cutoff = datetime.now(timezone.utc) - timedelta(days=stale_days)
backlog_items: list[dict[str, Any]] = []
for item in ctx.backlog_items:
if item.get("item_kind") != "tech_debt":
continue
if item.get("status") not in _OPEN_BACKLOG:
continue
created = _parse_ts(item.get("created_at"))
stale = created is not None and created < cutoff
backlog_items.append(
{
"id": str(item["id"]),
"title": item.get("title") or "Technische Schuld",
"status": item.get("status"),
"stale": stale,
"created_at": item.get("created_at"),
}
)
actions: list[dict[str, Any]] = []
for action in ctx.actions:
if action.get("action_kind") != "tech_debt":
continue
if action.get("status") not in _OPEN_ACTION:
continue
created = _parse_ts(action.get("created_at"))
stale = created is not None and created < cutoff
actions.append(
{
"id": str(action["id"]),
"title": action.get("title") or "Technische Schuld",
"status": action.get("status"),
"stale": stale,
"created_at": action.get("created_at"),
}
)
stale_count = sum(1 for row in backlog_items if row["stale"]) + sum(
1 for row in actions if row["stale"]
)
return {
"enabled": True,
"backlog_items": backlog_items,
"actions": actions,
"total_open": len(backlog_items) + len(actions),
"stale_count": stale_count,
"stale_days": stale_days,
}