"""Human-readable query helpers over the governance portfolio.""" from __future__ import annotations from dataclasses import dataclass from typing import Iterable, Sequence from .governance_models import EcosystemGovernancePortfolio, GovernanceDomain, GovernanceStatus, PlatformGovernanceCard from .models import as_plain_data, merge_unique @dataclass(slots=True) class PortfolioQuestion: question_id: str question: str answer: str evidence: tuple[str, ...] next_action: str def to_dict(self) -> dict[str, object]: return as_plain_data(self) def card_or_none(portfolio: EcosystemGovernancePortfolio, platform_id: str) -> PlatformGovernanceCard | None: return portfolio.card_for(platform_id) def summarize_card(card: PlatformGovernanceCard) -> str: blockers = ", ".join(check.title for check in card.blockers[:3]) or "sem blocker principal" action = card.next_actions[0] if card.next_actions else "manter regressao" return ( f"{card.platform_id} esta em status {card.status_label}, score {card.governance_score}, " f"maturidade {card.maturity.value}. Blockers: {blockers}. Proxima acao: {action}." ) def strongest_platforms(portfolio: EcosystemGovernancePortfolio, limit: int = 5) -> tuple[PlatformGovernanceCard, ...]: return tuple(sorted(portfolio.cards, key=lambda item: (-item.governance_score, item.platform_id))[:limit]) def weakest_platforms(portfolio: EcosystemGovernancePortfolio, limit: int = 5) -> tuple[PlatformGovernanceCard, ...]: return tuple(sorted(portfolio.cards, key=lambda item: (item.governance_score, item.platform_id))[:limit]) def platforms_by_domain_gap(portfolio: EcosystemGovernancePortfolio, domain: GovernanceDomain) -> tuple[PlatformGovernanceCard, ...]: cards: list[PlatformGovernanceCard] = [] for card in portfolio.cards: if any(check.domain == domain and check.status in {GovernanceStatus.ATTENTION, GovernanceStatus.FAIL, GovernanceStatus.BLOCKED} for check in card.checks): cards.append(card) cards.sort(key=lambda item: (item.governance_score, item.platform_id)) return tuple(cards) def blockers_for_domain(portfolio: EcosystemGovernancePortfolio, domain: GovernanceDomain) -> tuple[str, ...]: items: list[str] = [] for card in portfolio.cards: for check in card.blockers: if check.domain == domain: items.append(f"{card.platform_id}: {check.title} - {check.next_action}") return merge_unique(items) def build_operational_questions(portfolio: EcosystemGovernancePortfolio) -> tuple[PortfolioQuestion, ...]: questions: list[PortfolioQuestion] = [] questions.append( PortfolioQuestion( question_id="estado-geral-governanca", question="Qual e o estado geral de governanca humana do ecossistema?", answer=( f"O score medio de governanca e {portfolio.average_governance_score}. " f"Plataformas bloqueadas: {len(portfolio.blocked_platforms)}. " f"Plataformas controladas: {len(portfolio.controlled_platforms)}." ), evidence=portfolio.executive_summary, next_action="atuar primeiro nos blockers de dominio com maior impacto humano", ) ) weak = weakest_platforms(portfolio) questions.append( PortfolioQuestion( question_id="plataformas-mais-fracas", question="Quais plataformas mais precisam de continuidade?", answer="As plataformas com menor score sao: " + ", ".join(f"{card.platform_id} ({card.governance_score})" for card in weak), evidence=tuple(summarize_card(card) for card in weak), next_action="executar as OS vinculadas aos checks dessas plataformas", ) ) strong = strongest_platforms(portfolio) questions.append( PortfolioQuestion( question_id="plataformas-mais-fortes", question="Quais plataformas estao mais maduras para leitura humana?", answer="As plataformas mais fortes sao: " + ", ".join(f"{card.platform_id} ({card.governance_score})" for card in strong), evidence=tuple(summarize_card(card) for card in strong), next_action="usar essas plataformas como referencia de padrao e regressao", ) ) for domain in ( GovernanceDomain.DOCS, GovernanceDomain.INTEGRATIONS, GovernanceDomain.IDENTITY, GovernanceDomain.BUSINESS, GovernanceDomain.MCP, GovernanceDomain.CLOUD, GovernanceDomain.OBSERVABILITY, ): blockers = blockers_for_domain(portfolio, domain) impacted = platforms_by_domain_gap(portfolio, domain) answer = ( f"Dominio {domain.value} tem {len(blockers)} blockers e " f"{len(impacted)} plataformas com gap/atencao." ) if blockers: answer += " Principais: " + " | ".join(blockers[:3]) questions.append( PortfolioQuestion( question_id=f"dominio-{domain.value}", question=f"O que bloqueia ou exige atencao no dominio {domain.value}?", answer=answer, evidence=blockers[:8] or tuple(summarize_card(card) for card in impacted[:5]), next_action=( f"priorizar checks do dominio {domain.value} e validar owner " "institucional antes da proxima promocao" ), ) ) questions.append( PortfolioQuestion( question_id="ordens-saida-justificadas", question="As ordens de saida estao justificadas por checks reais?", answer=( f"Ha {len(portfolio.order_candidates)} candidatas de OS derivadas de checks de governanca. " "Cada candidata guarda source_check_ids e validacoes." ), evidence=tuple(f"{candidate.candidate_id}: {', '.join(candidate.source_check_ids)}" for candidate in portfolio.order_candidates[:12]), next_action="manter ativas apenas ordens ligadas a pendencias reais ou continuidade impossivel nesta rodada", ) ) return tuple(questions) def questions_markdown(questions: Sequence[PortfolioQuestion]) -> str: lines = ["# Perguntas operacionais sobre governanca", ""] for question in questions: lines.append(f"## {question.question}") lines.append("") lines.append(question.answer) lines.append("") lines.append(f"Proxima acao: {question.next_action}") if question.evidence: lines.append("") lines.append("Evidencias:") for item in question.evidence[:10]: lines.append(f"- {item}") lines.append("") return "\n".join(lines).strip() + "\n" def questions_rows(questions: Sequence[PortfolioQuestion]) -> list[list[str]]: rows = [["question_id", "question", "answer", "next_action", "evidence_count"]] for question in questions: rows.append([question.question_id, question.question, question.answer, question.next_action, str(len(question.evidence))]) return rows def query_by_keyword(questions: Sequence[PortfolioQuestion], keyword: str) -> tuple[PortfolioQuestion, ...]: lowered = keyword.lower() return tuple( question for question in questions if lowered in question.question.lower() or lowered in question.answer.lower() or any(lowered in evidence.lower() for evidence in question.evidence) ) def unresolved_question_ids(questions: Sequence[PortfolioQuestion]) -> tuple[str, ...]: ids: list[str] = [] for question in questions: text = f"{question.answer} {question.next_action}".lower() if "blocker" in text or "gap" in text or "priorizar" in text: ids.append(question.question_id) return tuple(ids) def compact_question_payload(questions: Sequence[PortfolioQuestion]) -> dict[str, object]: return { "count": len(questions), "unresolved": unresolved_question_ids(questions), "questions": [question.to_dict() for question in questions], }