# Piano — PCB review (esame geometria + stesso motore finding) Specifica finding: [`motore-finding.md`](motore-finding.md). Schema e PCB usano **lo stesso oggetto**. Questo file è il job layout. **Non** è un auto-placer. PCB-only = geometria (mm, width, copper, via, pour, return stitch, pad-net). Validazione netlist / mux / LED resta schema. Contratto: | | Schema | PCB review | Placement F2 (fuori) | | --- | --- | --- | --- | | Promessa | Netlist vs libreria datasheet | Layout vs libreria + geometria | xy nuovi | | Motore finding | FACT / REQUIREMENT / INFERENCE | uguale | — | | Deep exam PDF | **Una volta** → `library/extracted` | Consuma cache, `pdf_path=None` | — | | Job | `MODE=run` | `MODE=pcb` | `MODE=placement` | | Output | `report.json` | `pcb_report.json` (merge in GET /report) | pack json | Senza `.kicad_pcb` il progetto è schema-completo. Il report schema non si riempie di PE-PLC/PE-SI. --- ## Motore (schema + PCB) Vedi `motore-finding.md`. Invarianti: 1. Non mescolare FACT / REQUIREMENT / INFERENCE. 2. Provenance **MANDATORY | RECOMMENDED | TYPICAL | EXAMPLE** nel rule DB. **Recommended ≠ ERROR**. 3. Classi **RULE | RISK | REVIEW | INFO**. LLM = REVIEW, mai RULE. 4. `decisions.json` + fingerprint: rescan non ri-naga (PSEL low intenzionale, ecc.). 5. `status`, `confidence`, `evidence_status` indipendenti. 6. Evidenza insufficiente → lo si dice, niente 1 oz / 10 °C / 50 Ω / IEC inventati. 7. Oggetto riproducibile: facts, requirement, source, calculation, assumptions, confidence, severity, action. UI report (schema e layout): stessa `FindingCard` — Fact / Requirement / Inference, badge classe e provenance. Libreria: `library/extracted/{safe_mpn}.json` ∪ extracted/ progetto. PDF in `library/datasheets/blobs/`. --- ## Job PCB (invariato nello stack) ``` ┌─ MODE=run → status → report.json Project files ───┼─ MODE=placement → placement_status → placement_plan.json └─ MODE=pcb → pcb_status → pcb_report.json ``` Stages: `ensure_graph` → `parse_pcb` → `classify` → `inventory` → `checks` → `ai_review` (layout vs library, no PDF) → `write_report`. Mutex vs analisi e vs placement. SSE `pcb_*`. IDs `PCB-{ref}-{001}`. KiCad `/net` vs net schema: **PE-LAY-003** una volta, non per-pad (`kicad_nets_match`). ### Check PCB (geometria) — gated | ID | Gate | | --- | --- | | PE-LAY-001…003 | pad/ref/hierarchy nets | | PE-PLC-001…004 | `layout_rules` numerici | | PE-SI-001 | `length_match` mm on USB/HDMI/PCIe/ETH/LVDS/DDR nets (not I2C/GPIO/CC) | | PE-SI-002 | ImpedenceFinder Zavg/min/max vs `impedance` window | | PE-SI-003…009 | max length, spacing, ref plane, vias, return, series R — each a datasheet number | | PE-SI-010 | HS bus measured, library has no SI FACT (not 90 Ω folklore) | | PE-PWR-001 | I_load + width; IPC solo con thickness e Tjmax | | PE-DRT-001 | Vop e Vrated, Vop > Vr → RULE ERROR | | PE-DRT-002 | 0.8 < Vop/Vr ≤ 1 → RISK MARGIN | | PE-DRT-003 | Vop/Vr ≤ 0.8 → PASS summary INFO | | PE-HIER-001 | pin IC senza net | | PE-TIM-001/002 | RC / strap solo con numeri | | PE-PI-001/002 | local vs bulk; mm solo da layout_rules | | PE-ESD-001 / PE-RET-001 | REVIEW unless mandatory FACT | | PE-VIA-001 | via count + I_load (INFO, no tabella inventata) | | PE-THM-001 | P=I_load×drop, courtyard senza pour/via | | PE-THM-002 | Tj=Ta+P·θJA solo con P, θJA, area rame, conteggio via | | PE-BOM-010…014 | package / pinout / V / T / I quando BOM, PCB e datasheet hanno i numeri | | PE-SPOF-001 | un regolatore (o un XTAL condiviso) — REVIEW | | PE-EMI-001 | filtro EMI solo se `layout_rules` cita choke/ferrite/shield | | PE-KEL-001 | sense/Kelvin pintable + ≥2 altri sul net | | PE-STCH-001 | pour GND + segnale senza via GND nel bbox | | Creepage | **skip** senza numero datasheet/IEC | | Crystal keepout | PE-PLC-004 se kind=keepout | AI PCB: skip IC senza pintable (`run schematic review first`). --- ## Fasi **A — shipped:** oggetto finding condiviso, clamp provenance/class (**ERROR solo RULE+MANDATORY**), decisions, UI, libreria unica, geometria gated, net-slash, Action on card, geometria in contesto AI. **B — questa slice (shipped, gated):** | ID | Cosa | Gate | | --- | --- | --- | | Gerarchia | `hierarchy.json` component→pin→net→block | grafo (+ domains se `classify`) | | PE-HIER-001 | pin IC senza net | FACT net='' | | PE-DRT-001/002/003 | RISK / MARGIN / PASS | Vop **e** Vrated; niente % dielettrico inventato | | PE-TIM-001/002 | RC reset / strap | R, C e t_reset o Vih/Vil numerici | | PE-PI-001/002 | local vs bulk, I se noto | cap valorizzati; distanza solo con `max_distance_mm` | | PE-ESD-001 | ESD connettore/USB | REVIEW se manca parte ESD | | PE-RET-001 | return HS senza via GND | REVIEW, non ERROR | **B — SI (shipped, gated):** ImpedenceFinder is **in the checks**, not only the impedance tab dump. Each datasheet SI requirement is one finding: PASS / FAIL / MARGIN + FACT / REQUIREMENT / source. Buses: USB D+/D−, HDMI, PCIe, Ethernet, LVDS, DDR. **Skip** I2C, GPIO, EN, analog REGN, USB CC (not 90 Ω pairs). No invented 50/90 Ω. Emmaforo USB verdict: if library has e.g. 90 Ω ±10% (81–99), Zavg ~88 Ω **PASS** on average and **MARGIN/FAIL** if min (~46 Ω) is outside the window; ~2.5 mm intra-pair skew **FAIL** only when `length_match` mm exists and 2.5 > limit. Without library Z, **PE-SI-010** INFO (insufficient) — not a 90 Ω ERROR. **B — still missing (true leftovers):** | Gap | Why it stays out | | --- | --- | | Thermal FEM / energy | PE-THM-002 is Tj=Ta+P·θJA with measured copper/vias; no spreading/FEM | | CPWG / OpenEMS | Field-solver export excluded from the ImpedenceFinder vendor snapshot | | ImpedenceFinder license | Upstream has **no LICENSE** (UNKNOWN). Not invented in-tree. See `vendor/impedancefinder/SOURCE.md` | | Auto-place / pcbnew write-back | Fase C | | PinScope identity / TOS | Other worker | Shipped this close-out: BOM↔PCB↔datasheet (`PE-BOM-010`…`014`); SPOF REVIEW; EMI with datasheet FACT; gated θJA Tj; SI re-extract when library `layout_rules` lack SI kinds (`model_version` 1.12.0, `si_extract_needed.json`). **C — fuori:** auto-place, write-back pcbnew, unire i job, sshd/keys, PinScope identity/fork. Deploy: `ssh periscope` + `/root/periscope/scripts/update-periscope.sh`.