report.py file-level

at sha256:8 · View file ↗ · Intel ↗

History
1 files
1 commits
0 hotspots
0 🧊 dead
0 💥 blast risk
sha256:2 fix(muse): use rev-parse for muse-only branch status · · Sep 22, 2026
1 """§K5.9 / §FRV report payload and stdout rendering."""
2
3 from __future__ import annotations
4
5 from typing import Any
6
7 from adapters.config import FreezeContractConfig
8 from tools.freeze_reviewer.engine import HUMAN_INSTRUCTIONS, resolve_exit_code
9 from tools.freeze_reviewer.types import Finding, ReviewResult, ReviewerSettings
10
11 HUMAN_INSTRUCTIONS_TEXT = HUMAN_INSTRUCTIONS
12
13
14 def reviewer_payload(settings: ReviewerSettings, config: FreezeContractConfig) -> dict[str, Any]:
15 """Reviewer section for JSON report."""
16 if settings.mode == "human":
17 return {
18 "mode": "human",
19 "model": config.reviewer.model,
20 "provider": config.reviewer.provider,
21 "fallback": config.reviewer.fallback,
22 }
23 return {
24 "mode": settings.mode,
25 "model": settings.model,
26 "provider": settings.provider,
27 "fallback": settings.fallback,
28 }
29
30
31 def finding_to_dict(finding: Finding) -> dict[str, Any]:
32 return {
33 "id": finding.id,
34 "check": finding.check,
35 "severity": finding.severity,
36 "category": finding.category,
37 "path": finding.path,
38 "line": finding.line,
39 "message": finding.message,
40 "citation": finding.citation,
41 }
42
43
44 def build_report(
45 *,
46 freeze_path: str,
47 result: ReviewResult,
48 reviewer: ReviewerSettings,
49 config: FreezeContractConfig,
50 enabled: bool,
51 ) -> dict[str, Any]:
52 """Build the unified §K5.9 / §FRV report object."""
53 exit_code = resolve_exit_code(result, refused=result.refused)
54 stamp_payload = result.stamp.to_mapping() if result.stamp else None
55 reason: str | None
56 if result.escalation_refused:
57 reason = result.escalation_refuse_cause or "stamp_escalation_refused"
58 else:
59 reason = result.reason
60 payload: dict[str, Any] = {
61 "command": "review",
62 "freeze": freeze_path,
63 "gate": "mechanical",
64 "verdict": result.verdict,
65 "mechanical_verdict": result.verdict if result.stamp else result.verdict,
66 "exit_code": exit_code,
67 "escalation": result.escalation,
68 "reason": reason,
69 "provider_cause": result.provider_cause,
70 "checklist": list(result.checklist_ids),
71 "instructions": HUMAN_INSTRUCTIONS_TEXT if result.escalation == "human" else None,
72 "enabled": enabled,
73 "declaration": result.declaration,
74 "reviewer": reviewer_payload(reviewer, config),
75 "findings": [finding_to_dict(item) for item in result.findings],
76 "stamp": stamp_payload,
77 "dry_run": result.dry_run,
78 "operator_block": result.operator_block,
79 "escalation_refused": result.escalation_refused,
80 }
81 if result.escalation_refused:
82 payload["existing_verdict"] = result.existing_stamp_verdict
83 return payload
84
85
86 def render_human_report(*, freeze_path: str, result: ReviewResult) -> str:
87 """Render human stdout per §K5.9 / §FRV.3.5."""
88 lines = [f"Freeze review: {freeze_path}"]
89 # Mechanical pass runs: Gate + Mechanical verdict (never bare "Verdict:")
90 if result.stamp is not None or (result.verdict == "pass" and not result.escalation):
91 lines.append("Gate: mechanical")
92 lines.append(f"Mechanical verdict: {result.verdict}")
93 else:
94 lines.append(f"Verdict: {result.verdict}")
95 lines.append(f"Findings ({len(result.findings)}):")
96 for finding in result.findings:
97 lines.append(
98 f" {finding.id} {finding.severity} {finding.category} "
99 f"{finding.citation} {finding.message}"
100 )
101 if result.escalation == "human":
102 lines.append("Escalation: human")
103 lines.append(f"Reason: {result.reason}")
104 cause = result.provider_cause or "(none)"
105 lines.append(f"Provider cause: {cause}")
106 lines.append(f"Checklist: {', '.join(result.checklist_ids)}")
107 lines.append(f"Instructions: {HUMAN_INSTRUCTIONS_TEXT}")
108 else:
109 lines.append("Escalation: none")
110 if result.escalation_refused:
111 existing = result.existing_stamp_verdict or "(unknown)"
112 lines.append(
113 f"Notice: stamp_escalation_refused — existing mechanical verdict is {existing}"
114 )
115 if result.operator_block is True:
116 lines.append("Operator block: auto_may_start is not true (Auto authorization blocked)")
117 if result.verdict == "pass" and result.stamp and result.escalation_refused:
118 lines.append("Stamp: (not written — stamp_escalation_refused)")
119 elif result.verdict == "pass" and result.stamp and not result.dry_run and result.stamp_written:
120 lines.append("Stamp: written")
121 elif result.verdict == "pass" and result.stamp and result.dry_run:
122 lines.append("Stamp: (dry-run — would write)")
123 elif result.verdict == "pass" and result.stamp and result.no_stamp:
124 lines.append("Stamp: (not written — no-stamp)")
125 elif result.verdict == "pass" and result.stamp and not result.stamp_written:
126 lines.append("Stamp: (unchanged — idempotent)")
127 else:
128 lines.append("Stamp: (not written — verdict != pass)")
129 return "\n".join(lines)