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
+9
View File
@@ -2,6 +2,15 @@
What's new in Periscope.
## 2.32.0 — 2026-09-20 — Finding engine (schema + PCB)
Same finding object on schematic analysis and PCB exam: FACT / REQUIREMENT / INFERENCE, datasheet provenance in the rule DB, RULE vs REVIEW, designer decisions, independent severity/confidence/evidence.
- [New] `docs/motore-finding.md` — engine spec. `finding_engine.py` + `decisions.json`.
- [New] Report cards show Fact / Requirement / Inference, class, provenance, insufficient evidence.
- [Changed] Recommended datasheet notes cannot stay ERROR. LLM findings are REVIEW, never RULE.
- [Changed] Schematic and PCB share `library/extracted`; PCB does not re-read the PDF.
## 2.31.0 — 2026-09-20 — Shared library PCB exam (no second PDF pass)
Schematic and PCB share `library/extracted`. PCB AI consumes that cache (pintable required; no PDF attach). Report **PCB exam** section plus power-trace/thermal checks.
@@ -60,6 +60,10 @@ export function FindingCard({
const hasCommentSupport = !!(projectId && collaborators && onCommentAdded && onCommentDeleted);
const expandable =
!!finding.recommendation ||
!!finding.facts ||
!!finding.requirement ||
!!finding.inference ||
!!finding.calculation ||
(hasCommentSupport && !!finding.finding_id) ||
!!(projectId && finding.finding_id && onReviewSaved);
@@ -187,10 +191,64 @@ export function FindingCard({
{finding.rule_id}
</span>
)}
{finding.finding_class && (
<span className="inline-flex items-center rounded border border-border px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground">
{finding.finding_class}
</span>
)}
{finding.provenance && (
<span className="inline-flex items-center rounded border border-border px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground">
{finding.provenance}
</span>
)}
{finding.evidence_status === "INSUFFICIENT" && (
<span className="inline-flex items-center rounded border border-amber-500/30 bg-amber-500/10 px-1.5 py-0.5 text-[10px] font-medium text-amber-800 dark:text-amber-300">
Insufficient evidence
</span>
)}
{finding.suppressed && (
<span className="inline-flex items-center rounded border border-border px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground">
Decision
</span>
)}
</div>
{open && (
<div onClick={(e) => e.stopPropagation()}>
{(finding.facts || finding.requirement || finding.inference) && (
<dl className="mt-2 space-y-1.5 text-xs text-muted-foreground">
{finding.facts ? (
<div>
<dt className="font-medium text-foreground">Fact</dt>
<dd>{finding.facts}</dd>
</div>
) : null}
{finding.requirement ? (
<div>
<dt className="font-medium text-foreground">Requirement</dt>
<dd>{finding.requirement}</dd>
</div>
) : null}
{finding.inference ? (
<div>
<dt className="font-medium text-foreground">Inference</dt>
<dd>{finding.inference}</dd>
</div>
) : null}
{finding.calculation ? (
<div>
<dt className="font-medium text-foreground">Calculation</dt>
<dd className="font-mono">{finding.calculation}</dd>
</div>
) : null}
{typeof finding.confidence === "number" ? (
<div>
<dt className="font-medium text-foreground">Confidence</dt>
<dd>{Math.round(finding.confidence * 100)}%</dd>
</div>
) : null}
</dl>
)}
{finding.recommendation && (
<p className="text-sm text-muted-foreground mt-2 pl-1 border-l-2 border-muted leading-relaxed">
{finding.recommendation}
+15
View File
@@ -1,4 +1,7 @@
export type FindingStatus = "ERROR" | "WARNING" | "INFO";
export type FindingClass = "RULE" | "RISK" | "REVIEW" | "INFO";
export type DatasheetProvenance = "MANDATORY" | "RECOMMENDED" | "TYPICAL" | "EXAMPLE";
export type EvidenceStatus = "SUFFICIENT" | "INSUFFICIENT";
export interface Finding {
finding_id: string | null;
@@ -20,6 +23,18 @@ export interface Finding {
cad_sheet?: string | null;
cad_uuid?: string | null;
variant?: string | null;
facts?: string;
requirement?: string;
inference?: string;
provenance?: DatasheetProvenance | null;
finding_class?: FindingClass | null;
confidence?: number | null;
evidence_status?: EvidenceStatus | null;
calculation?: string;
assumptions?: string[];
action?: string;
decision_id?: string | null;
suppressed?: boolean;
}
export interface FindingComment {