Implement human operational rulebook
This commit is contained in:
585
src/mais_humana/human_rulebook.py
Normal file
585
src/mais_humana/human_rulebook.py
Normal file
@@ -0,0 +1,585 @@
|
||||
"""Human-operational rulebook for Mais Humana.
|
||||
|
||||
The platform already knows how to scan repositories and build reports. This
|
||||
module adds a deterministic rulebook that translates those reports into a
|
||||
control-plane vocabulary a human administrator can use: which profile is served,
|
||||
which surface must exist, which MCP transit fields are mandatory, which evidence
|
||||
proves the rule, and what next order is justified when the rule is not covered.
|
||||
|
||||
Most rules are generated from the canonical catalog by
|
||||
``tools/generate_human_rulebook.py``. The hand-written code here keeps the
|
||||
runtime behavior small, testable, and independent of the generation step.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import csv
|
||||
import io
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from typing import Any, Iterable, Mapping, Sequence
|
||||
|
||||
from .models import PlatformHumanReport, as_plain_data, merge_unique, slugify, utc_now
|
||||
|
||||
|
||||
class RuleScope(str, Enum):
|
||||
"""Scope used to group rulebook entries."""
|
||||
|
||||
PROFILE_PLATFORM = "profile_platform"
|
||||
PROFILE_SURFACE = "profile_surface"
|
||||
PLATFORM_SURFACE = "platform_surface"
|
||||
DEPENDENCY = "dependency"
|
||||
MCP_TRANSIT = "mcp_transit"
|
||||
CANONICAL_IDENTITY = "canonical_identity"
|
||||
|
||||
|
||||
class RuleOutcome(str, Enum):
|
||||
"""Normalized coverage result for a rule."""
|
||||
|
||||
COVERED = "covered"
|
||||
PARTIAL = "partial"
|
||||
MISSING = "missing"
|
||||
BLOCKED = "blocked"
|
||||
EXCEPTION = "exception"
|
||||
|
||||
|
||||
class TruthState(str, Enum):
|
||||
"""How close a rule is to an operational source of truth."""
|
||||
|
||||
UNKNOWN = "unknown"
|
||||
CATALOG_ONLY = "catalog_only"
|
||||
DOCUMENTED = "documented"
|
||||
DERIVED = "derived"
|
||||
RESPONSE_READY = "response_ready"
|
||||
SAME_SOURCE_READY = "same_source_ready"
|
||||
LIVE_READONLY = "live_readonly"
|
||||
LIVE_WRITE = "live_write"
|
||||
BLOCKED = "blocked"
|
||||
FORMAL_EXCEPTION = "formal_exception"
|
||||
|
||||
|
||||
OUTCOME_SCORE: dict[RuleOutcome, int] = {
|
||||
RuleOutcome.COVERED: 100,
|
||||
RuleOutcome.EXCEPTION: 82,
|
||||
RuleOutcome.PARTIAL: 58,
|
||||
RuleOutcome.MISSING: 18,
|
||||
RuleOutcome.BLOCKED: 0,
|
||||
}
|
||||
|
||||
|
||||
TRUTH_SCORE: dict[TruthState, int] = {
|
||||
TruthState.UNKNOWN: 0,
|
||||
TruthState.CATALOG_ONLY: 20,
|
||||
TruthState.DOCUMENTED: 38,
|
||||
TruthState.DERIVED: 52,
|
||||
TruthState.RESPONSE_READY: 70,
|
||||
TruthState.SAME_SOURCE_READY: 82,
|
||||
TruthState.LIVE_READONLY: 88,
|
||||
TruthState.LIVE_WRITE: 94,
|
||||
TruthState.FORMAL_EXCEPTION: 76,
|
||||
TruthState.BLOCKED: 0,
|
||||
}
|
||||
|
||||
|
||||
MCP_TRANSIT_FIELDS: tuple[str, ...] = (
|
||||
"origin",
|
||||
"destination",
|
||||
"tool",
|
||||
"payload",
|
||||
"actor",
|
||||
"permission",
|
||||
"result",
|
||||
"traceId",
|
||||
"auditId",
|
||||
"timestamp",
|
||||
)
|
||||
|
||||
|
||||
CANONICAL_PROJECT_ID = "tudo-para-ia-mais-humana-plataform"
|
||||
CURRENT_PROJECT_ID = "tudo-para-ia-mais-humana"
|
||||
MCP_CONTROL_PLANE_ID = "tudo-para-ia-mcps-internos-plataform"
|
||||
UI_SUPPORT_PLATFORM_ID = "tudo-para-ia-ui-platform"
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class HumanControlRule:
|
||||
"""One rule linking a platform, a human profile, and an operational surface."""
|
||||
|
||||
rule_id: str
|
||||
scope: RuleScope
|
||||
platform_id: str
|
||||
profile_id: str
|
||||
title: str
|
||||
purpose: str
|
||||
source_of_truth: str
|
||||
required_surfaces: tuple[str, ...]
|
||||
success_markers: tuple[str, ...]
|
||||
evidence_terms: tuple[str, ...]
|
||||
negative_terms: tuple[str, ...]
|
||||
mcp_transit_fields: tuple[str, ...]
|
||||
expected_payload_fields: tuple[str, ...]
|
||||
validation_steps: tuple[str, ...]
|
||||
next_order_hint: str
|
||||
priority: str = "media"
|
||||
generated_from: str = "catalog"
|
||||
canonical_project_id: str = CANONICAL_PROJECT_ID
|
||||
control_plane_id: str = MCP_CONTROL_PLANE_ID
|
||||
ui_support_platform_id: str = UI_SUPPORT_PLATFORM_ID
|
||||
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
return as_plain_data(self)
|
||||
|
||||
@property
|
||||
def slug(self) -> str:
|
||||
return slugify(self.title)
|
||||
|
||||
@property
|
||||
def is_mcp_bound(self) -> bool:
|
||||
return self.control_plane_id in self.source_of_truth or "mcp" in self.source_of_truth.lower()
|
||||
|
||||
@property
|
||||
def field_count(self) -> int:
|
||||
return len(self.mcp_transit_fields) + len(self.expected_payload_fields)
|
||||
|
||||
def mentions(self, text: str) -> bool:
|
||||
lowered = text.lower()
|
||||
values = (
|
||||
self.rule_id,
|
||||
self.platform_id,
|
||||
self.profile_id,
|
||||
self.title,
|
||||
self.purpose,
|
||||
self.source_of_truth,
|
||||
" ".join(self.required_surfaces),
|
||||
" ".join(self.success_markers),
|
||||
)
|
||||
return any(value.lower() in lowered for value in values if value)
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class RuleEvidenceHit:
|
||||
"""Evidence hit found while evaluating a rule against generated reports."""
|
||||
|
||||
path: str
|
||||
summary: str
|
||||
term: str
|
||||
confidence: float
|
||||
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
return as_plain_data(self)
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class RuleCoverage:
|
||||
"""Coverage decision for one rule."""
|
||||
|
||||
rule_id: str
|
||||
platform_id: str
|
||||
profile_id: str
|
||||
scope: RuleScope
|
||||
outcome: RuleOutcome
|
||||
truth_state: TruthState
|
||||
score: int
|
||||
reason: str
|
||||
evidence: tuple[RuleEvidenceHit, ...]
|
||||
missing_terms: tuple[str, ...]
|
||||
next_order_hint: str
|
||||
validation_steps: tuple[str, ...]
|
||||
generated_at: str = field(default_factory=utc_now)
|
||||
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
return as_plain_data(self)
|
||||
|
||||
@property
|
||||
def needs_order(self) -> bool:
|
||||
return self.outcome in {RuleOutcome.MISSING, RuleOutcome.BLOCKED, RuleOutcome.PARTIAL}
|
||||
|
||||
@property
|
||||
def compact_status(self) -> str:
|
||||
return f"{self.outcome.value}/{self.truth_state.value}/{self.score}"
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class RulebookReport:
|
||||
"""Full rulebook evaluation for the current generation run."""
|
||||
|
||||
project_id: str
|
||||
canonical_project_id: str
|
||||
generated_at: str
|
||||
rules_count: int
|
||||
coverage: tuple[RuleCoverage, ...]
|
||||
executive_summary: tuple[str, ...]
|
||||
active_risks: tuple[str, ...]
|
||||
next_order_hints: tuple[str, ...]
|
||||
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
return as_plain_data(self)
|
||||
|
||||
@property
|
||||
def average_score(self) -> int:
|
||||
if not self.coverage:
|
||||
return 0
|
||||
return round(sum(item.score for item in self.coverage) / len(self.coverage))
|
||||
|
||||
@property
|
||||
def blocked_count(self) -> int:
|
||||
return sum(1 for item in self.coverage if item.outcome == RuleOutcome.BLOCKED)
|
||||
|
||||
@property
|
||||
def partial_count(self) -> int:
|
||||
return sum(1 for item in self.coverage if item.outcome == RuleOutcome.PARTIAL)
|
||||
|
||||
@property
|
||||
def missing_count(self) -> int:
|
||||
return sum(1 for item in self.coverage if item.outcome == RuleOutcome.MISSING)
|
||||
|
||||
|
||||
def _generated_rules() -> tuple[HumanControlRule, ...]:
|
||||
"""Import the generated rulebook lazily to avoid an import cycle."""
|
||||
|
||||
from .generated_human_rulebook import RULES
|
||||
|
||||
return RULES
|
||||
|
||||
|
||||
def iter_rules() -> tuple[HumanControlRule, ...]:
|
||||
return _generated_rules()
|
||||
|
||||
|
||||
def rules_for_platform(platform_id: str) -> tuple[HumanControlRule, ...]:
|
||||
return tuple(rule for rule in iter_rules() if rule.platform_id == platform_id)
|
||||
|
||||
|
||||
def rules_for_profile(profile_id: str) -> tuple[HumanControlRule, ...]:
|
||||
return tuple(rule for rule in iter_rules() if rule.profile_id == profile_id)
|
||||
|
||||
|
||||
def rules_for_scope(scope: RuleScope) -> tuple[HumanControlRule, ...]:
|
||||
return tuple(rule for rule in iter_rules() if rule.scope == scope)
|
||||
|
||||
|
||||
def _report_corpus(report: PlatformHumanReport) -> str:
|
||||
parts: list[str] = [
|
||||
report.platform.platform_id,
|
||||
report.platform.title,
|
||||
report.platform.mission,
|
||||
report.scan.repo_path,
|
||||
report.scan.readme_excerpt,
|
||||
" ".join(report.platform.expected_surfaces),
|
||||
" ".join(report.platform.known_blockers),
|
||||
" ".join(report.scan.warnings),
|
||||
report.summary,
|
||||
" ".join(report.current_state),
|
||||
" ".join(report.future_state),
|
||||
" ".join(report.missing_for_humans),
|
||||
]
|
||||
for evidence in report.scan.evidence[:500]:
|
||||
parts.append(evidence.path)
|
||||
parts.append(evidence.summary)
|
||||
parts.append(evidence.kind.value)
|
||||
parts.append(" ".join(evidence.tags))
|
||||
for recommendation in report.recommendations:
|
||||
parts.append(recommendation.title)
|
||||
parts.append(recommendation.reason)
|
||||
parts.append(recommendation.expected_impact)
|
||||
parts.append(" ".join(recommendation.affected_paths))
|
||||
for cell in report.cells:
|
||||
parts.append(cell.profile_id)
|
||||
parts.append(cell.maturity.value)
|
||||
parts.append(cell.explanation)
|
||||
parts.append(" ".join(cell.strengths))
|
||||
parts.append(" ".join(cell.gaps))
|
||||
return "\n".join(item for item in parts if item).lower()
|
||||
|
||||
|
||||
def _reports_by_platform(reports: Sequence[PlatformHumanReport]) -> Mapping[str, PlatformHumanReport]:
|
||||
return {report.platform.platform_id: report for report in reports}
|
||||
|
||||
|
||||
def _hits_for_rule(rule: HumanControlRule, corpus: str, report: PlatformHumanReport | None) -> tuple[RuleEvidenceHit, ...]:
|
||||
hits: list[RuleEvidenceHit] = []
|
||||
terms = merge_unique(rule.evidence_terms + rule.success_markers + rule.required_surfaces)
|
||||
for term in terms:
|
||||
if term and term.lower() in corpus:
|
||||
hits.append(
|
||||
RuleEvidenceHit(
|
||||
path=report.scan.repo_path if report is not None else rule.source_of_truth,
|
||||
summary=f"Termo encontrado para regra: {term}",
|
||||
term=term,
|
||||
confidence=0.72,
|
||||
)
|
||||
)
|
||||
if len(hits) >= 12:
|
||||
break
|
||||
return tuple(hits)
|
||||
|
||||
|
||||
def _missing_terms(rule: HumanControlRule, corpus: str) -> tuple[str, ...]:
|
||||
candidates = merge_unique(rule.success_markers + rule.required_surfaces)
|
||||
missing = [term for term in candidates if term and term.lower() not in corpus]
|
||||
return tuple(missing[:12])
|
||||
|
||||
|
||||
def _truth_state_for_rule(rule: HumanControlRule, corpus: str, negative_hits: int) -> TruthState:
|
||||
lowered = corpus.lower()
|
||||
if negative_hits:
|
||||
if "catalogonly" in lowered or "catalog_only" in lowered:
|
||||
return TruthState.CATALOG_ONLY
|
||||
if "unsupported" in lowered or "needs_token" in lowered or "blocked" in lowered:
|
||||
return TruthState.BLOCKED
|
||||
if "live_write" in lowered or "write readback" in lowered or "persist" in lowered:
|
||||
return TruthState.LIVE_WRITE
|
||||
if "live_readonly" in lowered or "readonly" in lowered or "readback" in lowered:
|
||||
return TruthState.LIVE_READONLY
|
||||
if "sameSource".lower() in lowered or "same-source" in lowered or "mesma fonte" in lowered:
|
||||
return TruthState.SAME_SOURCE_READY
|
||||
if "responseReady".lower() in lowered or "response-ready" in lowered:
|
||||
return TruthState.RESPONSE_READY
|
||||
if "contract" in lowered or "contrato" in lowered or "readiness" in lowered:
|
||||
return TruthState.DERIVED
|
||||
if "readme" in lowered or "docs" in lowered or "document" in lowered:
|
||||
return TruthState.DOCUMENTED
|
||||
return TruthState.UNKNOWN
|
||||
|
||||
|
||||
def _outcome_for_rule(
|
||||
rule: HumanControlRule,
|
||||
positive_hits: int,
|
||||
missing_terms: Sequence[str],
|
||||
negative_hits: int,
|
||||
truth_state: TruthState,
|
||||
) -> RuleOutcome:
|
||||
if truth_state == TruthState.CATALOG_ONLY and rule.platform_id == "docs":
|
||||
return RuleOutcome.BLOCKED
|
||||
if truth_state == TruthState.BLOCKED:
|
||||
return RuleOutcome.BLOCKED
|
||||
if negative_hits and positive_hits < max(2, len(rule.success_markers) // 2):
|
||||
return RuleOutcome.BLOCKED
|
||||
if positive_hits >= max(3, len(rule.success_markers) // 2) and not missing_terms:
|
||||
return RuleOutcome.COVERED
|
||||
if positive_hits >= 2:
|
||||
return RuleOutcome.PARTIAL
|
||||
if truth_state == TruthState.FORMAL_EXCEPTION:
|
||||
return RuleOutcome.EXCEPTION
|
||||
return RuleOutcome.MISSING
|
||||
|
||||
|
||||
def _coverage_score(outcome: RuleOutcome, truth_state: TruthState, positive_hits: int, missing_count: int) -> int:
|
||||
base = OUTCOME_SCORE[outcome]
|
||||
truth_bonus = round(TRUTH_SCORE[truth_state] * 0.2)
|
||||
evidence_bonus = min(14, positive_hits * 2)
|
||||
missing_penalty = min(30, missing_count * 3)
|
||||
return max(0, min(100, base + truth_bonus + evidence_bonus - missing_penalty))
|
||||
|
||||
|
||||
def evaluate_rule(rule: HumanControlRule, reports: Sequence[PlatformHumanReport]) -> RuleCoverage:
|
||||
by_platform = _reports_by_platform(reports)
|
||||
report = by_platform.get(rule.platform_id)
|
||||
corpus = _report_corpus(report) if report is not None else ""
|
||||
positive_hits = sum(1 for term in merge_unique(rule.evidence_terms + rule.success_markers) if term.lower() in corpus)
|
||||
negative_hits = sum(1 for term in rule.negative_terms if term.lower() in corpus)
|
||||
missing = _missing_terms(rule, corpus)
|
||||
truth_state = _truth_state_for_rule(rule, corpus, negative_hits)
|
||||
outcome = _outcome_for_rule(rule, positive_hits, missing, negative_hits, truth_state)
|
||||
score = _coverage_score(outcome, truth_state, positive_hits, len(missing))
|
||||
if report is None:
|
||||
reason = "Repositorio ou relatorio de plataforma nao encontrado para a regra."
|
||||
elif outcome == RuleOutcome.COVERED:
|
||||
reason = "A regra possui sinais suficientes nos relatorios e evidencias da plataforma."
|
||||
elif outcome == RuleOutcome.PARTIAL:
|
||||
reason = "A regra possui sinais parciais, mas ainda falta superficie, marcador ou prova direta."
|
||||
elif outcome == RuleOutcome.BLOCKED:
|
||||
reason = "A regra encontrou bloqueio ou estado catalogOnly/unsupported que impede maturidade humana plena."
|
||||
elif outcome == RuleOutcome.EXCEPTION:
|
||||
reason = "A regra depende de excecao formal registrada como decisao de governanca."
|
||||
else:
|
||||
reason = "A regra ainda nao encontrou evidencias suficientes."
|
||||
return RuleCoverage(
|
||||
rule_id=rule.rule_id,
|
||||
platform_id=rule.platform_id,
|
||||
profile_id=rule.profile_id,
|
||||
scope=rule.scope,
|
||||
outcome=outcome,
|
||||
truth_state=truth_state,
|
||||
score=score,
|
||||
reason=reason,
|
||||
evidence=_hits_for_rule(rule, corpus, report),
|
||||
missing_terms=missing,
|
||||
next_order_hint=rule.next_order_hint,
|
||||
validation_steps=rule.validation_steps,
|
||||
)
|
||||
|
||||
|
||||
def evaluate_rulebook(reports: Sequence[PlatformHumanReport], *, limit: int | None = None) -> RulebookReport:
|
||||
rules = iter_rules()
|
||||
selected = rules if limit is None else rules[:limit]
|
||||
coverage = tuple(evaluate_rule(rule, reports) for rule in selected)
|
||||
risks = []
|
||||
hints = []
|
||||
for item in sorted(coverage, key=lambda cov: (cov.score, cov.platform_id, cov.profile_id, cov.rule_id)):
|
||||
if item.outcome in {RuleOutcome.BLOCKED, RuleOutcome.MISSING}:
|
||||
risks.append(f"{item.platform_id}/{item.profile_id}/{item.scope.value}: {item.reason}")
|
||||
hints.append(item.next_order_hint)
|
||||
summary = (
|
||||
f"Regras avaliadas: {len(coverage)}",
|
||||
f"Score medio do rulebook: {round(sum(item.score for item in coverage) / len(coverage)) if coverage else 0}",
|
||||
f"Bloqueadas: {sum(1 for item in coverage if item.outcome == RuleOutcome.BLOCKED)}",
|
||||
f"Parciais: {sum(1 for item in coverage if item.outcome == RuleOutcome.PARTIAL)}",
|
||||
f"Sem evidencia: {sum(1 for item in coverage if item.outcome == RuleOutcome.MISSING)}",
|
||||
f"Projeto canonico recomendado: {CANONICAL_PROJECT_ID}",
|
||||
f"Caminho administrativo obrigatorio: {MCP_CONTROL_PLANE_ID}",
|
||||
)
|
||||
return RulebookReport(
|
||||
project_id=CURRENT_PROJECT_ID,
|
||||
canonical_project_id=CANONICAL_PROJECT_ID,
|
||||
generated_at=utc_now(),
|
||||
rules_count=len(rules),
|
||||
coverage=coverage,
|
||||
executive_summary=summary,
|
||||
active_risks=merge_unique(risks)[:40],
|
||||
next_order_hints=merge_unique(hints)[:20],
|
||||
)
|
||||
|
||||
|
||||
def rulebook_rows(report: RulebookReport) -> list[list[str]]:
|
||||
rows = [[
|
||||
"rule_id",
|
||||
"platform",
|
||||
"profile",
|
||||
"scope",
|
||||
"outcome",
|
||||
"truth_state",
|
||||
"score",
|
||||
"missing_terms",
|
||||
"next_order_hint",
|
||||
]]
|
||||
for item in sorted(report.coverage, key=lambda cov: (cov.platform_id, cov.profile_id, cov.rule_id)):
|
||||
rows.append(
|
||||
[
|
||||
item.rule_id,
|
||||
item.platform_id,
|
||||
item.profile_id,
|
||||
item.scope.value,
|
||||
item.outcome.value,
|
||||
item.truth_state.value,
|
||||
str(item.score),
|
||||
"; ".join(item.missing_terms),
|
||||
item.next_order_hint,
|
||||
]
|
||||
)
|
||||
return rows
|
||||
|
||||
|
||||
def rows_to_csv(rows: Sequence[Sequence[str]]) -> str:
|
||||
buffer = io.StringIO()
|
||||
writer = csv.writer(buffer, lineterminator="\n")
|
||||
writer.writerows(rows)
|
||||
return buffer.getvalue()
|
||||
|
||||
|
||||
def rulebook_csv(report: RulebookReport) -> str:
|
||||
return rows_to_csv(rulebook_rows(report))
|
||||
|
||||
|
||||
def rulebook_markdown(report: RulebookReport) -> str:
|
||||
lines = [
|
||||
"# Rulebook humano-operacional",
|
||||
"",
|
||||
f"- project_id_atual: `{report.project_id}`",
|
||||
f"- project_id_canonico_recomendado: `{report.canonical_project_id}`",
|
||||
f"- generated_at: `{report.generated_at}`",
|
||||
f"- regras_geradas: `{report.rules_count}`",
|
||||
f"- regras_avaliadas: `{len(report.coverage)}`",
|
||||
f"- score_medio: `{report.average_score}`",
|
||||
f"- bloqueadas: `{report.blocked_count}`",
|
||||
f"- parciais: `{report.partial_count}`",
|
||||
f"- sem_evidencia: `{report.missing_count}`",
|
||||
"",
|
||||
"## Sumario",
|
||||
"",
|
||||
]
|
||||
lines.extend(f"- {item}" for item in report.executive_summary)
|
||||
lines.extend(["", "## Riscos ativos", ""])
|
||||
if report.active_risks:
|
||||
lines.extend(f"- {item}" for item in report.active_risks[:30])
|
||||
else:
|
||||
lines.append("- Nenhum risco ativo no rulebook avaliado.")
|
||||
lines.extend(["", "## Proximas ordens sugeridas", ""])
|
||||
if report.next_order_hints:
|
||||
lines.extend(f"- {item}" for item in report.next_order_hints[:20])
|
||||
else:
|
||||
lines.append("- Manter regressao e evidencias.")
|
||||
lines.extend(["", "## Cobertura por plataforma", ""])
|
||||
grouped: dict[str, list[RuleCoverage]] = {}
|
||||
for item in report.coverage:
|
||||
grouped.setdefault(item.platform_id, []).append(item)
|
||||
for platform_id in sorted(grouped):
|
||||
items = grouped[platform_id]
|
||||
avg = round(sum(item.score for item in items) / len(items)) if items else 0
|
||||
blocked = sum(1 for item in items if item.outcome == RuleOutcome.BLOCKED)
|
||||
partial = sum(1 for item in items if item.outcome == RuleOutcome.PARTIAL)
|
||||
missing = sum(1 for item in items if item.outcome == RuleOutcome.MISSING)
|
||||
lines.append(f"### {platform_id}")
|
||||
lines.append("")
|
||||
lines.append(f"- score: `{avg}`")
|
||||
lines.append(f"- bloqueadas: `{blocked}`")
|
||||
lines.append(f"- parciais: `{partial}`")
|
||||
lines.append(f"- sem_evidencia: `{missing}`")
|
||||
for item in sorted(items, key=lambda cov: (cov.score, cov.scope.value, cov.profile_id))[:8]:
|
||||
lines.append(
|
||||
f"- `{item.compact_status}` `{item.profile_id}` `{item.scope.value}` "
|
||||
f"{item.reason} Proxima OS: {item.next_order_hint}"
|
||||
)
|
||||
lines.append("")
|
||||
return "\n".join(lines).strip() + "\n"
|
||||
|
||||
|
||||
def rulebook_compact_json(report: RulebookReport) -> dict[str, Any]:
|
||||
return {
|
||||
"project_id": report.project_id,
|
||||
"canonical_project_id": report.canonical_project_id,
|
||||
"generated_at": report.generated_at,
|
||||
"rules_count": report.rules_count,
|
||||
"coverage_count": len(report.coverage),
|
||||
"average_score": report.average_score,
|
||||
"blocked_count": report.blocked_count,
|
||||
"partial_count": report.partial_count,
|
||||
"missing_count": report.missing_count,
|
||||
"executive_summary": list(report.executive_summary),
|
||||
"active_risks": list(report.active_risks[:20]),
|
||||
"next_order_hints": list(report.next_order_hints[:20]),
|
||||
"coverage": [
|
||||
{
|
||||
"rule_id": item.rule_id,
|
||||
"platform_id": item.platform_id,
|
||||
"profile_id": item.profile_id,
|
||||
"scope": item.scope.value,
|
||||
"outcome": item.outcome.value,
|
||||
"truth_state": item.truth_state.value,
|
||||
"score": item.score,
|
||||
"next_order_hint": item.next_order_hint,
|
||||
}
|
||||
for item in report.coverage
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def rulebook_artifact_records(project_root: Path) -> tuple[dict[str, str], ...]:
|
||||
records: list[dict[str, str]] = []
|
||||
for rel, description, function, file_type in (
|
||||
("dados/rulebook-humano-operacional.json", "Rulebook completo de controle humano.", "rulebook humano", "json"),
|
||||
("dados/rulebook-humano-operacional-compacto.json", "Rulebook compacto para consumo por MCP/UI.", "rulebook compacto", "json"),
|
||||
("ecossistema/RULEBOOK-HUMANO-OPERACIONAL.md", "Relatorio Markdown do rulebook humano-operacional.", "rulebook humano", "markdown"),
|
||||
("matrizes/rulebook-humano-operacional.csv", "Matriz CSV de cobertura do rulebook.", "matriz rulebook", "csv"),
|
||||
):
|
||||
records.append(
|
||||
{
|
||||
"path": str(project_root / rel),
|
||||
"description": description,
|
||||
"function": function,
|
||||
"file_type": file_type,
|
||||
}
|
||||
)
|
||||
return tuple(records)
|
||||
|
||||
Reference in New Issue
Block a user