WROOM lands are not the ESP32 die table (U11). QFN-48 pads 25–48 are not extras when extraction stopped at 1–24 (U12). VQFN is the QFN family (U19 24-QFN 4×4). Real extra signal pads still ERROR.
670 lines
37 KiB
Markdown
670 lines
37 KiB
Markdown
# Albero delle analisi Periscope
|
||
|
||
Versione codice **2.63.1** (`periscope/src/frontend/content/changelog.md`). Letto in `/Users/michelebigi/Development/periscope`. Costituzione senza eccezioni: via ≠ pad ≠ traccia ≠ zona; niente I/Z/mm inventati; skip se manca evidenza. **Non è DRC KiCad.** DRC = KiCad. Questo documento mappa l’analisi, non implementa.
|
||
|
||
I test pytest stanno in `tests/datasheet/`, `tests/library/`, `tests/schematic/`, `tests/pcb/`, `tests/af_ai/` (stessi file di 2.62.1, cartelle di mestiere). I path `tests/test_*.py` sotto i nodi restano i nomi file.
|
||
|
||
Due pipeline più una porta libreria (non è un esame):
|
||
|
||
0. **Libreria** — `POST /api/library/datasheets` registra un componente da PDF senza `MODE=run`/`MODE=pcb`.
|
||
1. **Schematico / review AI** — `MODE=run`, `services/pipeline.py` → `validate_design_async` (`validation.py`): estrazione datasheet, check deterministici, review IC DeepSeek.
|
||
2. **Esame PCB** — `MODE=pcb`, `services/pcb_pipeline.py` → `run_pcb_checks` (`pcb_checks.py`, lista 2.62.1 **invariata**) + sezione additiva AF Board+AI (`af_ai_hf.py`) + review AI PCB (`pcb_review.py` / `pcb_validation.py`). Non auto-place.
|
||
|
||
I certifier USB-C / Ethernet / PoE girano su **entrambe** le pipeline (grafo, non geometria). DDR / CPU / FPGA / USB-PD sono la stessa qualità di finding ma partono da `run_pcb_checks` (grafo). Niente pezzo sul grafo → silenzio, non N/A.
|
||
|
||
Nodi analisi: **108** (99 PE-* + 1 LED senza `rule_id` + 8 processo estrazione/AI). Test pytest citati sotto i nodi: nomi reali da `tests/`.
|
||
|
||
---
|
||
|
||
```
|
||
Periscope 2.63.1
|
||
├── 0. Libreria (porta, non esame)
|
||
│ └── PDF + MPN → scheda (inbox IC / passivo / discreto) senza MODE=run/pcb
|
||
├── 1. Schematico / review AI (MODE=run)
|
||
│ ├── ingest (non è analisi: parser → grafo)
|
||
│ ├── estrazione datasheet
|
||
│ ├── check deterministici schema
|
||
│ ├── certifier di classe (condivisi con PCB)
|
||
│ └── review AI per-IC + post-pass
|
||
└── 2. Esame PCB (MODE=pcb)
|
||
├── ingest layout (non è DRC)
|
||
├── run_pcb_checks ← 2.62.1 invariato (SI + HF line inclusi)
|
||
│ ├── net / footprint
|
||
│ ├── placement vs layout_rules
|
||
│ ├── SI + linee HF (2.62.0 Fase A)
|
||
│ ├── DDR / CPU / FPGA (2.62.0)
|
||
│ ├── potenza / via / termico
|
||
│ ├── derating / timing / PI
|
||
│ ├── ESD / return / EMI / SPOF
|
||
│ ├── BOM ↔ PCB ↔ datasheet
|
||
│ └── resto Fase B (2.62.1)
|
||
├── AF Board + AI (additivo: flag → investigate_hf_hypotheses; non sostituisce SI/HF)
|
||
├── review AI PCB
|
||
└── tab antenna RF (non in run_pcb_checks)
|
||
```
|
||
|
||
---
|
||
|
||
## 1. Schematico / review AI (`MODE=run`)
|
||
|
||
Orchestrazione: `pipeline.py` stadi `bom_parse` → `ic_extraction` → `simple_extraction` → `passive_extraction` → `graph_build` → `validation`. Check deterministici: `_run_deterministic_checks` in `validation.py`. Finding ID schema `PS-{ref}-{nnn}` (non `PCB-`).
|
||
|
||
### 1.1 Ingest (prerequisito, non analisi)
|
||
|
||
Parser e grafo non emettono PE-*. Skip MPN: `ic_mpn_skip_reason` (silenzio onesto, niente MPN inventato).
|
||
|
||
- Test parser/grafo: `tests/test_bom_parser.py` (`test_parse_bom_uses_value_for_ic_when_mpn_column_empty`, `test_parse_bom_keeps_pnm_and_datasheet_url`); `tests/test_hubaudio_bom.py` (tutti e 4); `tests/test_netlist_parser.py` (tutti e 3); `tests/test_edif_parser.py` (14); `tests/test_kicad_parser.py` (17); `tests/test_kicad_project.py` (19); `tests/test_netlist_bundle.py` (9); `tests/test_functional_groups.py` (8).
|
||
- Test overlay parser: `tests/test_periscope_parsers_rewrite.py`, `tests/test_periscope_graph_rewrite.py`, `tests/test_periscope_models_rewrite.py`.
|
||
|
||
### 1.2 Estrazione datasheet (processo)
|
||
|
||
Skip se manca PDF o lo skill non ha numero. Libreria `library/extracted` condivisa schema+PCB. PCB review non rilegge il PDF (`pdf_path=None`).
|
||
|
||
#### `extract-pintable` — skill `skills/extract-pintable/`
|
||
|
||
Modulo: `services/datasheet_extract.py` + `llm/local_skill.py`. Analizza: pin table, package, taxonomy IC.
|
||
|
||
- Skip se: niente PDF; IC già in libreria alla `model_version` minima.
|
||
- Test: `tests/test_native_datasheet_extract.py` (`test_coerce_abs_max_matches_inherited`, `test_native_extract_has_no_anthropic_import`); `tests/test_pdf_ingest.py` (6); `tests/test_deepseek_provider.py` (`test_local_skills_load`, `test_extract_pdf_text_includes_page_markers`, `test_wroom_rejects_bare_soc_pin1_ant`); `tests/test_periscope_taxonomy_skills_src.py` (`test_pintable_skill_mentions_save_tool_and_wroom`, `test_skills_dir_is_src_and_local`).
|
||
|
||
#### `extract-specs` — skill `skills/extract-specs/`
|
||
|
||
Analizza: specs discreti/connettori/cristalli; parametri fuori taxonomy → scartati.
|
||
|
||
- Skip se: niente PDF o tipo non in `SIMPLE_TYPES`.
|
||
- Test: `tests/test_pdf_ingest.py` (`test_coerce_abs_max_keeps_valid_drops_junk`); `tests/test_layout_rules.py` (5 — `layout_rules` numerici, niente mm inventati).
|
||
|
||
#### `extract-pattern` — skill `skills/extract-pattern/`
|
||
|
||
Analizza: pattern MPN passivi (R/C/L/FB).
|
||
|
||
- Skip se: MPN opaco; DigiKey/value fallback copre prima.
|
||
- Test: `tests/test_inductor_specs.py` (13, Z ferrite solo se il PDF dice N Ω @ freq); `tests/test_pcb_software_bugs.py` (`test_lqw_decoder_and_unknown_skip`, `test_fb1_blm_is_bead_not_dcr_resistor`).
|
||
|
||
#### Resolve DigiKey / catalogo (esatto MPN)
|
||
|
||
Modulo: `services/digikey.py`, `passive_from_distributor.py`, `passive_from_mpn.py`. Analizza: parametri catalogo per auto-resolve. Hit fuzzy rifiutati.
|
||
|
||
- Skip se: MPN non esatto; API assente (fail-soft).
|
||
- Test: `tests/test_digikey_match.py` (3); `tests/test_passive_from_distributor.py` (7); `tests/test_passive_from_mpn.py` (7); `tests/test_datasheet_finder.py` (17); `tests/test_library.py` (5).
|
||
|
||
#### Resolve da stringa Value (solo progetto)
|
||
|
||
Modulo: `services/passive_from_value.py`. Specs value-derived **non** in libreria condivisa.
|
||
|
||
- Skip se: stringa ambigua / placeholder.
|
||
- Test: `tests/test_passive_from_value.py` (8).
|
||
|
||
### 1.3 Check deterministici schema
|
||
|
||
Fail-soft per modulo. Senza evidenza: skip (lista vuota), non folklore.
|
||
|
||
#### `PE-MUX-001` — `pin_mux_check.py`
|
||
|
||
Analizza: funzione pin vs net (UART/SPI/I2C mux).
|
||
Skip se: `functions` vuote; link inter-device stesso peripheral; net opaco.
|
||
|
||
- Test: `tests/test_pin_mux_check.py` — `test_real_defect_uart5_swapped_is_error`, `test_correct_assignment_no_finding`, `test_inter_device_same_peripheral_link_is_skipped`, `test_transceiver_peer_without_peripheral_still_fires`, `test_empty_functions_skipped`, `test_pin_exposes_peripheral_but_not_signal_no_complement`, `test_opaque_net_not_flagged`, `test_token_parser_and_normalizer`, `test_spi_legacy_net_names_match_modern_pin_functions`, `test_spi_controller_peripheral_names_are_synonyms`, `test_simple_project_uart0_nets_are_feasible_on_mspm0_pins`, `test_simple_project_uart0_swapped_on_mspm0_is_error`, `test_spi_genuine_infeasibility_still_fires_with_modern_names`, `test_finding_prints_full_raw_capability_list_and_intent_caveat`, `test_legacy_report_without_source_validates`.
|
||
|
||
#### `PE-NC-001` — `nc_pin_check.py`
|
||
|
||
Analizza: pin NC collegato a net attivo.
|
||
Skip se: niente pintable.
|
||
|
||
- Test: `tests/test_nc_pin_check.py` — `test_nc_pin_on_active_net_warns`, `test_nc_pin_on_nc_net_silent`, `test_no_pintable_silent`.
|
||
|
||
#### `PE-DEC-001` / `PE-DEC-002` — `passive_rail_check.py` `check_supply_decoupling`
|
||
|
||
Analizza: condensatore sul net di alimentazione IC (001 presenza; 002 valore vs datasheet).
|
||
Skip se: net NC; pin enable strap; manca valore per 002.
|
||
|
||
- Test: `tests/test_passive_rail_check.py` — `test_missing_decoupling_is_warning`, `test_cap_to_gnd_clears_decoupling`, `test_enable_strapped_to_rail_is_not_decoupling`, `test_nc_supply_net_is_skipped`, `test_ldo_vout_needs_cout`, `test_ldo_vout_100n_only_is_value_warning`, `test_vdd_100n_is_not_a_value_warning`, `test_ki_cad_voltage_prefix_is_power`, `test_fb_and_rn_prefixes`.
|
||
|
||
#### `PE-I2C-001` / `PE-I2C-002` — `passive_rail_check.py` `check_i2c_pullups`
|
||
|
||
Analizza: pull-up I2C (001 presenza; 002 valore vs banda).
|
||
Skip se: pin SDA è alias SPI; pull-up senza valore (002 non sizato); bus assente.
|
||
|
||
- Test: `tests/test_passive_rail_check.py` — `test_i2c_missing_pullup`, `test_i2c_pullup_present`, `test_i2c_pullup_to_3v3_digital_typed_as_signal`, `test_spi_pin_alias_sda_is_not_i2c`, `test_i2c_from_slash_alias_in_pin_name`, `test_i2c_4k7_pullup_is_in_nxp_wide_band`, `test_i2c_100ohm_pullup_is_too_stiff`, `test_i2c_100k_pullup_is_too_weak`, `test_i2c_pullup_without_value_is_not_sized`.
|
||
|
||
#### `PE-RST-001` / `PE-RST-002` — `passive_rail_check.py` `check_reset_pullups`
|
||
|
||
Analizza: reset/enable pull (001 presenza; 002 verso vs datasheet).
|
||
Skip se: GPIO guida il reset.
|
||
|
||
- Test: `tests/test_passive_rail_check.py` — `test_reset_no_finding_when_gpio_drives`, `test_reset_floating_is_warning`, `test_nrst_pulldown_is_warning`, `test_nrst_pullup_is_not_pulldown`.
|
||
|
||
#### `PE-BOM-001` / `PE-BOM-002` — `bom_match_check.py`
|
||
|
||
Analizza: MPN schema vs BOM (001 mismatch; 002 riga BOM orfana).
|
||
Skip se: mappa schematica vuota (PADS/EDIF senza properties).
|
||
|
||
- Test: `tests/test_bom_match_check.py` — `test_matching_mpns_produce_no_findings`, `test_mpn_mismatch_is_error_ps_bom_001`, `test_orphan_bom_ref_is_warning`, `test_mpn_case_and_whitespace_are_not_a_mismatch`, `test_empty_bom_mpn_with_schematic_mpn_is_not_a_mismatch`, `test_empty_schematic_map_skips_check`, `test_legacy_design_graph_without_source_fields_still_validates`, `test_build_graph_kicad_mpn_mismatch_surfaces`.
|
||
|
||
#### `PE-DNP-001` — `dnp_check.py`
|
||
|
||
Analizza: DNP vs enable floating.
|
||
Skip se: colonna DNP assente.
|
||
|
||
- Test: `tests/test_dnp_check.py` — `test_dnp_pull_leaves_enable_floating`, `test_fitted_pull_is_silent`, `test_no_dnp_column_skips_floating_enable`, `test_parse_bom_reads_dnp_and_skips_when_column_absent`.
|
||
|
||
#### LED corrente — `led_current_check.py` (nessun `PE-*`)
|
||
|
||
Analizza: I_f Ohm vs rating canale. Source `led_current_check`.
|
||
Skip se: niente `forward_current_*`; tensione rail sconosciuta (non si indovina).
|
||
|
||
- Test: `tests/test_led_current_check.py` — `test_over_current_is_error`, `test_proper_resistor_no_finding`, `test_unknown_rail_no_error`, `test_no_rating_skipped`, `test_parse_resistance`.
|
||
|
||
#### `PE-TH-001` / `PE-TH-002` / `PE-TH-003` — `thermal_check.py`
|
||
|
||
Analizza: P = I_load × drop; Tj = Ta + P·θJA (001 INFO senza θJA o Tj ok; 002 WARNING Tj alto — **non in catalogo `finding_engine`**; 003 I²R shunt).
|
||
Skip se: manca I_load (Iout_max non è carico).
|
||
|
||
- Test: `tests/test_thermal_check.py` — `test_ldo_without_theta_ja_is_info`, `test_iout_max_is_not_used_as_load`, `test_ldo_hot_tj_is_warning`, `test_shunt_over_rating_is_warning`, `test_led_resistor_within_rating_is_silent`.
|
||
|
||
#### `PE-PWR-001` (schema) — `power_margin_check.py`
|
||
|
||
Analizza: Σ IQ + I_load vs Iout regolatore; caduta su R serie. Source `power_margin_check`.
|
||
Skip se: manca I_load; IQ assente non è fail.
|
||
|
||
- Test: `tests/test_power_margin_check.py` — `test_load_over_iout_max_is_ps_pwr_001`, `test_missing_iq_is_not_guessed_into_margin_fail`, `test_series_r_ir_drop_uses_i_load_not_trace`, `test_no_series_r_does_not_invent_trace_drop`.
|
||
|
||
#### `PE-SEQ-001` — `sequencing_check.py`
|
||
|
||
Analizza: PG → EN da note datasheet.
|
||
Skip se: niente spec `power_sequence`.
|
||
|
||
- Test: `tests/test_sequencing_check.py` — `test_pg_not_tied_to_en_is_warning`, `test_pg_tied_to_en_is_silent`, `test_no_power_sequence_spec_skips_even_if_pg_open`.
|
||
|
||
#### `PE-FLT-001` / `PE-FLT-002` / `PE-FLT-003` — `filter_check.py`
|
||
|
||
Analizza: topologia filtro (001), pezzo (002), fc vs ADC / DCR ferrite (003).
|
||
Skip se: manca C; pull-up+decoupling non è filtro; DCR ferrite senza limite datasheet.
|
||
|
||
- Test: `tests/test_filter_check.py` — `test_rc_reports_fc_info_without_adc_rate`, `test_rc_vs_adc_rate_is_warning`, `test_pullup_plus_decoupling_is_not_a_filter`, `test_missing_c_value_does_not_invent_fc_warning`, `test_pi_and_t_need_l_and_c`, `test_ferrite_dcr_warns_only_with_datasheet_limit`.
|
||
|
||
#### `PE-XTAL-001` / `PE-XTAL-002` / `PE-XTAL-003` — `crystal_cl_check.py`
|
||
|
||
Analizza: CL vs C1/C2 (001 coppia; 002 serie/stray; 003 drive). Cstray solo se in specs.
|
||
Skip se: niente CL in specs.
|
||
|
||
- Test: `tests/test_crystal_cl_check.py` — `test_no_cl_in_specs_is_silent`, `test_series_above_cl_without_stray_warns`, `test_with_stray_mismatch_warns`, `test_simple_project_without_cl_silent`.
|
||
|
||
#### `PE-LF-001` / `PE-LF-002` / `PE-LF-003` — `lifecycle.py`
|
||
|
||
Analizza: Obsolete / NRND / RoHS non-compliant.
|
||
Skip se: catalogo assente.
|
||
|
||
- Test: `tests/test_lifecycle_check.py` — `test_obsolete_is_warning_and_uses_distributor_replacement`, `test_nrnd_is_info`, `test_explicit_rohs_non_compliant_is_warning`, `test_active_is_silent`, `test_rohs_not_applicable_is_not_a_fail`, `test_missing_catalog_and_missing_substitute_are_not_guessed`.
|
||
|
||
#### `PE-INT-001` — `internal_features_check.py`
|
||
|
||
Analizza: ESD/pull interni vs net.
|
||
Skip se: `internal_features` vuoto (non si indovina open-drain).
|
||
|
||
- Test: `tests/test_internal_features_check.py` — `test_listed_open_drain_without_pull_is_warning`, `test_listed_pin_with_pullup_is_silent`, `test_empty_features_does_not_guess_open_drain`.
|
||
|
||
#### `PE-ESR-001` — `hf_coverage_check.py`
|
||
|
||
Analizza: copertura HF/ESR (bulk vs 100 n).
|
||
Skip se: valore C sconosciuto.
|
||
|
||
- Test: `tests/test_hf_coverage_check.py` — `test_bulk_only_is_info_ps_esr_001`, `test_bulk_plus_100n_is_silent`, `test_unknown_cap_value_is_not_guessed`.
|
||
|
||
#### `PE-ERRATA-001` — `errata_check.py`
|
||
|
||
Analizza: errata pubblicata vs pull sul pin.
|
||
Skip se: MPN sconosciuto o entry senza URL.
|
||
|
||
- Test: `tests/test_errata_check.py` — `test_missing_errata_pullup_is_ps_errata_001`, `test_pullup_present_is_silent`, `test_unknown_mpn_and_url_less_entry_are_silent`.
|
||
|
||
Suite deterministica end-to-end: `tests/test_validation_deterministic_seed.py` `test_deterministic_findings_seeded_and_not_reviewed`; `tests/test_eval_report.py` `test_simple_project_eval_matches_committed_golden`.
|
||
|
||
### 1.4 Certifier di classe (grafo — schema **e** PCB)
|
||
|
||
Moduli: `interface_class_check.py` (schema **e** PCB), `hf_bus_class.py` (2.62.0, solo `run_pcb_checks`), `usb_pd_check.py` (2.62.1, solo PCB). Niente geometria. Niente connettore/dispositivo → **silenzio**.
|
||
|
||
#### USB-C — solo se receptacle USB-C sul grafo
|
||
|
||
- **`PE-USBC-001`** classe USB2 vs USB3 da BOM/footprint/MPN/net.
|
||
- **`PE-USBC-002`** CC1 e CC2.
|
||
- **`PE-USBC-003`** Rd 5.1 kΩ ±10% o Rp 56/22/10 kΩ. Skip/INSUFFICIENT se R ignota o CC verso controller senza R.
|
||
- **`PE-USBC-004`** VBUS e GND.
|
||
- **`PE-USBC-005`** SuperSpeed solo se classe USB3; USB2 = N/A (non ERROR).
|
||
|
||
Test: `tests/test_interface_class_check.py` — `test_no_connector_emits_nothing`, `test_usbc_rd_vbus_gnd_certified`, `test_usbc_missing_cc2_is_error`, `test_usbc_wrong_rd_is_error`, `test_usbc_cc_to_controller_without_r_is_insufficient_not_error`, `test_usbc_unknown_r_value_is_insufficient`, `test_usb3_class_without_ss_pairs_is_error`, `test_usb3_with_ss_pairs_is_certified`, `test_missing_vbus_is_error`, `test_simple_project_usbc_not_false_error`, `test_hubaudio_like_usbc_usb2_and_poe_magjack`, `test_usbc_vbus_does_not_invent_usb_500ma`, `test_does_not_use_layout_geometry`, `test_rule_catalog_shared_not_si`.
|
||
|
||
#### Ethernet — solo se RJ45/MagJack
|
||
|
||
- **`PE-ETH-001`** classe 10/100 vs GbE. Skip/INSUFFICIENT se velocità assente (non si inventa GbE).
|
||
- **`PE-ETH-002`** TX/RX (10/100) o quattro MDI (GbE).
|
||
- **`PE-ETH-003`** magnetics se GbE.
|
||
- **`PE-ETH-004`** Bob Smith 75 Ω: REVIEW (spesso in MagJack).
|
||
|
||
Test: `tests/test_interface_class_check.py` — `test_ethernet_10_100_magjack_no_gbe_magnetics_error`, `test_gbe_without_magnetics_is_error`, `test_gbe_with_magnetics_is_certified`, `test_rj45_without_speed_is_insufficient_not_invented_gbe`.
|
||
|
||
#### PoE — solo con evidenza PoE (RJ45 nudo = skip)
|
||
|
||
- **`PE-POE-001`** evidenza PoE.
|
||
- **`PE-POE-002`** classe/tipo (802.3af/at/bt, Class n) — mai inventata.
|
||
- **`PE-POE-003`** magnetics/isolamento (MagJack o LAN transformer).
|
||
- **`PE-POE-004`** (2.62.1) tensione di isolamento solo da numero sourced. Skip senza V.
|
||
|
||
Test: `tests/test_interface_class_check.py` — `test_bare_rj45_does_not_invent_poe`, `test_poe_with_evidence_requires_magnetics_isolation`, `test_poe_evidence_on_bare_jack_is_isolation_error`, `test_hubaudio_bom_has_usbc_and_poe_rj45_not_ddr`, `test_run_pcb_checks_skips_absent_interfaces`; `tests/test_pcb_phase_b.py` — `test_poe_isolation_skips_without_voltage_fact`.
|
||
|
||
#### DDR — solo se DRAM sul grafo (2.62.0)
|
||
|
||
- **`PE-DDR-001`** classe DRAM.
|
||
- **`PE-DDR-002`** CK, DQS, ≥8 DQ.
|
||
- **`PE-DDR-003`** VTT/VREF solo se quei net esistono (altrimenti skip, non VTT inventato).
|
||
|
||
Test: `tests/test_hf_line_check.py` — `test_no_ddr_cpu_fpga_on_usb_only_graph`, `test_ddr_present_class_and_nets_skip_invented_vtt`, `test_rule_catalog_hf_ids`.
|
||
|
||
#### CPU bus parallelo — solo se D/A/controllo esistono (2.62.0)
|
||
|
||
- **`PE-CPU-001`** presenza bus parallelo.
|
||
- **`PE-CPU-002`** ≥8 D/DQ/AD.
|
||
- **`PE-CPU-003`** address o AD mux.
|
||
- **`PE-CPU-004`** CS/WE/OE (o FMC).
|
||
|
||
Test: `tests/test_hf_line_check.py` — `test_cpu_parallel_bus_only_when_data_addr_control_exist`.
|
||
|
||
#### FPGA — solo se FPGA sul grafo (2.62.0)
|
||
|
||
- **`PE-FPGA-001`** classe FPGA.
|
||
- **`PE-FPGA-002`** flash di config se c’è un flash IC (altrimenti skip).
|
||
- **`PE-FPGA-003`** VCCINT/VCCIO solo se net nominati.
|
||
|
||
Test: `tests/test_hf_line_check.py` — `test_fpga_only_when_present_config_flash_optional`.
|
||
|
||
#### USB-PD contratto — `usb_pd_check.py` (2.62.1)
|
||
|
||
- **`PE-PD-001`** controller PD sul grafo (FUSB302/TPS257/…). Non è USB2 Rd. PDO non inventati.
|
||
|
||
Test: `tests/test_pcb_phase_b.py` — `test_usb_pd_skips_without_pd_controller`.
|
||
|
||
### 1.5 Review AI per-IC + post-pass
|
||
|
||
Moduli: `review_session.py`, `pcb_review.py` non entra qui. DeepSeek, PDF una volta per IC, fail-soft. Prompt tools grafo (`find_connected_components`, `get_net_for_pin`, `get_pintable`, `get_datasheet_excerpt`). Finding LLM = REVIEW, mai RULE ERROR (`finding_engine`).
|
||
|
||
Skip se: niente MPN; niente PDF (`not_reviewed`); fingerprint invariato (`review_fingerprint.py`).
|
||
|
||
- Review loop: `tests/test_native_review_loop.py` (5); `tests/test_validation_no_tool_recovery.py` `test_no_tool_calls_triggers_forced_submit_next_turn`; `tests/test_validation_concurrency.py` (3); `tests/test_validation_trace_log.py` (3); `tests/test_review_parse.py` (2); `tests/test_quote_verify.py` (5); `tests/test_finding_engine.py` (`test_llm_review_is_never_rule`, `test_review_and_risk_cannot_stay_error`, `test_insufficient_evidence_does_not_stay_error`); `tests/test_fase_b_checks.py` `test_llm_error_review_clamped_in_complete_finding`; `tests/test_review_fingerprint.py` (2); `tests/test_reprocess.py` (4); `tests/test_deepseek_provider.py` (`test_thinking_only_echoes_reasoning_content_on_follow_up` e roundtrip tool).
|
||
- Excerpt cross-IC: `tests/test_datasheet_excerpt_tool.py` (12); `tests/test_cache_breakpoint_limit.py` (3).
|
||
- **Normalize** (downgrade-only): `tests/test_normalize_findings.py` (14); `tests/test_periscope_postpass_rewrite.py` `test_emmaforo_uart_normalize_cannot_promote_warning`.
|
||
- **Dedupe** cross-IC: `tests/test_dedupe_findings.py` (11); `tests/test_periscope_postpass_rewrite.py` `test_emmaforo_uart_dedupe_collapses_both_endpoints`.
|
||
|
||
---
|
||
|
||
## 2. Esame PCB (`MODE=pcb`)
|
||
|
||
Stadi: `ensure_graph` → `parse_pcb` → `classify` → `inventory` → **`run_pcb_checks`** → `ai_review` → `pcb_report.json`. ID finding `PCB-{ref}-{001}`. Mutex vs analisi schema e vs placement. Inventario net (`pcb_inventory.py`) è numeri, non finding.
|
||
|
||
KiCad DRC **non** è un nodo Periscope. `tests/test_pcb_software_bugs.py` `test_drc_unconnected_items_count` e `test_hubaudio_pcb_lay004_vs_drc_order_of_magnitude` servono solo a **non** copiare il DRC.
|
||
|
||
Suite orchestrazione: `tests/test_pcb_review.py` `test_run_pcb_checks_assigns_pcb_ids_and_recommendations`, `test_pcb_pipeline_imports_build_placement_plan`; `tests/test_pcb_phase_b.py` `test_run_pcb_checks_phase_b_absent_is_silent`, `test_phase_b_rule_catalog`; `tests/test_interface_class_check.py` `test_merge_drops_duplicate_pcb_interface_findings`; `tests/test_pcb_sse_terminal.py` (4).
|
||
|
||
### 2.1 Layout ingest (non DRC)
|
||
|
||
`parsers_kicad_pcb.py` → `LayoutGraph`. Nessun default 1 oz.
|
||
|
||
- Test: `tests/test_kicad_pcb.py` (14); `tests/test_pcb_via_not_pad.py` (geometria via≠pad); `tests/test_pcb_software_bugs.py` `test_kicad10_name_only_nets_fill_index`.
|
||
|
||
### 2.2 Net / footprint — `pcb_net_match.py` + `pcb_checks._incomplete_layout_finding`
|
||
|
||
#### `PE-LAY-001`
|
||
|
||
Pad PCB su net diverso dallo schema (stesso pin).
|
||
Skip se: layout assente.
|
||
|
||
- Test: `tests/test_pcb_review.py` — `test_pad_net_mismatch_is_pe_lay_001`, `test_hierarchy_plus_real_mismatch_keeps_only_real_pe_lay_001`.
|
||
|
||
#### `PE-LAY-002`
|
||
|
||
Ref in schema, nessun footprint (tranne #PWR). Distingue “manca sulla PCB”.
|
||
|
||
- Test: `tests/test_pcb_review.py` `test_missing_footprint_is_pe_lay_002`; `tests/test_incomplete_pcb.py` `test_unplaced_ref_is_lay_002_not_invented_xy`.
|
||
|
||
#### `PE-LAY-003`
|
||
|
||
Stesso net, solo prefisso `/sheet/` KiCad (INFO, una volta).
|
||
|
||
- Test: `tests/test_pcb_review.py` `test_kicad_hierarchy_slash_is_one_sync_finding_not_per_pad`.
|
||
|
||
#### `PE-LAY-004` — `pcb_checks.py` (**non in catalogo `finding_engine`**)
|
||
|
||
Pad net senza rame (track **o** via **o** zone). Skip NC `unconnected-(…)`. INFO INSUFFICIENT, non FAIL inventato.
|
||
|
||
- Test: `tests/test_incomplete_pcb.py` `test_run_pcb_checks_on_partial_layout_does_not_invent_tracks`; `tests/test_pcb_software_bugs.py` — `test_lay004_zone_counts_as_copper`, `test_lay004_skips_unconnected_nc_and_matches_sheet_prefix`, `test_hubaudio_pcb_lay004_vs_drc_order_of_magnitude`.
|
||
|
||
### 2.3 Placement vs `layout_rules` — `placement_check.py`
|
||
|
||
Senza mm in `layout_rules`: skip. Niente 3 mm default. Distanza euclidea pad-pad **non** usata.
|
||
|
||
#### `PE-PLC-001`
|
||
|
||
Path rame (segmenti) vs `max_distance_mm`.
|
||
|
||
- Test: `tests/test_placement_check.py` — `test_crystal_load_cap_beyond_max_distance_mm_is_ps_plc_001`, `test_crystal_load_cap_within_max_distance_mm_is_silent`, `test_track_path_longer_than_max_distance_mm_is_ps_plc_001`; `tests/test_pcb_review.py` `test_close_decoupling_has_no_plc_001`; `tests/test_placement_check.py` `test_simple_project_without_pcb_has_no_ps_plc`.
|
||
|
||
#### `PE-PLC-002`
|
||
|
||
Via in courtyard vs `min_via_count`. Skip senza vertici courtyard e senza min.
|
||
|
||
- Test: `tests/test_placement_check.py` `test_via_count_is_calculated_from_courtyard_and_min_parameter` (geometria `_in_poly`; non emette ancora il finding end-to-end).
|
||
|
||
#### `PE-PLC-003`
|
||
|
||
`same_layer` decoupling.
|
||
|
||
- Test: `tests/test_placement_check.py` — `test_same_layer_param_opposite_layers_is_ps_plc_003`, `test_same_layer_param_same_copper_is_silent`, `test_opposite_layers_without_same_layer_param_is_silent`, `test_opposite_layers_with_via_in_courtyard_is_silent`.
|
||
|
||
#### `PE-PLC-004`
|
||
|
||
Keepout: net estraneo nel courtyard. Non è DRC rame-pad.
|
||
|
||
- Test: `tests/test_placement_check.py` — `test_keepout_foreign_track_in_courtyard_is_ps_plc_004`, `test_ic_courtyard_pad_nets_are_not_keepout_violations`, `test_keepout_own_net_in_courtyard_is_silent`, `test_keepout_without_courtyard_is_silent`.
|
||
|
||
#### `PE-PLC-005` (**non in catalogo `finding_engine`**)
|
||
|
||
Proximity non misurabile perché manca la **traccia**.
|
||
|
||
- Test: `tests/test_placement_check.py` `test_unrouted_crystal_cap_is_insufficient_not_euclidean`.
|
||
|
||
Schema runner omette i check layout: `tests/test_placement_check.py` `test_schema_deterministic_runner_omits_layout_checks`.
|
||
|
||
### 2.4 SI — `si_check.py` + ImpedenceFinder
|
||
|
||
Gate: `layout_rules` kind + `net_class`/quote sul **bus citato**. Skip I2C/GPIO/EN/CC/strap RC. Bus: USB2, USB3, ETH MDI, RGMII, SGMII, DDR3 CLK/DQS/DQ/ADDR, HDMI, PCIe, LVDS; 2.62.0 aggiunge MIPI e clock HF (`bus_class`); XTAL/OSCIN fuori.
|
||
|
||
Z0: `impedance.py` / `impedance_traces.py`. CPWG → errore, non numero inventato. Senza stackup: skip misura.
|
||
|
||
Suite gate: `tests/test_si_check.py` `test_skip_i2c_gpio_cc_regn`, `test_i2c_not_checked_as_50_ohm`, `test_simple_project_without_pcb_has_no_ps_si_001`; `tests/test_hf_line_check.py` `test_bus_class_mipi_hf_clk_not_xtal`. Calcolatrice: `tests/test_impedance.py` (20); misura net: `tests/test_impedance_traces.py` (10).
|
||
|
||
#### `PE-SI-001` length_match / skew coppia
|
||
|
||
Skip senza mm datasheet.
|
||
|
||
- Test: `tests/test_si_check.py` `test_length_match_2_5mm_vs_1mm_is_fail`, `test_coverage_mdi_rgmii_ddr3_usb3_gated_not_cross_mapped`.
|
||
|
||
#### `PE-SI-002` Zavg/min/max vs finestra datasheet
|
||
|
||
Mai folklore 90 Ω.
|
||
|
||
- Test: `tests/test_si_check.py` `test_emmaforo_usb_avg_in_window_min_out_is_margin`, `test_coverage_mdi_rgmii_ddr3_usb3_gated_not_cross_mapped`.
|
||
|
||
#### `PE-SI-003` lunghezza max mm
|
||
|
||
- Test: `tests/test_si_check.py` `test_coverage_mdi_rgmii_ddr3_usb3_gated_not_cross_mapped`.
|
||
|
||
#### `PE-SI-004` spacing intra-coppia mm
|
||
|
||
Skip senza mm. Copertura indiretta nella stessa suite SI gated.
|
||
|
||
#### `PE-SI-005` piano di riferimento / layer
|
||
|
||
Skip senza FACT datasheet.
|
||
|
||
#### `PE-SI-006` conteggio via HS vs min/max datasheet
|
||
|
||
Skip senza min/max.
|
||
|
||
#### `PE-SI-008` return via accanto alla coppia (`return_path`)
|
||
|
||
Skip senza kind.
|
||
|
||
#### `PE-SI-009` serie R ohm sul net HS (non EN-RC)
|
||
|
||
- Test: `tests/test_si_check.py` `test_en_rc_series_resistor_does_not_paint_usb`.
|
||
|
||
#### `PE-SI-010` bus HS misurato, libreria senza FACT Z → INFO, non FAIL 90 Ω
|
||
|
||
- Test: `tests/test_si_check.py` `test_usb_without_library_z_is_insufficient_not_90ohm_fail`, `test_empty_net_class_impedance_does_not_paint_usb`; `tests/test_hf_line_check.py` `test_existing_si_usb_not_polluted_by_hf_geometry`.
|
||
|
||
Refresh extract SI vecchio: `tests/test_pcb_plan_closeout.py` `test_si_refresh_when_old_extract_has_only_decoupling`, `test_si_extract_needed_skips_board_ics_only_not_shared_library`.
|
||
|
||
### 2.5 Linee HF extra — `hf_line_check.py` (2.62.0 Fase A)
|
||
|
||
Track ≠ via ≠ pad ≠ zona. Stub = traccia dangling.
|
||
|
||
#### `PE-SI-007` stub vs mm datasheet
|
||
|
||
INFO se misura c’è e mm mancano (INSUFFICIENT). Skip: pad-pad, via, zone.
|
||
|
||
- Test: `tests/test_hf_line_check.py` — `test_no_layout_skips_hf_geometry`, `test_pad_to_pad_usb_has_no_stub`, `test_via_is_not_a_stub`, `test_zone_is_not_a_track_stub`, `test_dangling_track_stub_vs_datasheet_mm_is_fail`, `test_measured_stub_without_datasheet_mm_is_insufficient`.
|
||
|
||
#### `PE-SI-011` gap pour GND sotto coppia HF (REVIEW)
|
||
|
||
Skip senza stackup/zone.
|
||
|
||
- Test: `tests/test_hf_line_check.py` `test_split_under_pair_needs_stackup_and_zone`.
|
||
|
||
#### `PE-SI-012` terminazione BOM serie/parallelo FACT
|
||
|
||
Skip se manca il terminatore (non si inventa 50 Ω).
|
||
|
||
- Test: `tests/test_hf_line_check.py` `test_bom_termination_is_fact_missing_is_skip`.
|
||
|
||
Catalogo HF: `tests/test_hf_line_check.py` `test_rule_catalog_hf_ids`. DDR/CPU/FPGA: §1.4 (stesso `check_memory_fpga_classes` in `run_pcb_checks`).
|
||
|
||
### 2.6 Potenza / via / termico sul board — `pcb_power_thermal.py`
|
||
|
||
#### `PE-PWR-001` (PCB) — `check_pcb_power_traces`
|
||
|
||
Larghezza **traccia** × spessore rame vs I_load/Imax/I_abs. IPC-2221 solo con thickness **e** ΔT da Tjmax. Iout_max non è carico. Niente 500 mA USB.
|
||
|
||
Skip se: manca I datasheet (**skip vero**, non INFO INSUFFICIENT — 2.61.1).
|
||
|
||
- Test: `tests/test_pcb_review.py` — `test_power_trace_ipc2221_errors_when_load_exceeds_ampacity`, `test_power_trace_uses_parsed_width_and_thickness`, `test_power_trace_skips_without_i_load`, `test_power_trace_skips_iout_max_as_load`, `test_power_trace_uses_imax_when_i_load_absent`, `test_power_trace_uses_i_abs_when_i_load_absent`; `tests/test_interface_class_check.py` `test_usbc_vbus_does_not_invent_usb_500ma`.
|
||
|
||
#### `PE-VIA-001` — `check_pcb_via_current`
|
||
|
||
Se I c’è e sul net **zero via**: INFO conteggio. Se le via ci sono: silenzio — **nessuna tabella ampacity**. Via ≠ traccia.
|
||
|
||
Skip se: manca I.
|
||
|
||
- Test: `tests/test_pcb_software_bugs.py` `test_via_current_skips_without_i_load`.
|
||
|
||
#### `PE-VIA-002` (2.62.1)
|
||
|
||
I + conteggio/drill FACT. Tabella IPC via **non** applicata.
|
||
|
||
Skip se: manca I.
|
||
|
||
- Test: `tests/test_pcb_phase_b.py` `test_via_current_skips_without_i_does_not_invent_ipc`.
|
||
|
||
#### `PE-THM-001` — `check_pcb_thermal_copper`
|
||
|
||
P = I_load × drop; courtyard senza pour/via.
|
||
|
||
Skip se: P sconosciuta.
|
||
|
||
- Test: `tests/test_pcb_review.py` `test_thermal_copper_warns_without_pour_or_vias`.
|
||
|
||
#### `PE-THM-002` — `check_pcb_junction_temp`
|
||
|
||
Tj = Ta + P·θJA solo con P, θJA, area rame, conteggio via. Non è FEM.
|
||
|
||
Skip/INSUFFICIENT se manca un fattore.
|
||
|
||
- Test: `tests/test_pcb_plan_closeout.py` — `test_tj_when_theta_copper_vias_exist`, `test_tj_insufficient_without_theta`.
|
||
|
||
#### `PE-KEL-001`
|
||
|
||
Pin Kelvin/sense sullo stesso net di carico. Ignora CRS/IN±.
|
||
|
||
- Test: `tests/test_pcb_review.py` `test_kelvin_sense_pin_on_shared_net`; `tests/test_pcb_software_bugs.py` `test_kelvin_ignores_crs_and_in_plus`.
|
||
|
||
#### `PE-STCH-001`
|
||
|
||
Segnale sopra pour GND senza via GND nel bbox. Skip GPIO minuscoli.
|
||
|
||
- Test: `tests/test_pcb_review.py` — `test_gnd_stitch_skips_tiny_gpio`, `test_gnd_stitch_info_when_signal_has_pour_but_no_via`.
|
||
|
||
### 2.7 Derating / gerarchia / timing / PI
|
||
|
||
#### `PE-DRT-001` / `PE-DRT-002` / `PE-DRT-003` — `pcb_checks.check_pcb_derating` + `derating.py`
|
||
|
||
Vop vs Vrated: ERROR / MARGIN / PASS INFO. Niente % dielettrico inventato. Skip se manca Vop o Vr.
|
||
|
||
- Test: `tests/test_fase_b_checks.py` `test_derating_pass_margin_risk`; `tests/test_derating.py` (7, C_eff stima — non è un PE-* a parte).
|
||
|
||
#### `PE-HIER-001` — `hierarchy.py`
|
||
|
||
Pin IC senza net nominato.
|
||
|
||
- Test: `tests/test_fase_b_checks.py` `test_hierarchy_component_pin_net_block`.
|
||
|
||
#### `PE-TIM-001` / `PE-TIM-002` — `timing_check.py`
|
||
|
||
001 RC reset vs t_reset; 002 strap vs Vih/Vil.
|
||
Skip senza R/C e t_reset o Vih/Vil.
|
||
|
||
- Test: `tests/test_fase_b_checks.py` — `test_timing_skips_without_numbers`, `test_timing_reset_rc_vs_t_reset`. **`PE-TIM-002` non ha un test che fa scattare il finding** (solo skip condiviso).
|
||
|
||
#### `PE-PI-001` / `PE-PI-002` — `pi_check.py`
|
||
|
||
001 cap locale sul rail IC; 002 locale vs bulk se i valori C esistono. Distanza mm solo da `layout_rules` (altrimenti è PE-PLC-001). Skip net NC.
|
||
|
||
- Test: `tests/test_fase_b_checks.py` — `test_pi_missing_local_is_risk_not_error`, `test_pi_any_cap_on_slash_prefixed_rail_suppresses_missing_local`, `test_pi_bulk_only_does_not_claim_no_local`; `tests/test_pcb_software_bugs.py` `test_emi_and_pi_skip_unconnected_nets`. **`PE-PI-002` non ha un assert dedicato sul `rule_id`.**
|
||
|
||
### 2.8 ESD / return / EMI / SPOF
|
||
|
||
#### `PE-ESD-001` — `esd_return_check.check_esd`
|
||
|
||
Net J* ∩ IC senza parte ESD: REVIEW, non ERROR. Niente GPIO/NC/rail spam.
|
||
|
||
- Test: `tests/test_fase_b_checks.py` — `test_esd_without_part_is_review_not_error`, `test_esd_skips_gpio_nc_unconnected_and_onboard_power`, `test_esd_one_finding_per_connector_ic_path`, `test_esd_skips_when_protection_part_on_net`.
|
||
|
||
#### `PE-ESD-002` (2.62.1) — `check_esd_distance`
|
||
|
||
Distanza **pad** TVS–connettore (via ≠ pad). INFO se no mm.
|
||
|
||
- Test: `tests/test_pcb_phase_b.py` `test_esd_distance_uses_pads_not_vias`.
|
||
|
||
#### `PE-RET-001` — `check_return_path`
|
||
|
||
Net HS sopra pour GND senza via GND nel bbox: REVIEW.
|
||
|
||
- Test: `tests/test_fase_b_checks.py` `test_return_path_hs_is_review`.
|
||
|
||
#### `PE-EMI-001` — `emi_check.py`
|
||
|
||
Solo se `layout_rules` cita choke/ferrite/shield. Non IEC 61000. Skip NC.
|
||
|
||
- Test: `tests/test_pcb_plan_closeout.py` `test_emi_only_with_datasheet_fact`; `tests/test_pcb_software_bugs.py` `test_emi_and_pi_skip_unconnected_nets`.
|
||
|
||
#### `PE-SPOF-001` — `spof_check.py`
|
||
|
||
Un regolatore o XTAL condiviso: REVIEW, mai ERROR.
|
||
|
||
- Test: `tests/test_pcb_plan_closeout.py` `test_spof_review_single_ldo`.
|
||
|
||
### 2.9 BOM ↔ PCB ↔ datasheet — `bom_pcb_check.py`
|
||
|
||
Skip se manca un lato (BOM, footprint, o numero datasheet).
|
||
|
||
#### `PE-BOM-010` famiglia package
|
||
|
||
VQFN/WQFN/HVQFN = QFN. LQFP ≠ QFN.
|
||
|
||
- Test: `tests/pcb/test_pcb_plan_closeout.py` `test_package_family_mismatch_is_pe_bom_010`; `tests/pcb/test_bom_011_named_join.py` `test_qfn_vs_vqfn_same_24_qfn_land_not_bom_010`.
|
||
|
||
#### `PE-BOM-011` pintable vs pad, join per nome
|
||
|
||
Join datasheet pintable × `.kicad_mod` lib × pad embedded. Non `pin_count` scalare. A5/B5 USB-C sono pin, non EP. EP/SH/mount classificati; via ≠ pad. Senza pintable: skip.
|
||
|
||
- Test: `tests/pcb/test_bom_011_named_join.py`; `tests/pcb/test_pcb_plan_closeout.py` `test_package_family_mismatch_is_pe_bom_010`; `tests/pcb/test_pcb_software_bugs.py` `test_ep_split_pads_not_bom_011`; `tests/pcb/test_pcb_via_not_pad.py`.
|
||
|
||
#### `PE-BOM-012` Vop vs rating/abs-max legato al **pin**
|
||
|
||
- Test: `tests/test_pcb_plan_closeout.py` `test_abs_max_voltage_and_temp_and_current`; `tests/test_pcb_software_bugs.py` `test_vddcr_absmax_does_not_bind_3v3_io`.
|
||
|
||
#### `PE-BOM-013` Top vs abs-max T
|
||
|
||
- Test: `tests/test_pcb_plan_closeout.py` `test_abs_max_voltage_and_temp_and_current`.
|
||
|
||
#### `PE-BOM-014` I_load vs rating
|
||
|
||
Skip se manca I.
|
||
|
||
- Test: `tests/test_pcb_plan_closeout.py` — `test_abs_max_voltage_and_temp_and_current`, `test_inductor_current_rating`.
|
||
|
||
### 2.10 Resto Fase B (2.62.1)
|
||
|
||
#### `PE-STK-001` — `stackup_check.py`
|
||
|
||
Spessore rame board vs stackup fab sourced. Mai 1 oz default.
|
||
Skip se: niente stackup layout o niente FACT fab.
|
||
|
||
- Test: `tests/test_pcb_phase_b.py` — `test_stackup_skips_without_fab_spec`, `test_stackup_vs_fab_spec_is_review_not_invented_oz`.
|
||
|
||
#### `PE-ANT-001` / `PE-ANT-002` — `antenna_layout_check.py`
|
||
|
||
Keepout zona e matching L/C sul feed. Solo se antenna/net RF sul grafo. Niente 50 Ω inventati.
|
||
Skip se: antenna assente; matching manca e non c’è FACT (002 non emette).
|
||
|
||
- Test: `tests/test_pcb_phase_b.py` — `test_antenna_skips_when_absent`, `test_antenna_keepout_and_match_are_facts`.
|
||
|
||
#### `PE-PDN-001` — `pdn_check.py`
|
||
|
||
Z(f) solo con FACT in frequenza. Z0 SI **non** è PDN.
|
||
|
||
- Test: `tests/test_pcb_phase_b.py` `test_pdn_skips_without_zf`.
|
||
|
||
Silenzio Fase B se assente: `tests/test_pcb_phase_b.py` `test_run_pcb_checks_phase_b_absent_is_silent`. Catalogo: `test_phase_b_rule_catalog`.
|
||
|
||
### 2.11 Review AI PCB
|
||
|
||
Modulo: `pcb_review.py` + `services/pcb_validation.py`. Stesso oggetto finding. Spiega FACT deterministici; **non** rilegge il PDF; non inventa interfacce / PoE / SuperSpeed / mm / Z / I.
|
||
|
||
Skip se: niente JSON libreria per l’IC.
|
||
|
||
- Test: `tests/test_pcb_review.py` — `test_pcb_ai_finding_gets_action`, `test_parse_review_action_field_without_recommendation`, `test_layout_context_includes_via_counts`, `test_layout_context_includes_domain_and_group`, `test_pcb_ai_skips_without_library_extraction`, `test_library_constraints_fill_from_storage`, `test_inventory_lists_net_length_and_pair`; `tests/test_finding_engine.py` `test_pcb_review_ai_finding_action_without_recommendation`; `tests/test_pcb_software_bugs.py` `test_thinking_reasoning_content_echo_still_present`.
|
||
|
||
### 2.12 Tab antenna RF (non in `run_pcb_checks`)
|
||
|
||
Moduli: `antenna_rf.py`, `antenna_geometry.py`. Topologia matching + ricetta routing. No EM/VSWR/CPWG. Distinto da `PE-ANT-001/002`.
|
||
|
||
- Test: `tests/test_antenna_rf.py` — `test_verify_finds_matching_path_to_ant_footprint`, `test_verify_missing_matching_is_warning`, `test_design_recipe_needs_marker_without_ant`, `test_design_recipe_ready_with_ant_and_stackup`, `test_geometry_templates_produce_export`, `test_geometry_overflow_tiny_zone`, `test_design_recipe_meander_template`.
|
||
|
||
Placement/auto-place **non** è esame PCB: `tests/test_placement_pack.py`, `tests/test_placement_pipeline.py` (fuori da questo albero di analisi).
|
||
|
||
---
|
||
|
||
## Motore finding (condiviso)
|
||
|
||
`finding_engine.py`: FACT / REQUIREMENT / INFERENCE; ERROR solo RULE+MANDATORY; `decisions.json` non ri-naga.
|
||
|
||
- Test: `tests/test_finding_engine.py` (11); `tests/test_finding_schema.py` (7); `tests/test_fase_b_checks.py` `test_sort_findings_error_then_class`; `tests/test_review_workflow.py` (10 — stati finding UI, non check PE). CAD bridge: `tests/test_cad_bridge.py` (5).
|
||
|
||
---
|
||
|
||
## Test che esistono ma non sono analisi
|
||
|
||
`tests/` ha **114 file / ~730 test**. Oltre ai nodi sopra restano overlay nativi, upload, auth, billing seam, UI rewrite. Non sono check PE-*.
|
||
|
||
**Upload / ingest cartella** (non analisi): `tests/test_periscope_frontend_upload_rewrite.py` `test_upload_zone_is_src`; `tests/test_kicad_project.py` (picker cartella, `.history`, Safari stem); `tests/test_netlist_bundle.py` (zip, zip-slip); `tests/test_kicad_pcb.py` `test_upload_pcb_sets_has_pcb`, `test_upload_sch_as_pcb_is_rejected`; `tests/test_purple_parts_resolver.py` (upload BOM/LCSC); `tests/test_reopen_replace.py` `test_reopen_then_replace_bom_and_netlist`.
|
||
|
||
**Auth**: i test auth esistono (`tests/test_local_auth.py`, `tests/test_periscope_auth_rewrite.py`, `tests/test_pinscope_compat.py`, più overlay che tengono il contratto login). Non si descrivono segreti, JWT, password, o flussi di autenticazione.
|
||
|
||
Altri non-analisi: rewrite frontend/Docker (`test_periscope_*_rewrite.py`, `test_periscope_leftover_package_requirements.py`, `test_periscope_boot_without_dependency_product.py`); delete progetto; cost estimator; event bridge; job runner; `test_deepseek_only.py` (routing modello, non finding).
|
||
|
||
---
|
||
|
||
## Conteggio nodi
|
||
|
||
| Gruppo | Nodi |
|
||
| --- | --- |
|
||
| Check solo schema (27 PE-* + LED senza ID) | 28 |
|
||
| USB-C / ETH / PoE (schema e PCB) | 13 |
|
||
| `PE-PWR-001` (due source: schema e PCB) | 1 |
|
||
| DDR / CPU / FPGA / PD (grafo, invocati da `run_pcb_checks`) | 11 |
|
||
| Check solo PCB (layout, SI/HF, potenza, Fase B, …) | 47 |
|
||
| Processo: 3 skill + DigiKey + value + IC AI + PCB AI + tab RF | 8 |
|
||
| **Totale nodi analisi** | **108** |
|
||
|
||
Check PE-* in codice: **99** (tutti elencati). Più LED senza `rule_id`. `PE-LAY-004`, `PE-PLC-005`, `PE-TH-002` sono in codice; `PE-LAY-004` / `PE-PLC-005` non sono in `_seed()` di `finding_engine.py`.
|
||
|
||
Buchi di test noti (il check c’è, il fire test no): `PE-TIM-002`, `PE-PI-002` (nessun assert `rule_id`), `PE-PLC-002` (solo geometria `_in_poly`), `PE-SI-004` / `PE-SI-005` / `PE-SI-006` / `PE-SI-008` (gate SI, niente caso dedicato).
|