Files
tudo-para-ia-mais-humana-pl…/src/mais_humana/workflow_registry.py
2026-04-30 06:42:00 -03:00

371 lines
20 KiB
Python

"""Human workflow registry for cross-platform operational maturity."""
from __future__ import annotations
from dataclasses import dataclass
from typing import Iterable, Sequence
from .governance_models import EcosystemGovernancePortfolio, GovernanceStatus
from .models import as_plain_data, merge_unique
@dataclass(slots=True)
class WorkflowStep:
step_id: str
title: str
owner_platform: str
required_signals: tuple[str, ...]
validation: str
human_output: str
def to_dict(self) -> dict[str, object]:
return as_plain_data(self)
@dataclass(slots=True)
class HumanWorkflow:
workflow_id: str
title: str
purpose: str
primary_profile: str
platforms: tuple[str, ...]
steps: tuple[WorkflowStep, ...]
expected_artifacts: tuple[str, ...]
risk_if_missing: str
def to_dict(self) -> dict[str, object]:
return as_plain_data(self)
@dataclass(slots=True)
class WorkflowEvaluation:
workflow_id: str
title: str
status: str
score: int
passed_steps: int
total_steps: int
blocking_steps: tuple[str, ...]
next_actions: tuple[str, ...]
evidence: tuple[str, ...]
def to_dict(self) -> dict[str, object]:
return as_plain_data(self)
@dataclass(slots=True)
class WorkflowPortfolio:
workflows: tuple[HumanWorkflow, ...]
evaluations: tuple[WorkflowEvaluation, ...]
summary: tuple[str, ...]
def to_dict(self) -> dict[str, object]:
return as_plain_data(self)
def step(
step_id: str,
title: str,
owner_platform: str,
required_signals: Iterable[str],
validation: str,
human_output: str,
) -> WorkflowStep:
return WorkflowStep(
step_id=step_id,
title=title,
owner_platform=owner_platform,
required_signals=tuple(required_signals),
validation=validation,
human_output=human_output,
)
WORKFLOWS: tuple[HumanWorkflow, ...] = (
HumanWorkflow(
workflow_id="docs-canonical-decision",
title="Decisao canonica de Docs",
purpose="Resolver ambiguidade entre catalogOnly, responseReady minimo e excecao formal.",
primary_profile="planejamento_estrategico",
platforms=("docs", "mcps", "ui", "compliance"),
steps=(
step("docs-source", "Identificar fonte documental canonica", "docs", ("docs", "canonical", "contrato"), "abrir contrato Docs", "fonte documental declarada"),
step("docs-read", "Promover leitura minima ou declarar excecao", "docs", ("responseReady", "catalogOnly", "excecao"), "executar leitura ou registrar decisao", "decisao legivel"),
step("mcp-reconcile", "Reconciliar readiness MCP", "mcps", ("readiness", "blocker", "docs"), "comparar readiness global", "blocker Docs classificado"),
step("ui-explain", "Explicar estado para painel e GPT", "ui", ("sameSource", "panelReady", "sourceHash"), "validar mesma fonte", "painel e GPT explicam igual"),
),
expected_artifacts=("contrato Docs", "readiness reconciliado", "evidencia HTTP", "OS de continuidade"),
risk_if_missing="Docs continua como blocker global ambiguo.",
),
HumanWorkflow(
workflow_id="byok-live-controlled",
title="BYOK live controlado por tenant",
purpose="Provar credencial real sem vazamento, com usuario, organizacao, entitlement, smoke e consumo.",
primary_profile="tecnico",
platforms=("identity", "business", "integracoes", "compliance", "customer_ops"),
steps=(
step("identity-org", "Criar organizacao de teste", "identity", ("organizationId", "tenant"), "criar organizacao", "tenant rastreavel"),
step("identity-user", "Criar usuario e sessao", "identity", ("actorId", "session", "role"), "assumir usuario", "ator autorizado"),
step("business-entitlement", "Vincular produto e entitlement", "business", ("entitlement", "productId", "plan"), "consultar entitlement", "uso permitido"),
step("integracoes-session", "Criar sessao BYOK", "integracoes", ("BYOK", "credentialRef"), "gerar credentialRef", "segredo nao exposto"),
step("integracoes-smoke", "Executar smoke readonly", "integracoes", ("smoke", "readonly", "usage"), "rodar smoke", "provider validado"),
step("compliance-redaction", "Validar nao vazamento", "compliance", ("redaction", "audit", "trace"), "varrer relatorios", "auditoria segura"),
step("support-diagnostic", "Criar diagnostico para falha", "customer_ops", ("diagnostic", "nextAction"), "simular falha", "suporte orientado"),
),
expected_artifacts=("credentialRef", "auditId", "usage", "redaction check", "diagnostico"),
risk_if_missing="Integracao parece pronta, mas nao e autosservico real.",
),
HumanWorkflow(
workflow_id="business-commercial-gate",
title="Gate comercial unico por Business",
purpose="Garantir que plano, franquia, consumo e bloqueio venham de Business.",
primary_profile="financeiro",
platforms=("business", "finance", "integracoes", "public", "customer_ops"),
steps=(
step("plan-source", "Definir plano como fonte Business", "business", ("plano", "entitlement"), "consultar plano", "plano unico"),
step("quota-source", "Materializar franquia e excedente", "business", ("quota", "usage", "franquia"), "simular consumo", "limite claro"),
step("billing-link", "Reconciliar cobranca Finance", "finance", ("invoice", "reconciliation"), "gerar extrato", "fatura rastreavel"),
step("product-isolation", "Isolar blocker por produto", "business", ("productId", "blockerId"), "listar blockers", "impacto isolado"),
step("support-message", "Gerar mensagem humana de bloqueio", "customer_ops", ("support", "nextAction"), "validar mensagem", "cliente orientado"),
),
expected_artifacts=("entitlement", "invoice", "usage", "blocker matrix", "mensagem de suporte"),
risk_if_missing="Produto pode ser vendido ou bloqueado por regra divergente.",
),
HumanWorkflow(
workflow_id="mcp-panel-same-source",
title="Painel humano com mesma fonte do GPT",
purpose="Impedir divergencia entre UI, MCP e explicacao do GPT.",
primary_profile="administrador_empresa",
platforms=("mcps", "ui", "docs", "identity", "business"),
steps=(
step("screen-instance", "Criar instancia administrativa", "mcps", ("viewInstance", "screenData"), "criar instancia", "payload rastreavel"),
step("source-hashes", "Gerar hashes de fonte e registros", "mcps", ("sourcePayloadHash", "sourceRecordsHash"), "comparar hashes", "mesma fonte"),
step("ui-render", "Renderizar UI sem backend paralelo", "ui", ("panelReady", "sameSource"), "validar tela", "painel confiavel"),
step("gpt-explain", "Explicar a mesma instancia pelo GPT", "mcps", ("gptExplainable", "sameSource"), "pedir explicacao", "resposta coerente"),
step("docs-contract", "Publicar contrato da tela", "docs", ("contractVersion", "schemaVersion"), "exportar contrato", "documentacao viva"),
),
expected_artifacts=("viewInstance", "source hashes", "screen contract", "evidencia HTTP"),
risk_if_missing="Painel e GPT podem mostrar verdades diferentes.",
),
HumanWorkflow(
workflow_id="identity-rbac-denial",
title="Identity com matriz RBAC de negacao",
purpose="Provar permissoes permitidas e negadas por papel, organizacao e escopo.",
primary_profile="juridico",
platforms=("identity", "compliance", "mcps", "customer_ops"),
steps=(
step("role-matrix", "Publicar matriz de papeis", "identity", ("role", "scope", "permission"), "listar papeis", "papel claro"),
step("allow-case", "Testar caminho permitido", "identity", ("allow", "200"), "executar allow", "acao liberada"),
step("deny-case", "Testar caminho negado", "identity", ("deny", "403", "forbidden"), "executar deny", "acao bloqueada"),
step("audit-case", "Registrar auditId da decisao", "compliance", ("auditId", "traceId"), "consultar audit", "evidencia juridica"),
step("support-case", "Explicar negacao para suporte", "customer_ops", ("diagnostic", "nextAction"), "gerar diagnostico", "suporte seguro"),
),
expected_artifacts=("matriz RBAC", "allow evidence", "deny evidence", "auditId", "diagnostico"),
risk_if_missing="Controle de acesso fica baseado em caminho feliz e nao em governanca real.",
),
HumanWorkflow(
workflow_id="compliance-evidence-chain",
title="Cadeia de evidencia Compliance",
purpose="Ligar politica, consentimento, redaction, audit e retencao.",
primary_profile="juridico",
platforms=("compliance", "identity", "docs", "mcps"),
steps=(
step("policy", "Publicar politica aplicavel", "compliance", ("policy", "retention"), "validar politica", "regra clara"),
step("consent", "Registrar consentimento quando aplicavel", "compliance", ("consent", "actorId"), "consultar consentimento", "consentimento auditavel"),
step("redaction", "Aplicar redaction em campos sensiveis", "compliance", ("redaction", "masked"), "rodar redaction", "sem segredo"),
step("audit", "Gerar auditId e evidenceId", "compliance", ("auditId", "evidenceId"), "consultar evidencia", "cadeia de custodia"),
step("docs", "Documentar contrato de privacidade", "docs", ("contrato", "hash"), "validar docs", "memoria institucional"),
),
expected_artifacts=("policy", "consent record", "redaction report", "audit evidence", "docs hash"),
risk_if_missing="A plataforma nao prova governanca juridica de longo prazo.",
),
HumanWorkflow(
workflow_id="customer-ops-incident",
title="Incidente Customer Ops completo",
purpose="Abrir, diagnosticar, encaminhar, resolver e auditar incidente.",
primary_profile="suporte",
platforms=("customer_ops", "identity", "business", "integracoes", "compliance"),
steps=(
step("open", "Abrir incidente", "customer_ops", ("incident", "open"), "criar incidente", "ticket criado"),
step("classify", "Classificar origem e severidade", "customer_ops", ("severity", "domain"), "classificar", "prioridade clara"),
step("handoff", "Encaminhar para owner", "customer_ops", ("handoff", "owner"), "encaminhar", "responsavel definido"),
step("resolve", "Resolver com evidencia", "customer_ops", ("resolved", "evidenceId"), "fechar incidente", "solucao rastreavel"),
step("audit", "Auditar ciclo", "compliance", ("audit", "trace"), "consultar audit", "cadeia completa"),
),
expected_artifacts=("ticket", "classification", "handoff", "resolution", "audit trail"),
risk_if_missing="Suporte perde historico e proxima acao em falhas recorrentes.",
),
HumanWorkflow(
workflow_id="cloudflare-wrangler-operations",
title="Operacao Cloudflare por wrangler",
purpose="Validar runtime real por wrangler, tratando plugin como teste esperado.",
primary_profile="tecnico",
platforms=("integracoes", "mcps", "ui", "public", "gettys"),
steps=(
step("plugin-test", "Registrar tentativa do plugin", "integracoes", ("plugin Cloudflare", "expected"), "registrar tentativa", "premissa cumprida"),
step("wrangler-auth", "Validar autenticacao wrangler quando houver trabalho real", "integracoes", ("wrangler", "whoami"), "wrangler whoami", "identidade operacional"),
step("bindings", "Verificar bindings e secrets", "integracoes", ("bindings", "secrets"), "wrangler secret/list", "runtime configurado"),
step("routes", "Validar rotas e deploy", "integracoes", ("routes", "deploy"), "wrangler deploy/check", "rota viva"),
step("logs", "Coletar logs/health", "integracoes", ("tail", "health"), "wrangler tail/health", "diagnostico real"),
),
expected_artifacts=("plugin attempt", "wrangler status", "bindings", "routes", "health evidence"),
risk_if_missing="Cloudflare fica dependente de plugin experimental ou sem prova live.",
),
HumanWorkflow(
workflow_id="intelligence-promotion",
title="Promocao controlada de Intelligence",
purpose="Tirar Intelligence de unsupported/catalogOnly apenas com endpoint, smoke e contrato.",
primary_profile="ceo",
platforms=("intelligence", "mcps", "docs", "ui"),
steps=(
step("planned-state", "Declarar estado planejado", "intelligence", ("planned", "catalogOnly"), "registrar estado", "sem ambiguidade"),
step("endpoint", "Publicar endpoint minimo", "intelligence", ("health", "profile", "readiness"), "chamar endpoint", "runtime basico"),
step("contract", "Publicar contrato", "intelligence", ("openapi", "schema"), "validar contrato", "surface auditavel"),
step("mcp-register", "Registrar no MCP", "mcps", ("provider", "readiness"), "comparar catalogo", "control-plane ciente"),
step("ui-readiness", "Expor status no painel", "ui", ("panelReady", "sameSource"), "validar tela", "status humano"),
),
expected_artifacts=("state decision", "health smoke", "openapi", "mcp readiness", "panel status"),
risk_if_missing="Intelligence permanece meio provider e confunde readiness global.",
),
HumanWorkflow(
workflow_id="release-and-rollback",
title="Release com rollback e contrato",
purpose="Promover mudanca sem quebrar contrato, UI, GPT ou auditoria.",
primary_profile="gestor_operacional",
platforms=("platform_base", "mcps", "docs", "ui"),
steps=(
step("version", "Gerar contractVersion/schemaVersion", "platform_base", ("contractVersion", "schemaVersion"), "exportar versao", "versao clara"),
step("compat", "Declarar compatibilidade", "platform_base", ("compatibilityVersion", "breakingChanges"), "validar compat", "risco explicito"),
step("smoke", "Executar smoke regressivo", "mcps", ("smoke", "readiness"), "rodar smoke", "prova tecnica"),
step("rollback", "Registrar rollback", "platform_base", ("rollback", "previousVersion"), "validar rollback", "reversao possivel"),
step("docs", "Publicar changelog", "docs", ("changelog", "migrationNotes"), "validar docs", "memoria de mudanca"),
),
expected_artifacts=("version", "compatibility", "smoke report", "rollback plan", "changelog"),
risk_if_missing="Mudancas futuras quebram contratos sem trilha de reversao.",
),
)
def signal_text_for_card(portfolio: EcosystemGovernancePortfolio, platform_id: str) -> str:
card = portfolio.card_for(platform_id)
if card is None:
return ""
parts: list[str] = [card.platform_id, card.status_label, card.maturity.value]
for check in card.checks:
parts.extend([check.check_id, check.title, check.reason, check.next_action, check.status.value])
for evidence in check.evidence:
parts.append(evidence.path)
parts.append(evidence.summary)
return "\n".join(parts).lower()
def evaluate_step(step_item: WorkflowStep, portfolio: EcosystemGovernancePortfolio) -> tuple[bool, tuple[str, ...]]:
text = signal_text_for_card(portfolio, step_item.owner_platform)
missing = tuple(signal for signal in step_item.required_signals if signal.lower() not in text)
return (not missing, missing)
def evaluate_workflow(workflow: HumanWorkflow, portfolio: EcosystemGovernancePortfolio) -> WorkflowEvaluation:
passed = 0
blocking: list[str] = []
actions: list[str] = []
evidence: list[str] = []
for step_item in workflow.steps:
ok, missing = evaluate_step(step_item, portfolio)
card = portfolio.card_for(step_item.owner_platform)
if ok:
passed += 1
evidence.append(f"{step_item.step_id}: {step_item.human_output}")
else:
blocking.append(f"{step_item.step_id}: faltam {', '.join(missing[:4])}")
if card and card.next_actions:
actions.append(card.next_actions[0])
else:
actions.append(step_item.validation)
total = len(workflow.steps)
score = round((passed / total) * 100) if total else 0
status = "pronto" if score >= 85 else "util" if score >= 65 else "atencao" if score >= 40 else "bloqueado"
return WorkflowEvaluation(
workflow_id=workflow.workflow_id,
title=workflow.title,
status=status,
score=score,
passed_steps=passed,
total_steps=total,
blocking_steps=tuple(blocking),
next_actions=merge_unique(actions)[:8],
evidence=tuple(evidence),
)
def build_workflow_portfolio(portfolio: EcosystemGovernancePortfolio, workflows: Sequence[HumanWorkflow] = WORKFLOWS) -> WorkflowPortfolio:
evaluations = tuple(evaluate_workflow(workflow, portfolio) for workflow in workflows)
ready = sum(1 for item in evaluations if item.status in {"pronto", "util"})
blocked = sum(1 for item in evaluations if item.status == "bloqueado")
avg = round(sum(item.score for item in evaluations) / len(evaluations)) if evaluations else 0
summary = (
f"Workflows avaliados: {len(evaluations)}",
f"Workflows prontos/uteis: {ready}",
f"Workflows bloqueados: {blocked}",
f"Score medio de workflow: {avg}",
)
return WorkflowPortfolio(workflows=tuple(workflows), evaluations=evaluations, summary=summary)
def workflow_markdown(portfolio: WorkflowPortfolio) -> str:
lines = ["# Workflows humanos operacionais", ""]
lines.extend(f"- {item}" for item in portfolio.summary)
lines.append("")
by_id = {workflow.workflow_id: workflow for workflow in portfolio.workflows}
for evaluation in sorted(portfolio.evaluations, key=lambda item: (item.score, item.workflow_id)):
workflow = by_id[evaluation.workflow_id]
lines.append(f"## {workflow.title}")
lines.append("")
lines.append(f"- workflow_id: `{workflow.workflow_id}`")
lines.append(f"- perfil principal: `{workflow.primary_profile}`")
lines.append(f"- status: `{evaluation.status}`")
lines.append(f"- score: `{evaluation.score}`")
lines.append(f"- passos: `{evaluation.passed_steps}/{evaluation.total_steps}`")
lines.append(f"- risco se faltar: {workflow.risk_if_missing}")
if evaluation.blocking_steps:
lines.append("- bloqueios:")
for item in evaluation.blocking_steps:
lines.append(f" - {item}")
if evaluation.next_actions:
lines.append("- proximas acoes:")
for item in evaluation.next_actions:
lines.append(f" - {item}")
lines.append("")
lines.append("Passos:")
for step_item in workflow.steps:
lines.append(f"- `{step_item.step_id}` {step_item.title} ({step_item.owner_platform}) -> {step_item.human_output}")
lines.append("")
return "\n".join(lines).strip() + "\n"
def workflow_rows(portfolio: WorkflowPortfolio) -> list[list[str]]:
rows = [["workflow_id", "status", "score", "passed_steps", "total_steps", "blocking_steps", "next_actions"]]
for evaluation in sorted(portfolio.evaluations, key=lambda item: item.workflow_id):
rows.append(
[
evaluation.workflow_id,
evaluation.status,
str(evaluation.score),
str(evaluation.passed_steps),
str(evaluation.total_steps),
" | ".join(evaluation.blocking_steps),
" | ".join(evaluation.next_actions),
]
)
return rows
def workflow_action_items(portfolio: WorkflowPortfolio, limit: int = 25) -> tuple[str, ...]:
actions: list[str] = []
for evaluation in sorted(portfolio.evaluations, key=lambda item: (item.score, item.workflow_id)):
for action in evaluation.next_actions:
actions.append(f"{evaluation.workflow_id}: {action}")
if len(actions) >= limit:
return merge_unique(actions)
return merge_unique(actions)