Stop periscope/src importing PinScope validate.py after live C2 smoke.

PCB review and src checks use native parse, constraints lookup, and
review_session. Inherited validate.py and validation_tools.py stay in
dependency/.
This commit is contained in:
2026-09-20 15:21:32 +02:00
parent 974923b2e6
commit a96ee2e88a
28 changed files with 38 additions and 30 deletions
@@ -2,6 +2,13 @@
What's new in Periscope. What's new in Periscope.
## 2.39.1 — 2026-09-20 — src no longer imports PinScope review loop
After 2.39.0 live smoke (Emmaforo ERROR 2 / WARNING 12 / INFO 18), native modules are the only review path from `periscope/src`. PinScope `validate.py` / `validation_tools.py` remain on disk.
- [Changed] PCB review and deterministic checks import `review_parse` / `constraints_lookup` / `review_session`, not `validate.py`.
- [Changed] Tests exercise the native parser and graph tools.
## 2.39.0 — 2026-09-20 — Fase C2: native DeepSeek review loop ## 2.39.0 — 2026-09-20 — Fase C2: native DeepSeek review loop
Live per-IC review runs from `periscope/src` (`review_session.py`, parse, graph tools). PinScope `validate.py` / `validation_tools.py` stay in `dependency/` as fallback; they are not deleted. LLM findings stay REVIEW; recommended never ERROR. AGPL unchanged. No parser/graph rewrite. No auto-place. Live per-IC review runs from `periscope/src` (`review_session.py`, parse, graph tools). PinScope `validate.py` / `validation_tools.py` stay in `dependency/` as fallback; they are not deleted. LLM findings stay REVIEW; recommended never ERROR. AGPL unchanged. No parser/graph rewrite. No auto-place.
@@ -26,7 +26,7 @@ from backend.periscopex.thermal_check import (
_net_voltage, _net_voltage,
_specs_values, _specs_values,
) )
from backend.periscopex.validate import _match_constraints from backend.periscopex.constraints_lookup import match_constraints as _match_constraints
_FAMILIES = ( _FAMILIES = (
"LQFP", "TQFP", "VQFP", "WQFN", "HVQFN", "VQFN", "QFN", "DFN", "LQFP", "TQFP", "VQFP", "WQFN", "HVQFN", "VQFN", "QFN", "DFN",
@@ -19,7 +19,7 @@ from backend.periscopex.passive_rail_check import (
_is_power_net, _is_power_net,
_pin_name_tokens, _pin_name_tokens,
) )
from backend.periscopex.validate import _match_constraints from backend.periscopex.constraints_lookup import match_constraints as _match_constraints
_EN_RE = re.compile( _EN_RE = re.compile(
r"(?:^|[_/])(EN|ENA|ENABLE|n?SHDN|nEN|EN_N|CHIP_EN)(?:$|[_/\d])", r"(?:^|[_/])(EN|ENA|ENABLE|n?SHDN|nEN|EN_N|CHIP_EN)(?:$|[_/\d])",
@@ -20,7 +20,7 @@ from backend.periscopex.pcb_net_match import (
refs_on_matched_net, refs_on_matched_net,
) )
from backend.periscopex.si_check import bus_class, skip_si_net from backend.periscopex.si_check import bus_class, skip_si_net
from backend.periscopex.validate import _match_constraints from backend.periscopex.constraints_lookup import match_constraints as _match_constraints
_EMI_RE = re.compile( _EMI_RE = re.compile(
r"(emi|emc|common[-\s]?mode|cmc|choke|ferrite\s*bead|shield(?:ing)?|" r"(emi|emc|common[-\s]?mode|cmc|choke|ferrite\s*bead|shield(?:ing)?|"
@@ -10,7 +10,7 @@ from backend.periscopex.passive_rail_check import (
_pin_name_tokens, _pin_name_tokens,
_resistor_to_power, _resistor_to_power,
) )
from backend.periscopex.validate import _match_constraints from backend.periscopex.constraints_lookup import match_constraints as _match_constraints
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -25,7 +25,7 @@ from backend.periscopex.passive_rail_check import (
_pin_name_tokens, _pin_name_tokens,
_resistor_ohms, _resistor_ohms,
) )
from backend.periscopex.validate import _match_constraints from backend.periscopex.constraints_lookup import match_constraints as _match_constraints
_ADC_RATE_KEYS = ("adc_sample_rate", "adc_sample_rate_hz", "data_rate", "data_rate_hz") _ADC_RATE_KEYS = ("adc_sample_rate", "adc_sample_rate_hz", "data_rate", "data_rate_hz")
_DCR_MAX_KEYS = ("max_ferrite_dcr_ohms", "ferrite_dcr_max_ohms", "max_bead_dcr_ohms") _DCR_MAX_KEYS = ("max_ferrite_dcr_ohms", "ferrite_dcr_max_ohms", "max_bead_dcr_ohms")
@@ -15,7 +15,7 @@ from backend.periscopex.passive_rail_check import (
_is_regulator_output_pin, _is_regulator_output_pin,
_pin_label, _pin_label,
) )
from backend.periscopex.validate import _match_constraints from backend.periscopex.constraints_lookup import match_constraints as _match_constraints
_BULK_MIN_F = 1e-6 _BULK_MIN_F = 1e-6
_HF_MAX_F = 1e-6 _HF_MAX_F = 1e-6
@@ -7,7 +7,7 @@ from backend.periscopex.passive_rail_check import (
_pin_name_tokens, _pin_name_tokens,
_resistor_to_power, _resistor_to_power,
) )
from backend.periscopex.validate import _match_constraints from backend.periscopex.constraints_lookup import match_constraints as _match_constraints
def check_internal_features( def check_internal_features(
@@ -19,7 +19,7 @@ from backend.periscopex.models import (
NetType, NetType,
ResistorSpecs, ResistorSpecs,
) )
from backend.periscopex.validate import _match_constraints from backend.periscopex.constraints_lookup import match_constraints as _match_constraints
from backend.periscopex.led_current_check import _parse_resistance from backend.periscopex.led_current_check import _parse_resistance
from backend.periscopex.resolve_passives import _parse_spice_value from backend.periscopex.resolve_passives import _parse_spice_value
@@ -28,7 +28,7 @@ from backend.periscopex.thermal_check import (
_pin_net_by_role, _pin_net_by_role,
_specs_values, _specs_values,
) )
from backend.periscopex.validate import _match_constraints from backend.periscopex.constraints_lookup import match_constraints as _match_constraints
# IPC-2221 §6.2 (empirical): I = k · ΔT^0.44 · A^0.725, A in mil², I in A. # IPC-2221 §6.2 (empirical): I = k · ΔT^0.44 · A^0.725, A in mil², I in A.
_IPC_B = 0.44 _IPC_B = 0.44
+1 -1
View File
@@ -18,7 +18,7 @@ from backend.periscopex.pcb_net_match import (
normalize_kicad_hierarchy_net, normalize_kicad_hierarchy_net,
refs_on_matched_net, refs_on_matched_net,
) )
from backend.periscopex.validate import _match_constraints from backend.periscopex.constraints_lookup import match_constraints as _match_constraints
_LOAD_KEYS = ("i_load_a", "i_load", "iout", "i_out") _LOAD_KEYS = ("i_load_a", "i_load", "iout", "i_out")
@@ -24,7 +24,7 @@ from backend.periscopex.models import (
LayoutPad, LayoutPad,
) )
from backend.periscopex.pcb_net_match import kicad_nets_match from backend.periscopex.pcb_net_match import kicad_nets_match
from backend.periscopex.validate import _match_constraints from backend.periscopex.constraints_lookup import match_constraints as _match_constraints
def _pad_for(layout: LayoutGraph, ref: str, number: str) -> LayoutPad | None: def _pad_for(layout: LayoutGraph, ref: str, number: str) -> LayoutPad | None:
@@ -27,7 +27,7 @@ from backend.periscopex.thermal_check import (
_pin_net_by_role, _pin_net_by_role,
_specs_values, _specs_values,
) )
from backend.periscopex.validate import _match_constraints from backend.periscopex.constraints_lookup import match_constraints as _match_constraints
_IQ_KEYS = ( _IQ_KEYS = (
"iq_a", "quiescent_current_a", "supply_current_a", "idd_a", "icc_a", "iq_a", "quiescent_current_a", "supply_current_a", "idd_a", "icc_a",
@@ -6,7 +6,7 @@ import hashlib
import json import json
from backend.periscopex.models import ComponentType, DesignGraph from backend.periscopex.models import ComponentType, DesignGraph
from backend.periscopex.validate import _match_constraints from backend.periscopex.constraints_lookup import match_constraints as _match_constraints
def ic_neighborhood_fingerprint( def ic_neighborhood_fingerprint(
@@ -20,7 +20,7 @@ from backend.periscopex.thermal_check import (
_pin_net_by_role, _pin_net_by_role,
_specs_values, _specs_values,
) )
from backend.periscopex.validate import _match_constraints from backend.periscopex.constraints_lookup import match_constraints as _match_constraints
_PG_RE = re.compile(r"(?:^|[_/])(PG|PGOOD|PWRGD|POWER_GOOD|POK)(?:$|[_/\d])", re.I) _PG_RE = re.compile(r"(?:^|[_/])(PG|PGOOD|PWRGD|POWER_GOOD|POK)(?:$|[_/\d])", re.I)
_EN_RE = re.compile( _EN_RE = re.compile(
+1 -1
View File
@@ -14,7 +14,7 @@ import re
from backend.periscopex.models import ComponentType, DesignGraph, Finding, LayoutGraph, LayoutSegment from backend.periscopex.models import ComponentType, DesignGraph, Finding, LayoutGraph, LayoutSegment
from backend.periscopex.pcb_net_match import kicad_nets_match, normalize_kicad_hierarchy_net from backend.periscopex.pcb_net_match import kicad_nets_match, normalize_kicad_hierarchy_net
from backend.periscopex.pcb_power_thermal import _is_gnd_name from backend.periscopex.pcb_power_thermal import _is_gnd_name
from backend.periscopex.validate import _match_constraints from backend.periscopex.constraints_lookup import match_constraints as _match_constraints
_PAIR_SUFFIXES = ( _PAIR_SUFFIXES = (
("_DP", "_DM"), ("_DP", "_DM"),
@@ -25,7 +25,7 @@ from backend.periscopex.models import (
) )
from backend.periscopex.passive_rail_check import _pin_name_tokens from backend.periscopex.passive_rail_check import _pin_name_tokens
from backend.periscopex.resolve_passives import _parse_spice_value from backend.periscopex.resolve_passives import _parse_spice_value
from backend.periscopex.validate import _match_constraints from backend.periscopex.constraints_lookup import match_constraints as _match_constraints
_TA_C = 25.0 _TA_C = 25.0
_TJ_WARN_C = 125.0 _TJ_WARN_C = 125.0
@@ -16,7 +16,7 @@ from backend.periscopex.passive_rail_check import (
_resistor_to_ground, _resistor_to_ground,
_resistor_to_power, _resistor_to_power,
) )
from backend.periscopex.validate import _match_constraints from backend.periscopex.constraints_lookup import match_constraints as _match_constraints
_T_RESET_KEYS = ("t_reset_min_s", "t_reset_min_ms", "reset_delay_ms", "t_por_ms") _T_RESET_KEYS = ("t_reset_min_s", "t_reset_min_ms", "reset_delay_ms", "t_por_ms")
_VIH_KEYS = ("vih", "vih_min_v", "v_ih_min", "vih_min") _VIH_KEYS = ("vih", "vih_min_v", "v_ih_min", "vih_min")
@@ -15,9 +15,10 @@ from backend.periscopex.pcb_review import (
build_pcb_layout_context, build_pcb_layout_context,
format_library_extraction, format_library_extraction,
) )
from backend.periscopex.validate import ReviewResult, _match_constraints from backend.periscopex.constraints_lookup import match_constraints as _match_constraints
from backend.periscopex.review_parse import ReviewResult
from backend.services.api_logs import ApiLogger from backend.services.api_logs import ApiLogger
from backend.services.validation import review_ic_async from backend.services.review_session import review_ic_async
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -1,6 +1,6 @@
# Piano — indipendenza architettonica e di licenza da PinScope # Piano — indipendenza architettonica e di licenza da PinScope
**Stato:** split fisico **2.38.0**. Fase C2 native review loop **2.39.0** (pytest verde; `validate.py` / `validation_tools.py` restano in `dependency/` come fallback `_inherited_review_ic_async`). Fork GitHub non staccato. **Stato:** split **2.38.0**. C2 native review loop **2.39.0** live. **2.39.1** `periscope/src` non importa più `validate.py` / `validation_tools.py` (PCB incluso). File PinScope restano in `dependency/`. Fork non staccato.
**Gate Michele:** sostituire/smettere di chiamare un modulo `dependency/` solo dopo pytest + deploy smoke. Se la verifica fallisce, resta il path ereditato. **Gate Michele:** sostituire/smettere di chiamare un modulo `dependency/` solo dopo pytest + deploy smoke. Se la verifica fallisce, resta il path ereditato.
**Sequenza:** split → sostituzione incrementale (C2 loop → C3 extraction → C4 PCB off `validate.py`). **Mai** empty-delete. Parsers/graph (C5/E) e auto-place fuori scope. AGPL resta. **Sequenza:** split → sostituzione incrementale (C2 loop → C3 extraction → C4 PCB off `validate.py`). **Mai** empty-delete. Parsers/graph (C5/E) e auto-place fuori scope. AGPL resta.
@@ -226,7 +226,7 @@ Qui sì si riscrive lengine ereditato. Ordine interno:
| C1 | Adapter: `validate.py` emette solo `Finding` grezzi → `complete_finding` | Già parziale; chiudere i campi doppi | | C1 | Adapter: `validate.py` emette solo `Finding` grezzi → `complete_finding` | Già parziale; chiudere i campi doppi |
| C2 | **REWRITE** loop per-IC native in `periscope/src` (DeepSeek, tools su `DesignGraph`) | **Shipped 2.39.0** live `review_ic_async``review_session`; PinScope files **kept**, not called from live loop | | C2 | **REWRITE** loop per-IC native in `periscope/src` (DeepSeek, tools su `DesignGraph`) | **Shipped 2.39.0** live `review_ic_async``review_session`; PinScope files **kept**, not called from live loop |
| C3 | Extraction: `local_skill.py` + schemi JSON (KEEP schema se identici; REWRITE orchestrazione Anthropic) | | | C3 | Extraction: `local_skill.py` + schemi JSON (KEEP schema se identici; REWRITE orchestrazione Anthropic) | |
| C4 | Spegnere import da `validate.py` nel PCB (`_parse_review` → parser finding nativo) | Toglie INDIRECT PCB→PinScope reviewer | | C4 | Spegnere import da `validate.py` nel PCB (`_parse_review` → parser finding nativo) | **Shipped 2.39.1** `pcb_validation``review_parse` / `review_session` |
| C5 | Test golden `simple_project` + Emmaforo: parity FACT/REQUIREMENT, non parity prose | | | C5 | Test golden `simple_project` + Emmaforo: parity FACT/REQUIREMENT, non parity prose | |
**Non in C:** riscrivere `graph.py` / parser KiCad. **Non in C:** riscrivere `graph.py` / parser KiCad.
@@ -50,7 +50,7 @@ def _check_model_defaults() -> list[str]:
def _check_simple_project_offline() -> list[str]: def _check_simple_project_offline() -> list[str]:
from backend.periscopex.models import DesignGraph from backend.periscopex.models import DesignGraph
from backend.periscopex.validation_tools import shortest_path from backend.periscopex.review_tools import shortest_path
errs: list[str] = [] errs: list[str] = []
graph_path = SIMPLE / "design_graph.json" graph_path = SIMPLE / "design_graph.json"
+2 -2
View File
@@ -12,14 +12,14 @@ from pypdf import PdfWriter
from backend.periscopex.models import DesignGraph from backend.periscopex.models import DesignGraph
from backend.periscopex.utils import safe_mpn from backend.periscopex.utils import safe_mpn
from backend.periscopex.validation_tools import ( from backend.periscopex.review_tools import (
EXCERPT_TOPICS, EXCERPT_TOPICS,
ExcerptState, ExcerptState,
execute_tool, execute_tool,
get_datasheet_excerpt, get_datasheet_excerpt,
) )
from backend.services.llm.types import PdfBlock from backend.services.llm.types import PdfBlock
from backend.services.validation import _signal_neighbors from backend.services.review_session import _signal_neighbors
GRAPH = SIMPLE_PROJECT / "design_graph.json" GRAPH = SIMPLE_PROJECT / "design_graph.json"
IC_MPNS = { IC_MPNS = {
+1 -1
View File
@@ -10,7 +10,7 @@ from backend.periscopex.finding_engine import (
lookup_rule, lookup_rule,
) )
from backend.periscopex.models import Finding from backend.periscopex.models import Finding
from backend.periscopex.validate import assign_finding_ids, _parse_review from backend.periscopex.review_parse import assign_finding_ids, parse_submit_review as _parse_review
def test_recommended_never_error(): def test_recommended_never_error():
+1 -1
View File
@@ -283,7 +283,7 @@ def test_pcb_ai_finding_gets_action():
def test_parse_review_action_field_without_recommendation(): def test_parse_review_action_field_without_recommendation():
from backend.periscopex.validate import _parse_review from backend.periscopex.review_parse import parse_submit_review as _parse_review
result = _parse_review( result = _parse_review(
{ {
+1 -1
View File
@@ -8,7 +8,7 @@ from backend.periscopex.quote_verify import (
quote_in_text, quote_in_text,
verify_finding_citations, verify_finding_citations,
) )
from backend.periscopex.validate import _parse_review from backend.periscopex.review_parse import parse_submit_review as _parse_review
from backend.services.llm.pdf_ingest import make_text_pdf from backend.services.llm.pdf_ingest import make_text_pdf
+1 -1
View File
@@ -1,6 +1,6 @@
"""submit_review parsing — ERROR without a datasheet quote is demoted.""" """submit_review parsing — ERROR without a datasheet quote is demoted."""
from backend.periscopex.validate import _parse_review from backend.periscopex.review_parse import parse_submit_review as _parse_review
def test_error_without_quote_becomes_unverified_warning(): def test_error_without_quote_becomes_unverified_warning():
+1 -1
View File
@@ -4,7 +4,7 @@ from __future__ import annotations
from backend.periscopex.library_gate import pintable_checksum, should_promote_extraction from backend.periscopex.library_gate import pintable_checksum, should_promote_extraction
from backend.periscopex.models import Component, ComponentType, DesignGraph, Net, NetType, PinConnection from backend.periscopex.models import Component, ComponentType, DesignGraph, Net, NetType, PinConnection
from backend.periscopex.validation_tools import shortest_path from backend.periscopex.review_tools import shortest_path
from backend.services.api_logs import cache_stats_by_stage from backend.services.api_logs import cache_stats_by_stage
+1 -1
View File
@@ -23,7 +23,7 @@ from pypdf import PdfWriter
from backend.config import settings from backend.config import settings
from backend.periscopex.models import Component, ComponentType, DesignGraph, Finding from backend.periscopex.models import Component, ComponentType, DesignGraph, Finding
from backend.periscopex.utils import safe_mpn from backend.periscopex.utils import safe_mpn
from backend.periscopex.validate import ReviewResult from backend.periscopex.review_parse import ReviewResult
from backend.services import validation as val from backend.services import validation as val
from backend.services.api_logs import ApiLogger from backend.services.api_logs import ApiLogger
from backend.services.storage import LocalStorageBackend from backend.services.storage import LocalStorageBackend