findings.py python
84 lines 2.3 KB
Raw
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1 docs: MuseHub-first before ISR #74 — staging solidify NEXT Human 12 hours ago
1 """Finding validation, stable sort, and verdict derivation (§K5.6)."""
2
3 from __future__ import annotations
4
5 from tools.freeze_reviewer.types import Finding, Verdict
6
7 SEVERITY_RANK = {"BLOCKER": 3, "MAJOR": 2, "MINOR": 1}
8
9 SYNTHETIC_UNCITED_MESSAGE = (
10 "Reviewer engine emitted an invalid finding (missing or mismatched citation)."
11 )
12
13
14 def stable_sort_findings(findings: list[Finding]) -> list[Finding]:
15 """Sort findings per §K5.6 stable sort rule."""
16 return sorted(
17 findings,
18 key=lambda item: (
19 item.path,
20 item.line,
21 -SEVERITY_RANK.get(item.severity, 0),
22 item.check,
23 item.message,
24 ),
25 )
26
27
28 def assign_finding_ids(findings: list[Finding]) -> list[Finding]:
29 """Assign F1..Fn after stable sort."""
30 sorted_items = stable_sort_findings(findings)
31 for index, finding in enumerate(sorted_items, start=1):
32 finding.id = f"F{index}"
33 return sorted_items
34
35
36 def validate_and_repair_findings(
37 findings: list[Finding],
38 *,
39 artifact_path: str,
40 ) -> list[Finding]:
41 """Enforce citation hard rule; synthesize blocked finding on violation."""
42 repaired: list[Finding] = []
43 for finding in findings:
44 if finding.is_valid_citation():
45 repaired.append(finding)
46 continue
47 repaired.append(
48 Finding(
49 check="OTHER",
50 severity="BLOCKER",
51 category="other",
52 path=artifact_path,
53 line=1,
54 message=SYNTHETIC_UNCITED_MESSAGE,
55 ).with_citation()
56 )
57 return assign_finding_ids(repaired)
58
59
60 def derive_verdict(
61 findings: list[Finding],
62 *,
63 human_escalation: list[str],
64 ) -> Verdict:
65 """Map findings to pass | findings | blocked."""
66 if not findings:
67 return "pass"
68 escalation = set(human_escalation)
69 for finding in findings:
70 if finding.category in escalation:
71 return "blocked"
72 for finding in findings:
73 if finding.severity == "BLOCKER":
74 return "blocked"
75 return "findings"
76
77
78 def verdict_exit_code(verdict: Verdict) -> int:
79 """Map verdict to review-specific exit code."""
80 if verdict == "pass":
81 return 0
82 if verdict == "findings":
83 return 7
84 return 8
File History 2 commits
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1 docs: MuseHub-first before ISR #74 — staging solidify NEXT Human 12 hours ago
sha256:4671b7f787ddbe63ced31c895b688c77ab495653b65a730b423329f26b3c1439 feat: K1-P1 complete — agent provenance, build-verification… Sonnet 4.6 patch 52 days ago