Apply the finding engine to schematic and PCB reviews.

FACT, REQUIREMENT, and INFERENCE are separate fields; datasheet provenance
lives in the rule DB so Recommended cannot stay ERROR; LLM output is REVIEW.
Designer decisions persist across rescans. Schematic and PCB share one library
and the same finding object in the report UI.
This commit is contained in:
2026-09-20 08:38:59 +02:00
parent 8f4ebc645c
commit dc65e96a22
16 changed files with 797 additions and 211 deletions
+16 -1
View File
@@ -20,6 +20,7 @@ from dotenv import load_dotenv
load_dotenv()
from backend.periscopex.finding_engine import complete_findings
from backend.periscopex.models import (
ComponentConstraints,
ComponentType,
@@ -149,6 +150,12 @@ neighbor's datasheet that you fetched via `get_datasheet_excerpt` — this \
links the page number to the right datasheet.
- **recommendation**: What to change (for ERROR/WARNING only).
This is a **design review**, not a design rule. Put observations in \
`finding` (FACT), datasheet text in `why` (REQUIREMENT), and judgment \
only there — do not invent millimetres, IEC numbers, or typical values. \
Recommended datasheet notes are never ERROR. If evidence is missing, say \
so (Unverified) instead of guessing.
### Calibration
ERROR only for clear violations: required pin floating, voltage exceeding \
absolute max, required external component completely missing, wrong \
@@ -922,12 +929,19 @@ def _parse_review(
mpn=mpn,
source_designator=src_designator,
finding=item["finding"],
facts=str(item.get("finding") or ""),
requirement=why,
inference=str(item.get("inference") or ""),
why=why,
status=status,
source_page=page,
source_quote=item.get("source_quote", ""),
recommendation=item.get("recommendation", ""),
action=str(item.get("recommendation") or ""),
reference=f"{src_mpn} datasheet p.{page if page is not None else '?'}",
source="review",
finding_class="REVIEW",
evidence_status="SUFFICIENT" if quote else "INSUFFICIENT",
))
except (KeyError, TypeError, ValueError) as exc:
print(f"Skipping malformed finding for {ic_ref}: {exc}", file=sys.stderr)
@@ -937,11 +951,12 @@ def _parse_review(
def assign_finding_ids(findings: list[Finding]) -> None:
"""Assign finding_id: {designator}-{001}, {002}, ..."""
"""Assign finding_id: {designator}-{001}, {002}, ... then run the finding engine."""
counter: Counter[str] = Counter()
for f in findings:
counter[f.designator] += 1
f.finding_id = f"{f.designator}-{counter[f.designator]:03d}"
complete_findings(findings)
# ---------------------------------------------------------------------------