Replace findings list with a tree; filter ESD/PI false positives.

PE-ESD-001 only on J* connector–IC nets (skip NC, unconnected, VSYS/GND/3V3).
PE-PI-001 treats any capacitor on the rail, including KiCad slash prefixes, as
decoupling. Sort findings ERROR then WARNING then INFO, then RULE/RISK/REVIEW/INFO.
Report sidebar is a collapsed expand-on-click tree instead of an all-open list.
GET /report and complete_findings fail-soft and sort the same way.
This commit is contained in:
2026-09-20 10:03:50 +02:00
parent 342792df2d
commit bee6369c1d
13 changed files with 628 additions and 137 deletions
+29 -1
View File
@@ -7,6 +7,7 @@ never becomes ERROR. LLM output is REVIEW, never RULE.
from __future__ import annotations
import logging
from datetime import datetime, timezone
from typing import Any, Iterable, Literal
@@ -14,6 +15,11 @@ from pydantic import BaseModel
from backend.periscopex.models import Finding
log = logging.getLogger(__name__)
_STATUS_ORDER = {"ERROR": 0, "WARNING": 1, "INFO": 2}
_CLASS_ORDER = {"RULE": 0, "RISK": 1, "REVIEW": 2, "INFO": 3}
Provenance = Literal["MANDATORY", "RECOMMENDED", "TYPICAL", "EXAMPLE"]
FindingClass = Literal["RULE", "RISK", "REVIEW", "INFO"]
EvidenceStatus = Literal["SUFFICIENT", "INSUFFICIENT"]
@@ -256,9 +262,31 @@ def complete_finding(f: Finding) -> Finding:
return f
def sort_findings(findings: list[Finding]) -> list[Finding]:
"""ERROR then WARNING then INFO; within that RULE > RISK > REVIEW > INFO."""
findings.sort(
key=lambda f: (
_STATUS_ORDER.get(f.status or "INFO", 9),
_CLASS_ORDER.get(f.finding_class or "INFO", 9),
f.designator or "",
f.rule_id or "",
f.finding or "",
)
)
return findings
def complete_findings(findings: list[Finding]) -> None:
for f in findings:
complete_finding(f)
try:
complete_finding(f)
except Exception:
log.exception(
"complete_finding failed for %s/%s",
getattr(f, "designator", "?"),
getattr(f, "rule_id", None),
)
sort_findings(findings)
def apply_decisions(