"""Portfolio-level steering aggregation — K-Ext-5.""" from __future__ import annotations from typing import Any from steering.kernel import evaluate_steering from tenant_context import TenantContext _SEVERITY_ORDER = {"critical": 0, "warning": 1, "info": 2} def _portfolio_rank_key(rank: int | None) -> int: return rank if rank is not None else 999_999 def _load_active_initiatives(ctx: TenantContext, *, limit: int) -> list[dict[str, Any]]: from data_layer.initiatives import get_active_initiatives return get_active_initiatives(ctx, limit=limit) def _enrich_item( item: dict[str, Any], *, initiative_id: str, initiative_title: str, portfolio_rank: int | None, ) -> dict[str, Any]: enriched = dict(item) enriched.setdefault("initiative_id", initiative_id) enriched["initiative_title"] = initiative_title if portfolio_rank is not None: enriched["portfolio_rank"] = portfolio_rank enriched.setdefault("data_source", "steering_kernel") return enriched def _sort_attention(items: list[dict[str, Any]]) -> list[dict[str, Any]]: return sorted( items, key=lambda item: ( _SEVERITY_ORDER.get(item.get("severity", "info"), 99), _portfolio_rank_key(item.get("portfolio_rank")), item.get("initiative_title") or "", item.get("title") or "", ), ) def _sort_next_work(items: list[dict[str, Any]]) -> list[dict[str, Any]]: return sorted( items, key=lambda item: ( _portfolio_rank_key(item.get("portfolio_rank")), item.get("rank") if item.get("rank") is not None else 999, item.get("title") or "", ), ) def aggregate_portfolio_steering( ctx: TenantContext, *, max_initiatives: int = 50, attention_per_initiative: int = 5, next_work_per_initiative: int = 3, total_attention: int = 30, total_next_work: int = 15, total_agent_slots: int = 20, ) -> dict[str, Any]: """ Aggregiert Kernel-Steuerung über aktive Initiativen (Portfolio-Rang). """ initiatives = _load_active_initiatives(ctx, limit=max_initiatives) attention: list[dict[str, Any]] = [] next_work: list[dict[str, Any]] = [] agent_slots: list[dict[str, Any]] = [] proposals_summary: list[dict[str, Any]] = [] attention_by_initiative: dict[str, list[dict[str, Any]]] = {} agent_slots_by_initiative: dict[str, list[dict[str, Any]]] = {} for initiative in initiatives: initiative_id = str(initiative["id"]) initiative_title = initiative.get("title") or "Vorhaben" portfolio_rank = initiative.get("portfolio_rank") evaluation = evaluate_steering( ctx, initiative_id=initiative_id, next_work_limit=next_work_per_initiative, attention_limit=attention_per_initiative, ) initiative_attention: list[dict[str, Any]] = [] for item in evaluation.attention: enriched = _enrich_item( item, initiative_id=initiative_id, initiative_title=initiative_title, portfolio_rank=portfolio_rank, ) initiative_attention.append(enriched) attention.append(enriched) attention_by_initiative[initiative_id] = initiative_attention for item in evaluation.next_work: next_work.append( _enrich_item( item, initiative_id=initiative_id, initiative_title=initiative_title, portfolio_rank=portfolio_rank, ) ) initiative_slots: list[dict[str, Any]] = [] for slot in evaluation.agent_slots: enriched = _enrich_item( slot, initiative_id=initiative_id, initiative_title=initiative_title, portfolio_rank=portfolio_rank, ) initiative_slots.append(enriched) agent_slots.append(enriched) agent_slots_by_initiative[initiative_id] = initiative_slots proposal_counts = { key: len(value) if isinstance(value, list) else 0 for key, value in (evaluation.proposals or {}).items() } if any(proposal_counts.values()): proposals_summary.append( { "initiative_id": initiative_id, "initiative_title": initiative_title, "portfolio_rank": portfolio_rank, "counts": proposal_counts, "total": sum(proposal_counts.values()), } ) attention = _sort_attention(attention)[:total_attention] next_work = _sort_next_work(next_work)[:total_next_work] agent_slots = sorted( agent_slots, key=lambda slot: ( {"high": 0, "normal": 1, "low": 2}.get(slot.get("priority", "normal"), 9), _portfolio_rank_key(slot.get("portfolio_rank")), ), )[:total_agent_slots] proposals_summary.sort( key=lambda row: ( -row.get("total", 0), _portfolio_rank_key(row.get("portfolio_rank")), ) ) return { "data_source": "steering_kernel_portfolio_v0.1", "initiative_count": len(initiatives), "attention": attention, "attention_by_initiative": attention_by_initiative, "next_work": next_work, "proposals_summary": proposals_summary, "agent_slots": agent_slots, "agent_slots_by_initiative": agent_slots_by_initiative, }