diff --git a/periscope/dependency/frontend/content/changelog.md b/periscope/dependency/frontend/content/changelog.md index 861bf06..de205fa 100644 --- a/periscope/dependency/frontend/content/changelog.md +++ b/periscope/dependency/frontend/content/changelog.md @@ -2,6 +2,13 @@ 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 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. diff --git a/periscope/src/backend/periscopex/bom_pcb_check.py b/periscope/src/backend/periscopex/bom_pcb_check.py index 6e83ddd..7d4e18f 100644 --- a/periscope/src/backend/periscopex/bom_pcb_check.py +++ b/periscope/src/backend/periscopex/bom_pcb_check.py @@ -26,7 +26,7 @@ from backend.periscopex.thermal_check import ( _net_voltage, _specs_values, ) -from backend.periscopex.validate import _match_constraints +from backend.periscopex.constraints_lookup import match_constraints as _match_constraints _FAMILIES = ( "LQFP", "TQFP", "VQFP", "WQFN", "HVQFN", "VQFN", "QFN", "DFN", diff --git a/periscope/src/backend/periscopex/dnp_check.py b/periscope/src/backend/periscopex/dnp_check.py index 3cdd389..58865f8 100644 --- a/periscope/src/backend/periscopex/dnp_check.py +++ b/periscope/src/backend/periscopex/dnp_check.py @@ -19,7 +19,7 @@ from backend.periscopex.passive_rail_check import ( _is_power_net, _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( r"(?:^|[_/])(EN|ENA|ENABLE|n?SHDN|nEN|EN_N|CHIP_EN)(?:$|[_/\d])", diff --git a/periscope/src/backend/periscopex/emi_check.py b/periscope/src/backend/periscopex/emi_check.py index cb57cca..761e87c 100644 --- a/periscope/src/backend/periscopex/emi_check.py +++ b/periscope/src/backend/periscopex/emi_check.py @@ -20,7 +20,7 @@ from backend.periscopex.pcb_net_match import ( refs_on_matched_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( r"(emi|emc|common[-\s]?mode|cmc|choke|ferrite\s*bead|shield(?:ing)?|" diff --git a/periscope/src/backend/periscopex/errata_check.py b/periscope/src/backend/periscopex/errata_check.py index 7ec2470..4f74faa 100644 --- a/periscope/src/backend/periscopex/errata_check.py +++ b/periscope/src/backend/periscopex/errata_check.py @@ -10,7 +10,7 @@ from backend.periscopex.passive_rail_check import ( _pin_name_tokens, _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__) diff --git a/periscope/src/backend/periscopex/filter_check.py b/periscope/src/backend/periscopex/filter_check.py index 02b2dc5..2e54809 100644 --- a/periscope/src/backend/periscopex/filter_check.py +++ b/periscope/src/backend/periscopex/filter_check.py @@ -25,7 +25,7 @@ from backend.periscopex.passive_rail_check import ( _pin_name_tokens, _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") _DCR_MAX_KEYS = ("max_ferrite_dcr_ohms", "ferrite_dcr_max_ohms", "max_bead_dcr_ohms") diff --git a/periscope/src/backend/periscopex/hf_coverage_check.py b/periscope/src/backend/periscopex/hf_coverage_check.py index dafd5af..adf1a2c 100644 --- a/periscope/src/backend/periscopex/hf_coverage_check.py +++ b/periscope/src/backend/periscopex/hf_coverage_check.py @@ -15,7 +15,7 @@ from backend.periscopex.passive_rail_check import ( _is_regulator_output_pin, _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 _HF_MAX_F = 1e-6 diff --git a/periscope/src/backend/periscopex/internal_features_check.py b/periscope/src/backend/periscopex/internal_features_check.py index 408a4e5..bcdaa08 100644 --- a/periscope/src/backend/periscopex/internal_features_check.py +++ b/periscope/src/backend/periscopex/internal_features_check.py @@ -7,7 +7,7 @@ from backend.periscopex.passive_rail_check import ( _pin_name_tokens, _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( diff --git a/periscope/src/backend/periscopex/passive_rail_check.py b/periscope/src/backend/periscopex/passive_rail_check.py index 600d17e..b756d19 100644 --- a/periscope/src/backend/periscopex/passive_rail_check.py +++ b/periscope/src/backend/periscopex/passive_rail_check.py @@ -19,7 +19,7 @@ from backend.periscopex.models import ( NetType, 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.resolve_passives import _parse_spice_value diff --git a/periscope/src/backend/periscopex/pcb_power_thermal.py b/periscope/src/backend/periscopex/pcb_power_thermal.py index a5edffb..e2e97e8 100644 --- a/periscope/src/backend/periscopex/pcb_power_thermal.py +++ b/periscope/src/backend/periscopex/pcb_power_thermal.py @@ -28,7 +28,7 @@ from backend.periscopex.thermal_check import ( _pin_net_by_role, _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_B = 0.44 diff --git a/periscope/src/backend/periscopex/pi_check.py b/periscope/src/backend/periscopex/pi_check.py index 7dbb084..33c755b 100644 --- a/periscope/src/backend/periscopex/pi_check.py +++ b/periscope/src/backend/periscopex/pi_check.py @@ -18,7 +18,7 @@ from backend.periscopex.pcb_net_match import ( normalize_kicad_hierarchy_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") diff --git a/periscope/src/backend/periscopex/placement_check.py b/periscope/src/backend/periscopex/placement_check.py index f824dd7..efea379 100644 --- a/periscope/src/backend/periscopex/placement_check.py +++ b/periscope/src/backend/periscopex/placement_check.py @@ -24,7 +24,7 @@ from backend.periscopex.models import ( LayoutPad, ) 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: diff --git a/periscope/src/backend/periscopex/power_margin_check.py b/periscope/src/backend/periscopex/power_margin_check.py index 2389550..084cd99 100644 --- a/periscope/src/backend/periscopex/power_margin_check.py +++ b/periscope/src/backend/periscopex/power_margin_check.py @@ -27,7 +27,7 @@ from backend.periscopex.thermal_check import ( _pin_net_by_role, _specs_values, ) -from backend.periscopex.validate import _match_constraints +from backend.periscopex.constraints_lookup import match_constraints as _match_constraints _IQ_KEYS = ( "iq_a", "quiescent_current_a", "supply_current_a", "idd_a", "icc_a", diff --git a/periscope/src/backend/periscopex/review_fingerprint.py b/periscope/src/backend/periscopex/review_fingerprint.py index e2826c7..7beb545 100644 --- a/periscope/src/backend/periscopex/review_fingerprint.py +++ b/periscope/src/backend/periscopex/review_fingerprint.py @@ -6,7 +6,7 @@ import hashlib import json 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( diff --git a/periscope/src/backend/periscopex/sequencing_check.py b/periscope/src/backend/periscopex/sequencing_check.py index 9c15aa6..bfefe14 100644 --- a/periscope/src/backend/periscopex/sequencing_check.py +++ b/periscope/src/backend/periscopex/sequencing_check.py @@ -20,7 +20,7 @@ from backend.periscopex.thermal_check import ( _pin_net_by_role, _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) _EN_RE = re.compile( diff --git a/periscope/src/backend/periscopex/si_check.py b/periscope/src/backend/periscopex/si_check.py index 7250827..53be5d7 100644 --- a/periscope/src/backend/periscopex/si_check.py +++ b/periscope/src/backend/periscopex/si_check.py @@ -14,7 +14,7 @@ import re 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_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 = ( ("_DP", "_DM"), diff --git a/periscope/src/backend/periscopex/thermal_check.py b/periscope/src/backend/periscopex/thermal_check.py index 221504c..ccf6f44 100644 --- a/periscope/src/backend/periscopex/thermal_check.py +++ b/periscope/src/backend/periscopex/thermal_check.py @@ -25,7 +25,7 @@ from backend.periscopex.models import ( ) from backend.periscopex.passive_rail_check import _pin_name_tokens 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 _TJ_WARN_C = 125.0 diff --git a/periscope/src/backend/periscopex/timing_check.py b/periscope/src/backend/periscopex/timing_check.py index 4248e8d..6f9cc98 100644 --- a/periscope/src/backend/periscopex/timing_check.py +++ b/periscope/src/backend/periscopex/timing_check.py @@ -16,7 +16,7 @@ from backend.periscopex.passive_rail_check import ( _resistor_to_ground, _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") _VIH_KEYS = ("vih", "vih_min_v", "v_ih_min", "vih_min") diff --git a/periscope/src/backend/services/pcb_validation.py b/periscope/src/backend/services/pcb_validation.py index ab06f32..5011d02 100644 --- a/periscope/src/backend/services/pcb_validation.py +++ b/periscope/src/backend/services/pcb_validation.py @@ -15,9 +15,10 @@ from backend.periscopex.pcb_review import ( build_pcb_layout_context, 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.validation import review_ic_async +from backend.services.review_session import review_ic_async log = logging.getLogger(__name__) diff --git a/periscope/src/docs/development/PINSCOPE_INDEPENDENCE_PLAN.md b/periscope/src/docs/development/PINSCOPE_INDEPENDENCE_PLAN.md index 9792ab5..5c83805 100644 --- a/periscope/src/docs/development/PINSCOPE_INDEPENDENCE_PLAN.md +++ b/periscope/src/docs/development/PINSCOPE_INDEPENDENCE_PLAN.md @@ -1,6 +1,6 @@ # 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. **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 l’engine ereditato. Ordine interno: | 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 | | 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 | | **Non in C:** riscrivere `graph.py` / parser KiCad. diff --git a/periscope/src/scripts/smoke_simple_project.py b/periscope/src/scripts/smoke_simple_project.py index 6829795..8c9b754 100644 --- a/periscope/src/scripts/smoke_simple_project.py +++ b/periscope/src/scripts/smoke_simple_project.py @@ -50,7 +50,7 @@ def _check_model_defaults() -> list[str]: def _check_simple_project_offline() -> list[str]: 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] = [] graph_path = SIMPLE / "design_graph.json" diff --git a/tests/test_datasheet_excerpt_tool.py b/tests/test_datasheet_excerpt_tool.py index 171e837..248a319 100644 --- a/tests/test_datasheet_excerpt_tool.py +++ b/tests/test_datasheet_excerpt_tool.py @@ -12,14 +12,14 @@ from pypdf import PdfWriter from backend.periscopex.models import DesignGraph from backend.periscopex.utils import safe_mpn -from backend.periscopex.validation_tools import ( +from backend.periscopex.review_tools import ( EXCERPT_TOPICS, ExcerptState, execute_tool, get_datasheet_excerpt, ) 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" IC_MPNS = { diff --git a/tests/test_finding_engine.py b/tests/test_finding_engine.py index e310453..ebdeb53 100644 --- a/tests/test_finding_engine.py +++ b/tests/test_finding_engine.py @@ -10,7 +10,7 @@ from backend.periscopex.finding_engine import ( lookup_rule, ) 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(): diff --git a/tests/test_pcb_review.py b/tests/test_pcb_review.py index c73d939..e403fc5 100644 --- a/tests/test_pcb_review.py +++ b/tests/test_pcb_review.py @@ -283,7 +283,7 @@ def test_pcb_ai_finding_gets_action(): 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( { diff --git a/tests/test_quote_verify.py b/tests/test_quote_verify.py index e4e2b75..a3ba4cd 100644 --- a/tests/test_quote_verify.py +++ b/tests/test_quote_verify.py @@ -8,7 +8,7 @@ from backend.periscopex.quote_verify import ( quote_in_text, 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 diff --git a/tests/test_review_parse.py b/tests/test_review_parse.py index 114fbb3..bf536f5 100644 --- a/tests/test_review_parse.py +++ b/tests/test_review_parse.py @@ -1,6 +1,6 @@ """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(): diff --git a/tests/test_roadmap_integrations.py b/tests/test_roadmap_integrations.py index d1e8bd6..67d759e 100644 --- a/tests/test_roadmap_integrations.py +++ b/tests/test_roadmap_integrations.py @@ -4,7 +4,7 @@ from __future__ import annotations 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.validation_tools import shortest_path +from backend.periscopex.review_tools import shortest_path from backend.services.api_logs import cache_stats_by_stage diff --git a/tests/test_validation_concurrency.py b/tests/test_validation_concurrency.py index 235e2ae..49f0ce2 100644 --- a/tests/test_validation_concurrency.py +++ b/tests/test_validation_concurrency.py @@ -23,7 +23,7 @@ from pypdf import PdfWriter from backend.config import settings from backend.periscopex.models import Component, ComponentType, DesignGraph, Finding 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.api_logs import ApiLogger from backend.services.storage import LocalStorageBackend