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:
@@ -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}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user