Make the component library a standalone product door (2.63.0).
Add /api/library datasheet import and component GET/PUT with no exam. Reorganize pytest into datasheet, library, schematic, PCB, and AF+AI. Document Rust criteria (none chosen; no rustup) and coding conformity.
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
"""Finding engine — FACT/REQUIREMENT/INFERENCE, provenance clamp, decisions."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from backend.periscopex.finding_engine import (
|
||||
DesignerDecision,
|
||||
apply_decisions,
|
||||
complete_finding,
|
||||
finding_fingerprint,
|
||||
lookup_rule,
|
||||
)
|
||||
from backend.periscopex.models import Finding
|
||||
from backend.periscopex.review_parse import assign_finding_ids, parse_submit_review as _parse_review
|
||||
|
||||
|
||||
def test_recommended_never_error():
|
||||
f = Finding(
|
||||
designator="U1",
|
||||
finding="layout note",
|
||||
why="place close",
|
||||
status="ERROR",
|
||||
rule_id="PE-THM-001",
|
||||
source="pcb_power_thermal",
|
||||
evidence_status="SUFFICIENT",
|
||||
facts="no pour",
|
||||
requirement="datasheet layout page",
|
||||
)
|
||||
complete_finding(f)
|
||||
assert f.provenance == "RECOMMENDED"
|
||||
assert f.status != "ERROR"
|
||||
assert f.finding_class != "RULE"
|
||||
|
||||
|
||||
def test_llm_review_is_never_rule():
|
||||
result = _parse_review(
|
||||
{
|
||||
"findings": [{
|
||||
"finding": "strap",
|
||||
"why": "datasheet",
|
||||
"status": "ERROR",
|
||||
"source_page": 2,
|
||||
"source_quote": "PSEL must be high for 3.3 V.",
|
||||
"recommendation": "Tie PSEL high.",
|
||||
}],
|
||||
"checked_areas": [],
|
||||
},
|
||||
"U1",
|
||||
"PART",
|
||||
)
|
||||
f = result.findings[0]
|
||||
complete_finding(f)
|
||||
assert f.finding_class == "REVIEW"
|
||||
assert f.facts
|
||||
assert f.requirement
|
||||
assert f.status == "WARNING"
|
||||
|
||||
|
||||
def test_review_and_risk_cannot_stay_error():
|
||||
review = Finding(
|
||||
designator="U1",
|
||||
finding="PowerPAD vias",
|
||||
why="layout note",
|
||||
status="ERROR",
|
||||
source="pcb_review",
|
||||
facts="0 vias under U1",
|
||||
requirement="datasheet PowerPAD",
|
||||
source_quote="Connect the thermal pad with vias to GND.",
|
||||
evidence_status="SUFFICIENT",
|
||||
)
|
||||
complete_finding(review)
|
||||
assert review.finding_class == "REVIEW"
|
||||
assert review.status == "WARNING"
|
||||
|
||||
risk = Finding(
|
||||
designator="C1",
|
||||
finding="high utilization",
|
||||
why="Vop near Vr",
|
||||
status="ERROR",
|
||||
rule_id="PE-DRT-002",
|
||||
source="pcb_derating",
|
||||
evidence_status="SUFFICIENT",
|
||||
facts="3.0 / 3.3 V",
|
||||
requirement="utilization band",
|
||||
)
|
||||
complete_finding(risk)
|
||||
assert risk.finding_class == "RISK"
|
||||
assert risk.status == "WARNING"
|
||||
|
||||
|
||||
def test_mandatory_rule_stays_error():
|
||||
f = Finding(
|
||||
designator="U1",
|
||||
finding="NC tied",
|
||||
why="NC must float",
|
||||
status="ERROR",
|
||||
rule_id="PE-NC-001",
|
||||
source="nc_pin_check",
|
||||
facts="NC on GND",
|
||||
requirement="NC pins must not be connected.",
|
||||
evidence_status="SUFFICIENT",
|
||||
)
|
||||
complete_finding(f)
|
||||
assert f.finding_class == "RULE"
|
||||
assert f.provenance == "MANDATORY"
|
||||
assert f.status == "ERROR"
|
||||
|
||||
|
||||
def test_insufficient_evidence_does_not_stay_error():
|
||||
f = Finding(
|
||||
designator="U1",
|
||||
finding="narrow trace",
|
||||
why="",
|
||||
status="ERROR",
|
||||
source="pcb_power_thermal",
|
||||
rule_id="PE-PWR-001",
|
||||
evidence_status="INSUFFICIENT",
|
||||
)
|
||||
complete_finding(f)
|
||||
assert f.status != "ERROR"
|
||||
assert "Insufficient evidence" in f.inference
|
||||
assert f.finding_class != "RULE" or f.evidence_status == "INSUFFICIENT"
|
||||
|
||||
|
||||
def test_decision_suppresses_rescan_nag():
|
||||
f = Finding(
|
||||
designator="U1",
|
||||
finding="PSEL low",
|
||||
why="must be high",
|
||||
status="ERROR",
|
||||
rule_id="PE-MUX-001",
|
||||
source="pin_mux_check",
|
||||
net="PSEL",
|
||||
aspect="config",
|
||||
facts="PSEL net is GND",
|
||||
requirement="PSEL high for 3V3",
|
||||
)
|
||||
complete_finding(f)
|
||||
assert f.status == "ERROR"
|
||||
d = DesignerDecision(
|
||||
decision_id="DEC-1",
|
||||
fingerprint=finding_fingerprint(f),
|
||||
rule_id="PE-MUX-001",
|
||||
designator="U1",
|
||||
net="PSEL",
|
||||
aspect="config",
|
||||
intent="wontfix",
|
||||
reason="PSEL low intentional",
|
||||
)
|
||||
apply_decisions([f], [d])
|
||||
assert f.suppressed is True
|
||||
assert f.status == "INFO"
|
||||
assert f.finding_class == "INFO"
|
||||
assert "PSEL low intentional" in f.inference
|
||||
|
||||
|
||||
def test_schematic_assign_ids_fills_engine_fields():
|
||||
f = Finding(
|
||||
designator="U3",
|
||||
finding="NC tied",
|
||||
why="NC must float",
|
||||
status="ERROR",
|
||||
rule_id="PE-NC-001",
|
||||
source="nc_pin_check",
|
||||
)
|
||||
assign_finding_ids([f])
|
||||
assert f.finding_id == "U3-001"
|
||||
assert f.facts == "NC tied"
|
||||
assert f.requirement
|
||||
assert f.provenance == "MANDATORY"
|
||||
assert f.finding_class == "RULE"
|
||||
assert f.confidence is not None
|
||||
|
||||
|
||||
def test_same_object_pcb_and_schema():
|
||||
assert lookup_rule("PE-NC-001").domain == "schema"
|
||||
assert lookup_rule("PE-LAY-001").domain == "pcb"
|
||||
sch = Finding(designator="U1", finding="x", status="INFO", rule_id="PE-NC-001", source="nc_pin_check")
|
||||
pcb = Finding(designator="U1", finding="y", status="INFO", rule_id="PE-LAY-001", source="pcb_net_match")
|
||||
complete_finding(sch)
|
||||
complete_finding(pcb)
|
||||
for obj in (sch, pcb):
|
||||
assert obj.facts
|
||||
assert obj.finding_class
|
||||
assert obj.provenance
|
||||
assert obj.evidence_status
|
||||
assert (obj.action or "").strip()
|
||||
|
||||
|
||||
def test_empty_recommendation_gets_action():
|
||||
f = Finding(designator="U1", finding="note", status="INFO", source="review")
|
||||
complete_finding(f)
|
||||
assert f.action.strip()
|
||||
assert f.recommendation.strip() == f.action.strip()
|
||||
|
||||
|
||||
def test_pcb_review_ai_finding_action_without_recommendation():
|
||||
f = Finding(
|
||||
designator="U1",
|
||||
finding="PowerPAD vias",
|
||||
why="thermal pad",
|
||||
status="INFO",
|
||||
source="pcb_review",
|
||||
recommendation="",
|
||||
action="",
|
||||
)
|
||||
complete_finding(f)
|
||||
assert f.action.strip()
|
||||
assert f.finding_class == "REVIEW"
|
||||
|
||||
|
||||
def test_recommended_rc_cap_is_review_not_rule():
|
||||
f = Finding(
|
||||
designator="C1",
|
||||
finding="10n vs 100n",
|
||||
why="datasheet typical 100n",
|
||||
status="WARNING",
|
||||
rule_id="PE-DEC-002",
|
||||
source="passive_rail_check",
|
||||
)
|
||||
complete_finding(f)
|
||||
assert f.finding_class == "REVIEW"
|
||||
assert f.provenance == "RECOMMENDED"
|
||||
assert f.status != "ERROR"
|
||||
Reference in New Issue
Block a user