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:
@@ -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
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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 l’engine 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"
|
||||||
|
|||||||
@@ -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 = {
|
||||||
|
|||||||
@@ -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():
|
||||||
|
|||||||
@@ -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(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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,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():
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user