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
+9 -2
View File
@@ -3,6 +3,7 @@
from __future__ import annotations
import json
import logging
import re
import uuid
from datetime import datetime, timezone
@@ -15,6 +16,7 @@ from backend.periscopex.finding_engine import (
apply_decisions,
complete_findings,
decision_from_review,
sort_findings,
upsert_decision,
)
from backend.periscopex.models import Finding
@@ -30,6 +32,7 @@ from backend.routers.deps import get_storage, get_user_id, resolve_or_404
from backend.services import projects as proj_svc
router = APIRouter(tags=["reports"])
log = logging.getLogger(__name__)
# Allow alphanumeric, dash, underscore, dot, colon, forward-slash, plus, hash, space
_SAFE_MPN = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_\-\.:/ +#,()]*$")
@@ -56,7 +59,11 @@ async def get_report(project_id: str, request: Request):
if merged is None:
raise HTTPException(404, "Report not found — run the pipeline first")
findings = _findings_from_report(merged)
complete_findings(findings)
try:
complete_findings(findings)
except Exception:
log.exception("complete_findings failed while serving report %s", project_id)
sort_findings(findings)
dec_key = f"{prefix}/decisions.json"
if storage.exists(dec_key):
try:
@@ -158,7 +165,7 @@ def _findings_from_report(report_data: dict) -> list[Finding]:
try:
out.append(Finding.model_validate(raw))
except Exception:
continue
log.warning("Skipping malformed finding in report", exc_info=True)
return out