@@ -1203,7 +1203,7 @@ async def send_feedback_reply_email(
finding_designator: str | None = None,
finding_mpn: str | None = None,
) -> None:
- """Notify the original submitter that the Pinscope team replied. Fire-and-forget."""
+ """Notify the original submitter that the Periscope team replied. Fire-and-forget."""
if not settings.use_email:
return
@@ -1229,15 +1229,15 @@ async def send_feedback_reply_email(
first_name = full_name.split()[0] if full_name else "there"
msg = MIMEMultipart("alternative")
- msg["From"] = f"Pinscope <{settings.email_sender}>"
+ msg["From"] = f"Periscope <{settings.email_sender}>"
msg["To"] = to_email
- msg["Subject"] = "The Pinscope team replied to your feedback"
+ msg["Subject"] = "The Periscope team replied to your feedback"
# Plain text fallback
text_lines = [
f"Hi {first_name},",
"",
- "The Pinscope team just replied to your feedback.",
+ "The Periscope team just replied to your feedback.",
"",
"— Reply —",
reply_text,
@@ -1245,10 +1245,10 @@ async def send_feedback_reply_email(
"— Your original message —",
original_message,
"",
- f"View in Pinscope: {settings.email_frontend_url}/feedback",
+ f"View in Periscope: {settings.email_frontend_url}/feedback",
"",
"Thank you so much for taking the time to share your feedback — we truly value it.",
- "— The Pinscope team",
+ "— The Periscope team",
]
msg.attach(MIMEText("\n".join(text_lines), "plain"))
diff --git a/backend/services/extraction.py b/backend/services/extraction.py
index 7f21ad5..0a35c1b 100644
--- a/backend/services/extraction.py
+++ b/backend/services/extraction.py
@@ -17,8 +17,8 @@ import tempfile
import time
from pathlib import Path
-from backend.pinscopex.utils import safe_mpn
-from backend.pinscopex.models import (
+from backend.periscopex.utils import safe_mpn
+from backend.periscopex.models import (
CapacitorSpecs,
ComponentConstraints,
ComponentModel,
@@ -27,7 +27,7 @@ from backend.pinscopex.models import (
NetType,
SimpleComponentSpecs,
)
-from backend.pinscopex.taxonomy import (
+from backend.periscopex.taxonomy import (
TAXONOMY_DIR,
add_subtype,
format_for_prompt,
@@ -399,13 +399,13 @@ def _coerce_abs_max(raw: object) -> list[dict]:
def _coerce_layout_rules(raw: object) -> list[dict]:
- from backend.pinscopex.layout_rules import validate_layout_rules
+ from backend.periscopex.layout_rules import validate_layout_rules
rows, _errors = validate_layout_rules(raw if isinstance(raw, list) else [])
return rows
def _coerce_internal_features(raw: object):
- from backend.pinscopex.models import InternalFeatures
+ from backend.periscopex.models import InternalFeatures
if not isinstance(raw, dict):
return None
try:
@@ -1088,7 +1088,7 @@ async def auto_resolve_specs(
# Convert passive SimpleComponentSpecs to typed models
if component_type == "passive":
- from backend.pinscopex.resolve_passives import simple_to_typed_passive_specs
+ from backend.periscopex.resolve_passives import simple_to_typed_passive_specs
typed = simple_to_typed_passive_specs(specs)
return ComponentModel(mpn=mpn, specs=typed)
@@ -1260,7 +1260,7 @@ async def resolve_from_value(
)
if component_type == "passive":
- from backend.pinscopex.resolve_passives import simple_to_typed_passive_specs
+ from backend.periscopex.resolve_passives import simple_to_typed_passive_specs
typed = simple_to_typed_passive_specs(specs)
return ComponentModel(mpn=mpn, specs=typed)
diff --git a/backend/services/llm/local_skill.py b/backend/services/llm/local_skill.py
index 338cb17..0a4cffb 100644
--- a/backend/services/llm/local_skill.py
+++ b/backend/services/llm/local_skill.py
@@ -62,7 +62,7 @@ def load_skill_validator(skill_name: str):
if not path.is_file():
return None
spec = importlib.util.spec_from_file_location(
- f"pinscope_skill_{skill_name.replace('-', '_')}_validate", path,
+ f"periscope_skill_{skill_name.replace('-', '_')}_validate", path,
)
if spec is None or spec.loader is None:
return None
diff --git a/backend/services/llm/pdf_ingest.py b/backend/services/llm/pdf_ingest.py
index 60571c6..934c506 100644
--- a/backend/services/llm/pdf_ingest.py
+++ b/backend/services/llm/pdf_ingest.py
@@ -15,7 +15,7 @@ import logging
import re
from pathlib import Path
-from backend.pinscopex.pdf_text import (
+from backend.periscopex.pdf_text import (
extract_pdf_document_text,
fitz_page_text,
page_is_sparse,
diff --git a/backend/services/local_jwt.py b/backend/services/local_jwt.py
index 90cf8f4..aff8b9b 100644
--- a/backend/services/local_jwt.py
+++ b/backend/services/local_jwt.py
@@ -1,4 +1,4 @@
-"""Pinscope local JWT helpers (HS256)."""
+"""Periscope local JWT helpers (HS256)."""
from __future__ import annotations
@@ -21,7 +21,7 @@ def issue_token(user_id: str, email: str) -> str:
payload = {
"sub": user_id,
"email": email,
- "iss": "pinscope-local",
+ "iss": "periscope-local",
"iat": now,
"exp": now + timedelta(days=TOKEN_TTL_DAYS),
}
@@ -37,7 +37,7 @@ def decode_token(token: str) -> dict[str, Any] | None:
token,
secret,
algorithms=[ALGORITHM],
- issuer="pinscope-local",
+ issuer="periscope-local",
options={"verify_aud": False},
leeway=10,
)
diff --git a/backend/services/local_users.py b/backend/services/local_users.py
index 8380490..e835ca6 100644
--- a/backend/services/local_users.py
+++ b/backend/services/local_users.py
@@ -1,4 +1,4 @@
-"""Local Pinscope user directory (self-host auth, no Clerk).
+"""Local Periscope user directory (self-host auth, no Clerk).
Users live under ``data/auth/users/{user_id}.json`` with an email index.
Passwords use stdlib ``hashlib.scrypt``.
diff --git a/backend/services/normalize_findings.py b/backend/services/normalize_findings.py
index e481ea6..d909635 100644
--- a/backend/services/normalize_findings.py
+++ b/backend/services/normalize_findings.py
@@ -23,7 +23,7 @@ from datetime import datetime, timezone
from typing import Awaitable, Callable
from backend.config import settings
-from backend.pinscopex.models import Finding
+from backend.periscopex.models import Finding
from backend.services.api_logs import ApiLogger
from backend.services.llm import Message, TextBlock
from backend.services.llm.factory import call_with_fallback
diff --git a/backend/services/passive_from_distributor.py b/backend/services/passive_from_distributor.py
index cdd6fca..c102b6c 100644
--- a/backend/services/passive_from_distributor.py
+++ b/backend/services/passive_from_distributor.py
@@ -8,8 +8,8 @@ from __future__ import annotations
import re
-from backend.pinscopex.models import ComponentModel, SimpleComponentSpecs
-from backend.pinscopex.resolve_passives import simple_to_typed_passive_specs
+from backend.periscopex.models import ComponentModel, SimpleComponentSpecs
+from backend.periscopex.resolve_passives import simple_to_typed_passive_specs
_CAP = re.compile(
r"(?P\d+(?:\.\d+)?)\s*(?P[pnuμµmk])?\s*[fF]\b",
diff --git a/backend/services/passive_from_mpn.py b/backend/services/passive_from_mpn.py
index b210b19..5d7d1e4 100644
--- a/backend/services/passive_from_mpn.py
+++ b/backend/services/passive_from_mpn.py
@@ -9,8 +9,8 @@ from __future__ import annotations
import re
-from backend.pinscopex.models import ComponentModel, SimpleComponentSpecs
-from backend.pinscopex.resolve_passives import simple_to_typed_passive_specs
+from backend.periscopex.models import ComponentModel, SimpleComponentSpecs
+from backend.periscopex.resolve_passives import simple_to_typed_passive_specs
from backend.services.passive_from_distributor import _spice
_SIZE = r"(?:0201|0402|0603|0805|1206|1210|1812|2010|2512)"
diff --git a/backend/services/passive_from_value.py b/backend/services/passive_from_value.py
index 7b1ba21..cb839cd 100644
--- a/backend/services/passive_from_value.py
+++ b/backend/services/passive_from_value.py
@@ -4,8 +4,8 @@ from __future__ import annotations
import re
-from backend.pinscopex.models import ComponentModel, SimpleComponentSpecs
-from backend.pinscopex.resolve_passives import simple_to_typed_passive_specs
+from backend.periscopex.models import ComponentModel, SimpleComponentSpecs
+from backend.periscopex.resolve_passives import simple_to_typed_passive_specs
from backend.services.passive_from_distributor import _spice
_PLACEHOLDER = re.compile(
diff --git a/backend/services/pipeline.py b/backend/services/pipeline.py
index eb2f962..af554b9 100644
--- a/backend/services/pipeline.py
+++ b/backend/services/pipeline.py
@@ -33,15 +33,15 @@ from pathlib import Path
from typing import Any, Awaitable, Callable
-from backend.pinscopex.models import ComponentType
-from backend.pinscopex.utils import natural_sort_key, safe_mpn
-from backend.pinscopex.bom_summary import build_bom_summary
-from backend.pinscopex.derating import build_derating_table
-from backend.pinscopex.validate import _load_datasheets
-from backend.pinscopex.graph import build_graph
-from backend.pinscopex.parsers import parse_bom, parse_netlist_any
-from backend.pinscopex.resolve_passives import SkippedItem, load_patterns, resolve_mpn
-from backend.pinscopex.taxonomy import SIMPLE_TYPES, type_for_ref
+from backend.periscopex.models import ComponentType
+from backend.periscopex.utils import natural_sort_key, safe_mpn
+from backend.periscopex.bom_summary import build_bom_summary
+from backend.periscopex.derating import build_derating_table
+from backend.periscopex.validate import _load_datasheets
+from backend.periscopex.graph import build_graph
+from backend.periscopex.parsers import parse_bom, parse_netlist_any
+from backend.periscopex.resolve_passives import SkippedItem, load_patterns, resolve_mpn
+from backend.periscopex.taxonomy import SIMPLE_TYPES, type_for_ref
from backend.config import settings
from backend.services import admin_settings as settings_svc
@@ -196,7 +196,7 @@ def _cancel_gate_check(ctx: PipelineContext) -> None:
class PipelineWorkspace:
"""Downloads project files from storage to a temp dir for pipeline execution.
- The pinscopex core library operates on local paths. This context manager
+ The periscopex core library operates on local paths. This context manager
downloads inputs at enter, provides local paths, and uploads results at exit.
"""
@@ -264,7 +264,7 @@ class PipelineWorkspace:
self._upload_file("bom_summary.json")
self._upload_file("derating.json")
self._upload_file("report.json")
- self._upload_file("pinscope-findings.json")
+ self._upload_file("periscope-findings.json")
self._upload_file("review_fingerprints.json")
self._upload_file("api_logs.jsonl")
@@ -763,7 +763,7 @@ async def _stage_ic_extraction(ctx: PipelineContext) -> None:
# Pre-categorize: workspace cache, library cache, or needs extraction
from backend.config import settings as app_settings
- from backend.pinscopex.layout_rules import needs_layout_rules_refresh
+ from backend.periscopex.layout_rules import needs_layout_rules_refresh
layout_scan_ver = app_settings.get_default_model_version()
_ic_cache: dict[str, tuple] = {}
@@ -863,7 +863,7 @@ async def _stage_ic_extraction(ctx: PipelineContext) -> None:
extracted_key = f"{ctx.ws.prefix}/extracted/{safe}.json"
ctx.storage.upload_from_local(json_path, extracted_key)
try:
- from backend.pinscopex.library_gate import should_promote_extraction
+ from backend.periscopex.library_gate import should_promote_extraction
payload = json.loads(json_path.read_text(encoding="utf-8"))
ok, reason = should_promote_extraction(payload)
@@ -1545,7 +1545,7 @@ def _write_layout_graph(ws: PipelineWorkspace, project_id: str) -> None:
if not pcb.is_file():
return
try:
- from backend.pinscopex.parsers_kicad_pcb import parse_kicad_pcb
+ from backend.periscopex.parsers_kicad_pcb import parse_kicad_pcb
layout = parse_kicad_pcb(pcb)
out = ws.local_path("layout_graph.json")
@@ -1561,8 +1561,8 @@ def _write_layout_graph(ws: PipelineWorkspace, project_id: str) -> None:
def _write_functional_groups(ws: PipelineWorkspace, graph) -> None:
"""Layout F1: topology domains/groups (no mm). Fail-soft."""
try:
- from backend.pinscopex.functional_groups import build_functional_groups
- from backend.pinscopex.validate import _build_constraints_map, _load_datasheets
+ from backend.periscopex.functional_groups import build_functional_groups
+ from backend.periscopex.validate import _build_constraints_map, _load_datasheets
extracted_dir = ws.local_path("extracted")
cmap = {}
@@ -1584,8 +1584,8 @@ def _write_impedance_nets(ws: PipelineWorkspace, graph) -> None:
if not path.is_file():
return
try:
- from backend.pinscopex.impedance_traces import analyze_where_needed
- from backend.pinscopex.models import LayoutGraph
+ from backend.periscopex.impedance_traces import analyze_where_needed
+ from backend.periscopex.models import LayoutGraph
layout = LayoutGraph.model_validate_json(path.read_text())
report = analyze_where_needed(layout, graph)
@@ -1792,11 +1792,11 @@ async def _stage_validation(ctx: PipelineContext) -> None:
fp_path = ctx.ws.local_path("review_fingerprints.json")
current_fp: dict[str, str] = {}
try:
- from backend.pinscopex.review_fingerprint import (
+ from backend.periscopex.review_fingerprint import (
graph_ic_fingerprints,
skip_unchanged_ics,
)
- from backend.pinscopex.validate import _build_constraints_map, _load_datasheets
+ from backend.periscopex.validate import _build_constraints_map, _load_datasheets
cmap = _build_constraints_map(_load_datasheets(extracted_dir))
current_fp = graph_ic_fingerprints(ctx.graph, cmap)
diff --git a/backend/services/placement_pipeline.py b/backend/services/placement_pipeline.py
index 55f6a3a..26f3f81 100644
--- a/backend/services/placement_pipeline.py
+++ b/backend/services/placement_pipeline.py
@@ -11,10 +11,10 @@ from __future__ import annotations
import logging
from pathlib import Path
-from backend.pinscopex.functional_groups import build_placement_plan
-from backend.pinscopex.graph import build_graph
-from backend.pinscopex.models import ComponentConstraints, DesignGraph, LayoutGraph
-from backend.pinscopex.placement_pack import build_placement_pack
+from backend.periscopex.functional_groups import build_placement_plan
+from backend.periscopex.graph import build_graph
+from backend.periscopex.models import ComponentConstraints, DesignGraph, LayoutGraph
+from backend.periscopex.placement_pack import build_placement_pack
from backend.services import projects as proj_svc
from backend.services.pipeline import PipelineWorkspace, broker
from backend.services.storage import StorageBackend
@@ -218,7 +218,7 @@ def _load_layout(ws: PipelineWorkspace) -> LayoutGraph | None:
if not pcb.is_file():
return None
try:
- from backend.pinscopex.parsers_kicad_pcb import parse_kicad_pcb
+ from backend.periscopex.parsers_kicad_pcb import parse_kicad_pcb
layout = parse_kicad_pcb(pcb)
cached.write_text(layout.model_dump_json(indent=2) + "\n")
diff --git a/backend/services/projects.py b/backend/services/projects.py
index 35ca564..899609d 100644
--- a/backend/services/projects.py
+++ b/backend/services/projects.py
@@ -11,7 +11,7 @@ Each project lives at users/{user_id}/projects/{id}/ with:
models/ — cached component specs
design_graph.json — graph output
report.json — validation report
- pinscope-findings.json — KiCad cad-bridge (plugin pan-and-zoom)
+ periscope-findings.json — KiCad cad-bridge (plugin pan-and-zoom)
Library (global, shared across users):
library/extracted/{mpn}.json
@@ -31,9 +31,9 @@ from typing import Any
log = logging.getLogger(__name__)
-from pydantic import BaseModel
+from pydantic import AliasChoices, BaseModel, Field
-from backend.pinscopex.utils import safe_mpn
+from backend.periscopex.utils import safe_mpn
from backend.services.storage import StaleGeneration, StorageBackend
@@ -120,9 +120,13 @@ class ProjectMeta(BaseModel):
pause_reason: str | None = None
completed_review_refs: list[str] = [] # IC refs already reviewed (persists across pauses)
- # Pinscope app version that generated the project's report.
+ # Periscope app version that generated the project's report.
# Stamped on the first /start transition and preserved thereafter.
- pinscope_version: str | None = None
+ # Accept legacy pinscope_version from project.json written before the rebrand.
+ periscope_version: str | None = Field(
+ default=None,
+ validation_alias=AliasChoices("periscope_version", "pinscope_version"),
+ )
# Worker bookkeeping (set by the API on enqueue, read by /events SSE
# and by the stale-running sweeper).
@@ -166,7 +170,7 @@ def completed_review_refs_for_retry(
for ref in (report.get("review_errors") or {}):
if ref:
failed.add(str(ref))
- from backend.pinscopex.utils import natural_sort_key
+ from backend.periscopex.utils import natural_sort_key
kept = [r for r in (meta.completed_review_refs or []) if r and r not in failed]
return sorted(kept, key=natural_sort_key)
@@ -484,7 +488,7 @@ def clear_project_extractions(
"bom_summary.json",
"derating.json",
"report.json",
- "pinscope-findings.json",
+ "periscope-findings.json",
"review_fingerprints.json",
"api_logs.jsonl",
"graph_voltage_updates.json",
@@ -519,7 +523,7 @@ def reopen_project(
"bom_summary.json",
"derating.json",
"report.json",
- "pinscope-findings.json",
+ "periscope-findings.json",
"review_fingerprints.json",
"api_logs.jsonl",
"graph_voltage_updates.json",
@@ -936,7 +940,7 @@ def library_has_datasheet(
return key
# 3. Pattern-based fallback for passives
if patterns:
- from backend.pinscopex.resolve_passives import resolve_mpn
+ from backend.periscopex.resolve_passives import resolve_mpn
match = resolve_mpn(mpn, patterns)
if match is not None:
@@ -1121,11 +1125,11 @@ def list_library_patterns(storage: StorageBackend) -> list[str]:
def load_library_patterns(storage: StorageBackend):
"""Load and parse all passive patterns from the library.
- For local backend, delegates to pinscopex. For GCS, downloads to temp first.
+ For local backend, delegates to periscopex. For GCS, downloads to temp first.
This function is only used by the library/check endpoint — during pipeline
execution, patterns are loaded from the workspace temp directory.
"""
- from backend.pinscopex.resolve_passives import load_patterns
+ from backend.periscopex.resolve_passives import load_patterns
from backend.services.storage import LocalStorageBackend
diff --git a/backend/services/validation.py b/backend/services/validation.py
index cbe25ce..0866273 100644
--- a/backend/services/validation.py
+++ b/backend/services/validation.py
@@ -21,7 +21,7 @@ from typing import Awaitable, Callable
log = logging.getLogger(__name__)
-from backend.pinscopex.models import (
+from backend.periscopex.models import (
ComponentConstraints,
ComponentType,
DesignGraph,
@@ -30,7 +30,7 @@ from backend.pinscopex.models import (
NetType,
ValidationReport,
)
-from backend.pinscopex.validate import (
+from backend.periscopex.validate import (
SYSTEM_PROMPT,
_MAX_REVIEW_TURNS,
ReviewResult,
@@ -41,30 +41,30 @@ from backend.pinscopex.validate import (
build_component_context,
_parse_review,
)
-from backend.pinscopex.quote_verify import verify_finding_citations
-from backend.pinscopex.utils import safe_mpn
-from backend.pinscopex.pin_mux_check import check_pin_mux_feasibility
-from backend.pinscopex.led_current_check import check_led_current
-from backend.pinscopex.passive_rail_check import (
+from backend.periscopex.quote_verify import verify_finding_citations
+from backend.periscopex.utils import safe_mpn
+from backend.periscopex.pin_mux_check import check_pin_mux_feasibility
+from backend.periscopex.led_current_check import check_led_current
+from backend.periscopex.passive_rail_check import (
check_i2c_pullups,
check_reset_pullups,
check_supply_decoupling,
)
-from backend.pinscopex.bom_match_check import check_bom_schematic_match
-from backend.pinscopex.hf_coverage_check import check_hf_decoupling_coverage
-from backend.pinscopex.cad_bridge import annotate_findings_cad, build_cad_bridge, write_cad_bridge
-from backend.pinscopex.filter_check import check_filters
-from backend.pinscopex.thermal_check import check_thermal
-from backend.pinscopex.power_margin_check import check_power_margin
-from backend.pinscopex.sequencing_check import check_power_sequencing
-from backend.pinscopex.dnp_check import check_dnp_enables
-from backend.pinscopex.lifecycle import check_lifecycle, load_lifecycle_dir
-from backend.pinscopex.errata_check import check_errata
-from backend.pinscopex.internal_features_check import check_internal_features
-from backend.pinscopex.placement_check import check_placement
-from backend.pinscopex.si_check import check_si
-from backend.pinscopex.crystal_cl_check import check_crystal_cl
-from backend.pinscopex.nc_pin_check import check_nc_pins
+from backend.periscopex.bom_match_check import check_bom_schematic_match
+from backend.periscopex.hf_coverage_check import check_hf_decoupling_coverage
+from backend.periscopex.cad_bridge import annotate_findings_cad, build_cad_bridge, write_cad_bridge
+from backend.periscopex.filter_check import check_filters
+from backend.periscopex.thermal_check import check_thermal
+from backend.periscopex.power_margin_check import check_power_margin
+from backend.periscopex.sequencing_check import check_power_sequencing
+from backend.periscopex.dnp_check import check_dnp_enables
+from backend.periscopex.lifecycle import check_lifecycle, load_lifecycle_dir
+from backend.periscopex.errata_check import check_errata
+from backend.periscopex.internal_features_check import check_internal_features
+from backend.periscopex.placement_check import check_placement
+from backend.periscopex.si_check import check_si
+from backend.periscopex.crystal_cl_check import check_crystal_cl
+from backend.periscopex.nc_pin_check import check_nc_pins
TRACE_VERSION = 1
@@ -139,14 +139,14 @@ def _assistant_text(blocks) -> str:
except Exception:
log.exception("trace: assistant_text extraction failed")
return "\n".join(parts)
-from backend.pinscopex.validation_tools import (
+from backend.periscopex.validation_tools import (
ALL_TOOLS,
SUBMIT_REVIEW_SCHEMA,
ConstraintsMap,
ExcerptState,
execute_tool,
)
-from backend.pinscopex.utils import safe_mpn
+from backend.periscopex.utils import safe_mpn
from backend.config import settings
from backend.services.api_logs import ApiLogger
@@ -639,7 +639,7 @@ def _find_pdf(
then tries to download from the library.
"""
from backend.services.datasheet_finder import find_local_pdf
- from backend.pinscopex.utils import safe_mpn as _safe
+ from backend.periscopex.utils import safe_mpn as _safe
mpn = (mpn or "").strip()
if not mpn:
@@ -836,7 +836,7 @@ async def validate_design_async(
try:
prefix_id = (project_prefix or "").rstrip("/").rsplit("/", 1)[-1]
bridge = build_cad_bridge(report, prefix_id or report.project)
- write_cad_bridge(existing_path.with_name("pinscope-findings.json"), bridge)
+ write_cad_bridge(existing_path.with_name("periscope-findings.json"), bridge)
except Exception:
log.exception("cad bridge write failed")
return report
diff --git a/docker-compose.yml b/docker-compose.yml
index 21fade5..2c6de20 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -5,7 +5,7 @@ services:
context: .
dockerfile: backend/Dockerfile
- container_name: pinscope-backend
+ container_name: periscope-backend
restart: unless-stopped
env_file:
@@ -24,7 +24,7 @@ services:
- "8080:8080"
networks:
- - pinscope
+ - periscope
frontend:
build:
@@ -34,7 +34,7 @@ services:
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://localhost:8080}
NEXT_PUBLIC_AUTH_MODE: ${NEXT_PUBLIC_AUTH_MODE:-}
- container_name: pinscope-frontend
+ container_name: periscope-frontend
restart: unless-stopped
depends_on:
@@ -44,9 +44,9 @@ services:
- "3000:3000"
networks:
- - pinscope
+ - periscope
networks:
- pinscope:
+ periscope:
driver: bridge
diff --git a/docs/piano-implementazione.md b/docs/piano-implementazione.md
index 8b286d2..9615dd3 100644
--- a/docs/piano-implementazione.md
+++ b/docs/piano-implementazione.md
@@ -1,8 +1,8 @@
-# Piano di implementazione — Pinscope
+# Piano di implementazione — Periscope
Documento di lavoro **prima dello sviluppo**. La lista dell’utente è il minimo; sotto c’è anche ciò che serve perché quella lista non resti un insieme di moduli scollegati.
-**Questo piano copre due prodotti.** Pinscope originale resta il primo. Layout/plugin/placement mm sono il secondo. Non mescolare i changelog né vendere il secondo come “un po’ di Pinscope in più”.
+**Questo piano copre due prodotti.** Periscope originale resta il primo. Layout/plugin/placement mm sono il secondo. Non mescolare i changelog né vendere il secondo come “un po’ di Periscope in più”.
Stato del codice di riferimento: branch `cursor/deepseek-71c5` (post DeepSeek V4.1, costi USD, replace BOM/netlist, parser KiCad, fingerprint review, auth multi-utente, PCB pad nets).
@@ -10,7 +10,7 @@ Stato del codice di riferimento: branch `cursor/deepseek-71c5` (post DeepSeek V4
## 0b. Roadmap DeepSeek / crescita (integrata)
-Fonte originale: canvas *Pinscope: crescita e DeepSeek*. Qui lo stato operativo.
+Fonte originale: canvas *Periscope: crescita e DeepSeek*. Qui lo stato operativo.
| Fase | Voce | Stato |
| --- | --- | --- |
@@ -48,27 +48,27 @@ Obiettivo unico: **routing migliore** (loop corti, meno crossing, canali liberi)
1. **F1 polish (ora)** — satelliti classificatì; `other` nascosti; Domains/Rails = primary rail.
2. **C4 fill** — riestrazione IC → `layout_rules` con `max_distance_mm` dove il PDF lo dice (skill 1.10.0).
-3. **PCB gate** — upload `.kicad_pcb` → `layout_graph.json` (footprint xy già usati da PS-PLC*).
+3. **PCB gate** — upload `.kicad_pcb` → `layout_graph.json` (footprint xy già usati da PE-PLC*).
4. **F2 pack v1** — per ogni `decoupling_proximity` numerica: proporre xy satellite entro `max_distance_mm` dal pad (già skeleton); UI Pack lista proposte.
5. **F2 pack v2** — collisioni courtyard, stesso layer, ordine `assemble_order` per dominio, ancore IC fissi se già piazzati.
6. **F2 export** — scrivere posizioni proposte in file/plugin (pcbnew) senza muovere rame; `placement_check` resta verifica.
-Output F1: `functional_groups.json` scritto in `graph_build`. Pipeline parallela Placement (`POST …/placement/start`) riscrive anche `placement_plan.json` senza toccare lo `status` di analisi, poi tenta F2 pack. Verifica PCB esistente resta `placement_check` (PS-PLC*) — non confondere con packing.
+Output F1: `functional_groups.json` scritto in `graph_build`. Pipeline parallela Placement (`POST …/placement/start`) riscrive anche `placement_plan.json` senza toccare lo `status` di analisi, poi tenta F2 pack. Verifica PCB esistente resta `placement_check` (PE-PLC*) — non confondere con packing.
---
## 0. Due prodotti (stesso repo, due promesse)
-| | **Pinscope** (oggi + wave A–B, C schema, F/H leggere) | **Pinscope Layout** (wave D parziale, G, C4+G2, plugin pcbnew) |
+| | **Periscope** (oggi + wave A–B, C schema, F/H leggere) | **Periscope Layout** (wave D parziale, G, C4+G2, plugin pcbnew) |
| --- | --- | --- |
| Promessa | Lo schema rispetta il datasheet | Il rame rispetta datasheet + geometria |
| File | BOM, netlist, `.kicad_sch` gerarchico | + `.kicad_pcb` |
| Output | Finding su pin/net, derating, power tree | Distanze mm, 3W, creepage, skew, via EP |
| Utente | Chi chiude lo schema | Chi sbroglia |
-Farli nello stesso codebase (`pinscopex` + `LayoutGraph`) è ragionevole. Farli **nella stessa run obbligatoria** no: senza PCB il progetto deve restare un Pinscope completo, non “incompleto perché manca il gerber”.
+Farli nello stesso codebase (`periscopex` + `LayoutGraph`) è ragionevole. Farli **nella stessa run obbligatoria** no: senza PCB il progetto deve restare un Periscope completo, non “incompleto perché manca il gerber”.
-Nome in UI: tab **Layout** o prodotto “Layout checks” gated dal file `.kicad_pcb`. Il report schema non deve riempirsi di `PS-PLC` se il PCB non c’è.
+Nome in UI: tab **Layout** o prodotto “Layout checks” gated dal file `.kicad_pcb`. Il report schema non deve riempirsi di `PE-PLC` se il PCB non c’è.
Non serve un fork oggi. Serve disciplina: ogni PR dichiara se è Core o Layout.
@@ -76,7 +76,7 @@ Non serve un fork oggi. Serve disciplina: ogni PR dichiara se è Core o Layout.
## 0. Contratto di prodotto (non negoziabile)
-Pinscope oggi è un **validatore di schema**: BOM + netlist → grafo bipartito → check deterministici + review LLM con citazione datasheet. Non legge il PCB.
+Periscope oggi è un **validatore di schema**: BOM + netlist → grafo bipartito → check deterministici + review LLM con citazione datasheet. Non legge il PCB.
Molti punti della lista (larghezza traccia, 3W, creepage, CPW clearance, length matching) **non esistono senza geometria**. Il piano li tiene, ma li mette **dopo** un ingest layout. Se li si forza sullo schema si producono finding inventati.
@@ -118,7 +118,7 @@ Da fare (Wave A1), in ordine:
Regole:
-1. Ogni nuovo check è una funzione pura in `backend/pinscopex/` che legge `DesignGraph` (+ opzionale layout). Niente SDK LLM dentro `pinscopex/`.
+1. Ogni nuovo check è una funzione pura in `backend/periscopex/` che legge `DesignGraph` (+ opzionale layout). Niente SDK LLM dentro `periscopex/`.
2. I finding usano lo stesso schema (`Finding` in `models.py` / `frontend/src/lib/types.ts`). Campo `source` già distingue check automatici vs review.
3. Finding normalization resta **downgrade-only**.
4. Libreria condivisa: MPN exact-match. Niente fuzzy sul die.
@@ -134,14 +134,14 @@ Aggiungere (backward compatible):
| --- | --- |
| `net` | Telemetry CAD, filtri, SI |
| `pins[]` | Pan-and-zoom su U1.4 |
-| `rule_id` | Plugin DRC (`PS-DEC-001`) |
+| `rule_id` | Plugin DRC (`PE-DEC-001`) |
| `cad_sheet` / `cad_uuid` | Sync plugin KiCad |
| `variant` | DNP / ECO |
| `severity_calibrated` | già implicito; non alzare in post |
Passi:
-1. Estendere `Finding` in `backend/pinscopex/models.py` e `frontend/src/lib/types.ts`.
+1. Estendere `Finding` in `backend/periscopex/models.py` e `frontend/src/lib/types.ts`.
2. Aggiornare `assign_finding_ids`, export Excel, report UI (campi opzionali nascosti se null).
3. Test su `simple_project/` che i check esistenti ancora serializzano.
@@ -157,12 +157,12 @@ Passi:
| 2 Datasheet / errata / OCR blocchi | Pintable, excerpt, quote_verify, errata, `internal_features` | Nessun RAG vendor | **OK** |
| 3 Impedenze / stackup | ImpedenceFinder: calcolatrice + **Z0 sulle tracce** dei net signal (stackup PCB) | CPWG non nel vendor | **OK** |
| 4 Filtri | `check_filters` (solo con poli/numeri in specs) | Niente \(f_c\) inventata | **OK** |
-| 5 Capacità PI | Decoupling sulla net; derating V; DC-bias/ESR se c’è il numero; mm sul PCB (`PS-PLC-001`) | — | **OK** |
+| 5 Capacità PI | Decoupling sulla net; derating V; DC-bias/ESR se c’è il numero; mm sul PCB (`PE-PLC-001`) | — | **OK** |
| 6 Elettrico | Pin mux; I2C/reset pull-up; LED; sequencing/IR/power margin se c’è il parametro | Senza numero in specs → skip | **OK** |
| 7 RF | Tab **RF / Impedance**: verify matching + template geometry (IFA/meander/stub → SVG + `.kicad_mod`); Z0 feed se PCB | CPWG / auto-place into `.kicad_pcb` dopo | **Improved** |
| 8 HV / isolation | — | Serve `layout_rules` + V/mm dal datasheet, non IEC inventato | — |
| 9 Termico | `check_thermal` se θJA/I sono in specs; via courtyard vs `min_via_count` | Niente \(T_j\) senza parametro | **OK** |
-| 10 SI / DNP | DNP enable; `PS-SI-001` solo con `length_match` mm | Niente 3W/crosstalk inventati | **OK** |
+| 10 SI / DNP | DNP enable; `PE-SI-001` solo con `length_match` mm | Niente 3W/crosstalk inventati | **OK** |
| 11 Lifecycle | `lifecycle_check` su cache distributore (EOL/NRND/RoHS esplicito) | Niente equivalente LLM | **OK** |
| 13 Placement da datasheet | `layout_rules` + `.kicad_pcb`: PLC-001…004, same_layer, crystal, keepout, path | 3W/creepage/CPW/isolation senza numero | **OK** |
@@ -176,7 +176,7 @@ Senza questi i moduli 1–12 non si misurano e i plugin mentono.
**E1. KiCad gerarchico GA.** Obbligatorio. Vedi “Schema gerarchico” sopra. Senza flatten dei fogli il plugin e il `.kicad_pcb` non allineano i net.
-**E2. Protocollo plugin KiCad** (`pinscope-cad-bridge` JSON). Un file per progetto:
+**E2. Protocollo plugin KiCad** (`periscope-cad-bridge` JSON). Un file per progetto:
```json
{
@@ -184,14 +184,14 @@ Senza questi i moduli 1–12 non si misurano e i plugin mentono.
"project_id": "...",
"findings": [
{
- "rule_id": "PS-MUX-001",
+ "rule_id": "PE-MUX-001",
"ref": "U3",
"pins": ["12"],
"sheet": "...",
"uuid": "...",
"severity": "error",
"message": "...",
- "url": "https://pinscope.../report?finding=U3-001"
+ "url": "https://periscope.../report?finding=U3-001"
}
]
}
@@ -219,7 +219,7 @@ Passi: quelli in “Schema gerarchico (più file)” + file-guide riscritta (nie
Passi:
1. Pacchetto `plugins/kicad/` (action plugin Python, KiCad 9/10).
-2. `pinscope-findings.json` dal report (E2).
+2. `periscope-findings.json` dal report (E2).
3. Marcatori / focus `uuid` su **eeschema** (foglio figlio corretto) e, per finding layout, su **pcbnew**.
4. Pan-and-zoom: `FocusOnItem` / select symbol; se l’API 10 differisce, adapter sottile.
@@ -235,7 +235,7 @@ Passi:
1. Da campi KiCad (`MPN`, `mpn`, `PN`, `lcsc`) già letti in `parsers_kicad.py` / `graph.py`.
2. Tabella conflitti: ref in schema senza MPN, MPN in BOM senza ref, mismatch Value.
-3. Finding `source=bom_match` con `rule_id=PS-BOM-001`.
+3. Finding `source=bom_match` con `rule_id=PE-BOM-001`.
4. UI wizard: riga rossa nel matching, non solo colonne.
**Done when:** U1 in schema e U1 in BOM con MPN diversi → ERROR citabile.
@@ -244,7 +244,7 @@ Passi:
## Wave B — Check deterministici schema (sblocca 4, 5, 6, 9, 10 DNP)
-Obiettivo: meno LLM, più numeri. Ogni item = modulo `pinscopex` + test su grafo sintetico + riga in eval.
+Obiettivo: meno LLM, più numeri. Ogni item = modulo `periscopex` + test su grafo sintetico + riga in eval.
### B1. Power tree & drop (6)
@@ -253,7 +253,7 @@ Passi:
1. Riuse UI power tree esistente.
2. Per ogni IC: somma IQ + load stimato da specs se c’è; confronta con `Iout_max` LDO/Buck se estratto.
3. IR drop **solo se** esiste Rseries esplicito (shunt/ferrite) — niente stima di pista.
-4. Finding `PS-PWR-001` margin fail.
+4. Finding `PE-PWR-001` margin fail.
### B2. Sequencing (6)
@@ -343,7 +343,7 @@ Passi:
1. Non scraping indiscriminato (TOS, HTML instabile).
2. Catalogo URL noti (TI `lit/er`, STM `errata`, Microchip).
3. DeepSeek `web_search` **opzionale** gated, citazione obbligatoria, stesso `quote_verify`.
-4. Finding `PS-ERRATA-001` se il workaround (pull-up, bond-out) non è nello schema.
+4. Finding `PE-ERRATA-001` se il workaround (pull-up, bond-out) non è nello schema.
**Done when:** un MPN con errata nota in fixture produce finding; vendor senza URL → skip silenzioso loggato.
@@ -407,7 +407,7 @@ Motore **standalone**, stile calcolatrice. I vincoli CAD sono export, non verit
Passi:
-1. `pinscopex/impedance.py`: microstrip, stripline, coupled diff, CPW — formule documentate + test numerici vs 3 valori ImpedanceFinder.
+1. `periscopex/impedance.py`: microstrip, stripline, coupled diff, CPW — formule documentate + test numerici vs 3 valori ImpedanceFinder.
2. Input: `h`, `er`, `t`, `w`, `s`, `target_z`.
3. UI tab progetto “Impedance” (non LLM).
@@ -461,7 +461,7 @@ Passi:
Passi:
-1. Length matching / intra-pair skew vs limite datasheet (USB/HDMI/PCIe). **OK** (`PS-SI-001`, solo `length_match` mm).
+1. Length matching / intra-pair skew vs limite datasheet (USB/HDMI/PCIe). **OK** (`PE-SI-001`, solo `length_match` mm).
2. 3W: distanza centro-centro vs W aggressore. — skip senza numero (non IEC/USB folklore).
3. Creepage/clearance: profilo IEC 62368 (pollution, RMS V dai net). — skip senza V/mm nel datasheet.
4. Isolation barrier: bbox isolator + divieto piste LV nel courtyard HV. — skip senza regola.
@@ -477,17 +477,17 @@ Passi:
1. Per ogni pin alimentazione del pintable: footprint pad xy sul `.kicad_pcb`; condensatori sul medesimo net (grafo); distanza euclidea pad-cap (pin cap verso GND/VDD). **OK**
2. Se `max_distance_mm` estratto: ERROR/WARNING se oltre. Se assente: skip (niente default 3 mm). **OK**
-3. Via in pad / via sotto EP: contare via nel courtyard del thermal pad vs `min_via_count`. **OK** (`PS-PLC-002`)
-4. Stesso layer: se `same_layer: true` e il cap è sull’altro lato senza via sotto il pin → WARNING. **OK** (`PS-PLC-003`)
+3. Via in pad / via sotto EP: contare via nel courtyard del thermal pad vs `min_via_count`. **OK** (`PE-PLC-002`)
+4. Stesso layer: se `same_layer: true` e il cap è sull’altro lato senza via sotto il pin → WARNING. **OK** (`PE-PLC-003`)
5. Piste: lunghezza net dal pin al cap = shortest path sui segmenti vs `max_distance_mm`. **OK**
6. Crystal: cap load vs pin XIN/XOUT (stessa metrica). **OK** (`X1`/`C9`/`C10`)
-7. Finding `PS-PLC-001`…`004` con `pins`, `net`; plugin focus pcbnew. **OK** (keepout = `PS-PLC-004`)
+7. Finding `PE-PLC-001`…`004` con `pins`, `net`; plugin focus pcbnew. **OK** (keepout = `PE-PLC-004`)
Non confrontare una foto del layout TI con il board pixel-a-pixel. Solo vincoli numerici/topologici.
-**Done when:** fixture PCB con C di decoupling a 15 mm da VDD (regola 2 mm) → `PS-PLC-001`; cap a 1 mm → niente finding.
+**Done when:** fixture PCB con C di decoupling a 15 mm da VDD (regola 2 mm) → `PE-PLC-001`; cap a 1 mm → niente finding.
-**Done when (G1+G2):** un `.kicad_pcb` di test (USB diff pair volutamente sbagliata) produce `PS-SI-001`. I net coincidono con lo schema gerarchico della stessa repo.
+**Done when (G1+G2):** un `.kicad_pcb` di test (USB diff pair volutamente sbagliata) produce `PE-SI-001`. I net coincidono con lo schema gerarchico della stessa repo.
---
@@ -529,7 +529,7 @@ Stima onesta: Wave A–B (schema) sono il ritorno; G è un secondo prodotto. Non
## Passi operativi per **ogni** check nuovo
1. Fixture grafo minimo in `tests/test_.py` (non solo `simple_project`).
-2. Funzione in `pinscopex/` senza I/O.
+2. Funzione in `periscopex/` senza I/O.
3. Registrare in `services/validation.py` accanto a pin_mux/LED.
4. `rule_id` + `source`.
5. Una riga changelog.
@@ -557,6 +557,6 @@ Resta, senza inventare numeri:
1. HV / isolation (blocco 8) se il datasheet dà V/mm o keepout HV.
2. 3W / creepage / CPW solo con numero in `layout_rules` o dal calcolatore D2.
-3. Un `.kicad_pcb` reale di progetto (non fixture USB inventata) per vedere `PS-PLC`/`PS-SI` sul board.
+3. Un `.kicad_pcb` reale di progetto (non fixture USB inventata) per vedere `PE-PLC`/`PE-SI` sul board.
Il plugin KiCad aspetta ancora verifica uuid + sheet su un progetto multi-foglio vero.
diff --git a/frontend/CLAUDE.md b/frontend/CLAUDE.md
index 8432bc6..838ffc0 100644
--- a/frontend/CLAUDE.md
+++ b/frontend/CLAUDE.md
@@ -1,8 +1,8 @@
@AGENTS.md
-# Pinscope Frontend
+# Periscope Frontend
-Next.js 16 app (App Router, Turbopack) providing a web UI for Pinscope schematic validation. Talks to the FastAPI backend at `localhost:8000`.
+Next.js 16 app (App Router, Turbopack) providing a web UI for Periscope schematic validation. Talks to the FastAPI backend at `localhost:8000`.
## Architecture
@@ -38,7 +38,7 @@ credit state only through `useCredits()` — both are inert in this repo.
| Path | Purpose |
|---|---|
-| `src/lib/types.ts` | TS types mirroring `pinscopex/models.py` |
+| `src/lib/types.ts` | TS types mirroring `periscopex/models.py` |
| `src/lib/api.ts` | All data fetching — single integration point with backend |
| `src/lib/mock-data.ts` | Pipeline step definitions for progress UI |
| `src/components/report/` | Report viewer components + power tree React Flow graph + derating table + finding comments |
@@ -97,6 +97,6 @@ Requires the backend running at `localhost:8000` (or set `NEXT_PUBLIC_API_URL`).
- Keep all data fetching in `src/lib/api.ts` — don't scatter fetch calls across components
- Report filters persist in URL search params (`?status=ERROR&component=U3&q=decoupling`)
-- When modifying types, keep `src/lib/types.ts` in sync with `backend/pinscopex/models.py`
+- When modifying types, keep `src/lib/types.ts` in sync with `backend/periscopex/models.py`
- Use `font-mono` for technical values: designators (U1), MPNs, pin names, component values
- Status colors: emerald = PASS, amber = WARNING, rose = ERROR, blue = accent/active
diff --git a/frontend/content/changelog.md b/frontend/content/changelog.md
index 4fe93ab..1a4b1ca 100644
--- a/frontend/content/changelog.md
+++ b/frontend/content/changelog.md
@@ -1,6 +1,14 @@
# Changelog
-What's new in Pinscope.
+What's new in Periscope.
+
+## 2.29.0 — 2026-09-13 — Rebrand to Periscope
+
+Product name, package (`periscopex`), Docker containers, deploy script, and default site URL are now **Periscope** (`https://periscope.michelebigi.it`). Deterministic finding IDs use the `PE-*` prefix. Existing project.json `pinscope_version` and browser storage keys are still read.
+
+- [Changed] Brand Pinscope → Periscope across UI, docs, and APIs.
+- [Changed] `backend/pinscopex` → `backend/periscopex`; rule IDs `PS-*` → `PE-*`.
+- [Changed] Default host / update script → `periscope.michelebigi.it` / `update-periscope.sh`.
## 2.28.12 — 2026-09-13 — Antenna templates: IFA / meander / stub + .kicad_mod
@@ -99,7 +107,7 @@ Dedicated Placement job builds the routing-first topology plan without touching
Routing-first floorplan foundation without inventing millimetres: domains and satellite role hints after graph build, plus deterministic crystal CL and NC-pin checks.
- [New] `functional_groups.json` from `graph_build` (domains, IC groups, `role_hint`, attached `layout_rules`).
-- [New] `crystal_cl_check` (PS-XTAL-*) and `nc_pin_check` (PS-NC-001) in the deterministic suite.
+- [New] `crystal_cl_check` (PS-XTAL-*) and `nc_pin_check` (PE-NC-001) in the deterministic suite.
- [Docs] Piano §0c Placement F1/F2; packing mm stays Layout F2.
## 2.27.1 — 2026-09-12 — DeepSeek roadmap integrations
@@ -113,7 +121,7 @@ Close the open P0/P2 items from the growth plan: offline smoke on `simple_projec
## 2.27.0 — 2026-09-11 — Local multi-user auth
-Self-host Pinscope accounts (email + password) so several people can share a project. Invite collaborators by email from the project page — same flow as cloud, without Clerk.
+Self-host Periscope accounts (email + password) so several people can share a project. Invite collaborators by email from the project page — same flow as cloud, without Clerk.
- [New] `AUTH_JWT_SECRET` enables register/login; first user is admin and inherits `users/local` projects.
- [New] `/sign-in` and `/sign-up`; sidebar account menu. Set `NEXT_PUBLIC_AUTH_MODE=local` on the frontend build.
@@ -166,38 +174,38 @@ A pipeline run with `.kicad_pcb` + stackup samples routed **signal** nets (power
`layout_rules` `keepout` flags a foreign net whose track endpoint is inside the KiCad courtyard. Own net and missing courtyard skip. No invented analog/digital classes.
-- [New] `PS-PLC-004` WARNING. Tests use `X1` / `/HFXIN` / `GND` from `simple_project`.
+- [New] `PE-PLC-004` WARNING. Tests use `X1` / `/HFXIN` / `GND` from `simple_project`.
## 2.24.0 — 2026-09-10 — Crystal load caps and track path
Load caps on XIN/XOUT use the same `max_distance_mm` as decoupling. If the PCB has segments on the net, the limit is shortest-path length, not a guessed “loop is too big” ratio.
-- [New] Crystals (`X1` / C9 / C10 on `simple_project`) run `PS-PLC-001` when `layout_rules` has millimetres.
+- [New] Crystals (`X1` / C9 / C10 on `simple_project`) run `PE-PLC-001` when `layout_rules` has millimetres.
- [New] Detour tracks: path along segments vs the same `max_distance_mm`. No segments → euclidean pad distance.
## 2.23.0 — 2026-09-10 — same_layer decoupling
If `layout_rules` sets `same_layer: true`, a decoupling cap on the opposite copper from the IC is a WARNING. A via inside the courtyard (calculated) is enough. Unset flag → skip.
-- [New] `PS-PLC-003` WARNING when every placed cap on the net is on F vs B opposite the IC.
+- [New] `PE-PLC-003` WARNING when every placed cap on the net is on F vs B opposite the IC.
## 2.22.0 — 2026-09-10 — Thermal vias vs min_via_count
Via count is calculated inside the KiCad courtyard. The limit is the `min_via_count` parameter from `layout_rules`. No courtyard or no count → skip. No pad radius default.
-- [New] `PS-PLC-002` when vias in courtyard < `min_via_count`. `simple_project` has no PCB so it stays silent.
+- [New] `PE-PLC-002` when vias in courtyard < `min_via_count`. `simple_project` has no PCB so it stays silent.
## 2.21.0 — 2026-09-10 — Layout SI skew (datasheet mm only)
Intra-pair skew is measured on the PCB only when `layout_rules` `length_match` has a number. 3W, creepage, and CPWG are not guessed.
-- [New] `PS-SI-001` ERROR when a named pair (_DP/_DM, _P/_N) exceeds that millimetre. No mm in the datasheet → skip.
+- [New] `PE-SI-001` ERROR when a named pair (_DP/_DM, _P/_N) exceeds that millimetre. No mm in the datasheet → skip.
## 2.20.0 — 2026-09-10 — Placement vs datasheet (PCB)
-Decoupling distance is measured on the `.kicad_pcb` against `layout_rules`. No board → no `PS-PLC-001`. A null millimetre skips — no 3 mm default.
+Decoupling distance is measured on the `.kicad_pcb` against `layout_rules`. No board → no `PE-PLC-001`. A null millimetre skips — no 3 mm default.
-- [New] `PS-PLC-001` when a decoupling cap is farther than datasheet `max_distance_mm`. Empty `layout_rules` and missing caps skip.
+- [New] `PE-PLC-001` when a decoupling cap is farther than datasheet `max_distance_mm`. Empty `layout_rules` and missing caps skip.
## 2.19.0 — 2026-09-10 — Finding review and ECO
@@ -219,14 +227,14 @@ The Impedance tab uses the closed-form engine from ImpedenceFinder (Hammerstad
Distributor lifecycle is a cached check, not a review scrape. Errata and layout_rules stay structured and skip when the catalog or the PDF has no number.
-- [New] `PS-LF-001` EOL, `PS-LF-002` NRND, `PS-LF-003` explicit RoHS fail. Replacement only if the distributor lists it. Active / RoHS N/A / missing cache row are silent.
-- [New] `PS-ERRATA-001` when a catalogued workaround pull-up is missing. No URL → skip.
-- [New] `PS-INT-001` when `internal_features.pullup_pins` has no rail resistor.
+- [New] `PE-LF-001` EOL, `PE-LF-002` NRND, `PE-LF-003` explicit RoHS fail. Replacement only if the distributor lists it. Active / RoHS N/A / missing cache row are silent.
+- [New] `PE-ERRATA-001` when a catalogued workaround pull-up is missing. No URL → skip.
+- [New] `PE-INT-001` when `internal_features.pullup_pins` has no rail resistor.
- [New] `layout_rules` closed kinds; non-numeric `max_distance_mm` is stored as null.
## 2.16.0 — 2026-09-10 — KiCad cad-bridge
-Pinscope writes `pinscope-findings.json` next to the report so a KiCad 9/10 action plugin can pan to the symbol uuid on the right sheet.
+Periscope writes `periscope-findings.json` next to the report so a KiCad 9/10 action plugin can pan to the symbol uuid on the right sheet.
- [New] E2 cad-bridge JSON (`version`, `ref`, `pins`, `sheet`, `uuid`, `severity`). Layout rule ids target pcbnew; others target eeschema.
- [New] `.kicad_sch` symbols keep `cad_uuid` + `cad_sheet` in `cad_index` (child sheet, not the empty root).
@@ -236,17 +244,17 @@ Pinscope writes `pinscope-findings.json` next to the report so a KiCad 9/10 acti
Schema checks now compare regulator load to Iout_max, look at PG→EN when a sequence is declared, and treat DNP as a fitted-variant graph.
-- [New] `PS-PWR-001` when specified IQ+I_load exceeds Iout_max, or an explicit series R/ferrite DCR drops >5% of the rail. Missing IQ and PCB traces are not guessed.
-- [New] `PS-SEQ-001` WARNING if `power_sequence` is in IC specs and upstream PG does not net to downstream EN.
-- [New] BOM `DNP`/`Fitted`/`Variant` on `bom_fields`. Fitted enable with only a DNP pull is `PS-DNP-001` ERROR; no DNP column skips the check.
+- [New] `PE-PWR-001` when specified IQ+I_load exceeds Iout_max, or an explicit series R/ferrite DCR drops >5% of the rail. Missing IQ and PCB traces are not guessed.
+- [New] `PE-SEQ-001` WARNING if `power_sequence` is in IC specs and upstream PG does not net to downstream EN.
+- [New] BOM `DNP`/`Fitted`/`Variant` on `bom_fields`. Fitted enable with only a DNP pull is `PE-DNP-001` ERROR; no DNP column skips the check.
## 2.14.0 — 2026-09-10 — Filtri e termico schema
Deterministic checks now match RC/LC/π/T filters and estimate LDO/resistor dissipation without inventing missing numbers.
-- [New] Filter topology `PS-FLT-001` (fc INFO) / `PS-FLT-002` vs `adc_sample_rate` only when that spec exists. Ferrite DCR `PS-FLT-003` only with a datasheet limit.
-- [New] LDO `P = I_load×(Vin−Vout)` and `Tj = 25 + P·θJA`. Missing θJA is `PS-TH-001` INFO. `Iout_max` is not treated as load.
-- [New] Resistor `I²R` vs `power_rating_w` on LED paths and shunts with known ΔV (`PS-TH-003`).
+- [New] Filter topology `PE-FLT-001` (fc INFO) / `PE-FLT-002` vs `adc_sample_rate` only when that spec exists. Ferrite DCR `PE-FLT-003` only with a datasheet limit.
+- [New] LDO `P = I_load×(Vin−Vout)` and `Tj = 25 + P·θJA`. Missing θJA is `PE-TH-001` INFO. `Iout_max` is not treated as load.
+- [New] Resistor `I²R` vs `power_rating_w` on LED paths and shunts with known ΔV (`PE-TH-003`).
## 2.13.0 — 2026-09-10 — DC-bias C_eff stima
@@ -254,20 +262,20 @@ The derating table now shows an effective capacitance under DC bias for C0G/X7R/
- [New] `C_eff` column from an empirical V/Vrated table. Tantalum/electrolytic and unknown dielectrics are left blank.
- [Improved] C0G/NP0 stays at nominal C; X7R at 50% of rated V is about 70% of C.
-- [New] Bulk C without a ~100 nF ceramic is `PS-ESR-001` INFO (no invented Z(f) target).
+- [New] Bulk C without a ~100 nF ceramic is `PE-ESR-001` INFO (no invented Z(f) target).
## 2.12.0 — 2026-09-10 — Pull-up sizing and LDO Cout
Deterministic schema checks now size I2C pull-ups, flag NRST pull-downs, and look at LDO VOUT capacitance — still WARNING, never a invented datasheet µF ERROR.
-- [New] I2C pull-up value vs a wide NXP UM10204 band (`PS-I2C-002`). 4.7 kΩ is in-band; missing values are not sized.
-- [New] Active-low reset with a resistor to ground is `PS-RST-002`.
-- [New] Regulator VOUT needs Cout (`PS-DEC-001`); 100 nF-only on VOUT is `PS-DEC-002`. MCU VDD 100 nF is not flagged.
+- [New] I2C pull-up value vs a wide NXP UM10204 band (`PE-I2C-002`). 4.7 kΩ is in-band; missing values are not sized.
+- [New] Active-low reset with a resistor to ground is `PE-RST-002`.
+- [New] Regulator VOUT needs Cout (`PE-DEC-001`); 100 nF-only on VOUT is `PE-DEC-002`. MCU VDD 100 nF is not flagged.
- [Improved] Pin-mux UART0 on the `simple_project` MSPM0 nets is covered in tests (SPI PICO/POCI already was).
## 2.11.0 — 2026-09-10 — DeepSeek V4.1 and re-analyze
-Pinscope now defaults to DeepSeek-V4.1-Flash (`deepseek-flash`) for every LLM stage, shows API cost in dollars, and lets you replace the BOM and netlist on an existing project without deleting it.
+Periscope now defaults to DeepSeek-V4.1-Flash (`deepseek-flash`) for every LLM stage, shows API cost in dollars, and lets you replace the BOM and netlist on an existing project without deleting it.
- [New] Default model is `deepseek-flash` (native vision). Legacy `deepseek-v4-flash` / `deepseek-v4-flash-vision-exp` names still work; they route to V4.1.
- [New] Replace BOM & netlist on a finished project and re-run the analysis. History, library cache, and prior spend stay on the same project.
@@ -291,7 +299,7 @@ Chips and passives stay in a shared library after the first look, so the next bo
## 2.8.0 — 2026-08-27 — Automatic datasheets
-Pinscope now finds datasheet PDFs on its own. You can still upload a file, but you no longer need DigiKey keys for the common case.
+Periscope now finds datasheet PDFs on its own. You can still upload a file, but you no longer need DigiKey keys for the common case.
- [New] Auto-fetch from LCSC (no API key) by manufacturer part number or LCSC code, with exact-MPN matching so a CH340E never silently becomes a CH340G.
- [New] Direct Texas Instruments datasheet URLs (`ti.com/lit/ds/symlink/…`) as a second source for TI parts.
@@ -300,7 +308,7 @@ Pinscope now finds datasheet PDFs on its own. You can still upload a file, but y
## 2.7.0 — 2026-08-27 — DeepSeek API
-Pinscope now talks to DeepSeek by default. Extraction skills run locally; datasheet PDFs are converted to text (and page images on the vision model) because DeepSeek does not accept native PDF documents.
+Periscope now talks to DeepSeek by default. Extraction skills run locally; datasheet PDFs are converted to text (and page images on the vision model) because DeepSeek does not accept native PDF documents.
- [New] DeepSeek provider (`deepseek-v4-flash`, `deepseek-v4-pro`, `deepseek-v4-flash-vision-exp`) via the OpenAI-compatible Chat Completions API.
- [New] Local skill runner: `skills/*/SKILL.md` is inlined and `validate.py` runs in-process — no Anthropic Console upload required.
@@ -309,7 +317,7 @@ Pinscope now talks to DeepSeek by default. Extraction skills run locally; datash
## 2.6.0 — 2026-07-12 — Export Report to Excel
-Download a project's findings as an Excel spreadsheet straight from the report — one click, ready to share, filter, or archive outside Pinscope.
+Download a project's findings as an Excel spreadsheet straight from the report — one click, ready to share, filter, or archive outside Periscope.
- [New] "Export Excel" button on the validation report. Every finding becomes a spreadsheet row — designator, part number, ID, severity, title, description, recommendation, and its datasheet source (page included) — sorted most-severe first.
@@ -321,7 +329,7 @@ Schematic review now works through every functional area of a component before f
## 2.5.0 — 2026-07-02 — Light Mode
-Pinscope now has a light theme. Toggle between light and dark with the sun/moon button — in the sidebar next to your account menu, or in the header on the website.
+Periscope now has a light theme. Toggle between light and dark with the sun/moon button — in the sidebar next to your account menu, or in the header on the website.
- [New] Theme toggle. Switch between light and dark mode anywhere in the app; your choice is remembered on this device. Everything defaults to dark, exactly as before, until you flip it.
- [Improved] Every status color — error, warning, and pass badges, finding cards, the progress view, billing — is tuned for both themes, so reports stay legible either way.
@@ -331,14 +339,14 @@ Pinscope now has a light theme. Toggle between light and dark with the sun/moon
Two datasheet-grounded checks now run on every project, independent of the schematic review — catching a swapped-peripheral pin or an over-driven LED — plus a clear list of any components that had no datasheet to review against.
-- [New] Pin-function feasibility check. Pinscope now flags when a net assigns an IC pin a peripheral function its silicon can't route — for example a `UART5_TX` net on a pin whose alternate-function table only offers `UART5_RX`. It's reported as an error straight from the datasheet's pin table and names the likely swap (TX↔RX, SDA↔SCL). It deliberately does not judge signal *direction* across an interface — a direct UART crosses TX↔RX while a transceiver runs straight through — so it only fires on physically impossible pin assignments, never on wiring style.
-- [New] LED forward-current check. For each LED, Pinscope computes the forward current from the supply rail, the series resistor, and the LED's rated forward voltage, and flags any channel whose current exceeds the LED's rated maximum. Each color of an RGB LED is checked separately, and a leg with no current-limiting resistor at all is called out as a caution.
+- [New] Pin-function feasibility check. Periscope now flags when a net assigns an IC pin a peripheral function its silicon can't route — for example a `UART5_TX` net on a pin whose alternate-function table only offers `UART5_RX`. It's reported as an error straight from the datasheet's pin table and names the likely swap (TX↔RX, SDA↔SCL). It deliberately does not judge signal *direction* across an interface — a direct UART crosses TX↔RX while a transceiver runs straight through — so it only fires on physically impossible pin assignments, never on wiring style.
+- [New] LED forward-current check. For each LED, Periscope computes the forward current from the supply rail, the series resistor, and the LED's rated forward voltage, and flags any channel whose current exceeds the LED's rated maximum. Each color of an RGB LED is checked separately, and a leg with no current-limiting resistor at all is called out as a caution.
- [New] "Not reviewed" list on the report. Components with no datasheet on file — for instance a do-not-populate footprint that isn't in the BOM — are now called out explicitly, so a mis-wired pin on an unreviewed part shows up as a known gap instead of being silently absent.
- [New] Findings from these automatic checks carry an "Automated check" badge, so they're easy to tell apart from datasheet-review findings.
## 2.3.3 — 2026-06-08 — Faster Reviews
-Multi-chip designs now review several times faster — Pinscope works through ICs in parallel instead of one at a time.
+Multi-chip designs now review several times faster — Periscope works through ICs in parallel instead of one at a time.
- [Improved] Datasheet extraction and schematic review now process multiple ICs at once, so reports on multi-IC projects come back substantially faster. The findings are unchanged — only the wait is shorter.
@@ -364,7 +372,7 @@ EDIF 2.0.0 netlists upload alongside PADS-PCB, with a sub-design picker for file
## 2.3.0 — 2026-05-24 — LCSC Part Number Support
-JLCPCB-style BOMs with LCSC part numbers (e.g. `C12044`) now work out of the box — Pinscope auto-detects the column, resolves each id to the real manufacturer part number, and shows you what it resolved to before the pipeline runs.
+JLCPCB-style BOMs with LCSC part numbers (e.g. `C12044`) now work out of the box — Periscope auto-detects the column, resolves each id to the real manufacturer part number, and shows you what it resolved to before the pipeline runs.
- [New] LCSC part numbers in the manufacturer part number column are auto-detected at BOM upload and converted to real MPNs. Works with JLCPCB / EasyEDA exports without any column renaming.
- [New] Project setup now shows the LCSC → MPN mapping on each IC row in the datasheet step (e.g. `C12044 → TP4057-42-SOT26-R`), so you can see what each LCSC id became before the pipeline starts.
@@ -378,7 +386,7 @@ Tabbed file upload guide with per-tool instructions, Xpedition coverage, and dir
- [New] Documentation for exporting a PADS-PCB netlist from Siemens Xpedition Designer / DxDesigner (VX.2.x, including VX.2.14).
- [Improved] File upload guide reorganized into tabs — KiCad, Altium, OrCAD/Allegro, Xpedition, EasyEDA, and Eagle each get their own panel.
- [Improved] Netlist uploads now accept `.asc`, `.net`, `.NET`, and `.txt` directly — no more renaming required before upload.
-- [Improved] File guide now calls out the difference between the PADS-PCB schematic netlist Pinscope needs and the `!PADS-POWERPCB` PCB-layout dump that some EDA tools also save as `.asc`.
+- [Improved] File guide now calls out the difference between the PADS-PCB schematic netlist Periscope needs and the `!PADS-POWERPCB` PCB-layout dump that some EDA tools also save as `.asc`.
## 2.2.0 — 2026-05-20 — Cross-chip Datasheet Review
diff --git a/frontend/content/file-guide.md b/frontend/content/file-guide.md
index 478995e..a6cce51 100644
--- a/frontend/content/file-guide.md
+++ b/frontend/content/file-guide.md
@@ -1,29 +1,29 @@
# File Upload Guide
-Pinscope needs two files from your EDA tool to review a schematic, and an optional KiCad board for layout:
+Periscope needs two files from your EDA tool to review a schematic, and an optional KiCad board for layout:
-- A **PADS-PCB ASCII netlist** or an **EDIF 2.0.0 netlist** — the circuit's connectivity. Pinscope accepts `.asc`, `.net`, `.NET`, `.txt` (PADS-PCB) and `.edn`, `.edif`, `.edf` (EDIF); the format is auto-detected from the file's first bytes.
+- A **PADS-PCB ASCII netlist** or an **EDIF 2.0.0 netlist** — the circuit's connectivity. Periscope accepts `.asc`, `.net`, `.NET`, `.txt` (PADS-PCB) and `.edn`, `.edif`, `.edf` (EDIF); the format is auto-detected from the file's first bytes.
- A **Bill of Materials** (CSV or XLSX) — mapping each reference designator to a manufacturer part number.
- Optional: a **KiCad PCB** (`.kicad_pcb`) — placement, keepout, pair length, and net Z0. Schematic review still runs without it.
## Example files
-New to Pinscope? Here's a complete set of files from [Phil's Lab](https://www.youtube.com/@PhilsLab)' KiCad 9 TI MSPM0 tutorial you can download and upload as a starter project:
+New to Periscope? Here's a complete set of files from [Phil's Lab](https://www.youtube.com/@PhilsLab)' KiCad 9 TI MSPM0 tutorial you can download and upload as a starter project:
- [TI-MSP-KICAD9-TUTORIAL.asc](/examples/TI-MSP-KICAD9-TUTORIAL.asc) — netlist
- [TI-MSP-KICAD9-TUTORIAL.csv](/examples/TI-MSP-KICAD9-TUTORIAL.csv) — BOM
-- [TI-MSP-KICAD9-TUTORIAL-SCHEMATIC.pdf](/examples/TI-MSP-KICAD9-TUTORIAL-SCHEMATIC.pdf) — schematic (for your reference; Pinscope doesn't need this)
+- [TI-MSP-KICAD9-TUTORIAL-SCHEMATIC.pdf](/examples/TI-MSP-KICAD9-TUTORIAL-SCHEMATIC.pdf) — schematic (for your reference; Periscope doesn't need this)
-The BOM below shows the shape Pinscope is looking for.
+The BOM below shows the shape Periscope is looking for.
## The BOM
-Pinscope auto-detects BOM columns by header name. After upload you'll confirm which column holds designators and which holds part numbers, so the exact header names don't matter — only that these columns exist.
+Periscope auto-detects BOM columns by header name. After upload you'll confirm which column holds designators and which holds part numbers, so the exact header names don't matter — only that these columns exist.
**Required**
-- **Designator / Reference** — one row per part, or grouped references like `C1,C2,C5` in a single row (Pinscope expands these automatically).
-- **Manufacturer Part Number (MPN)** — the full orderable part number. Pinscope uses this to look up datasheets, so `10uF 0805` on its own is **not** enough — it needs e.g. `GRM21BR61C106KE15L`.
+- **Designator / Reference** — one row per part, or grouped references like `C1,C2,C5` in a single row (Periscope expands these automatically).
+- **Manufacturer Part Number (MPN)** — the full orderable part number. Periscope uses this to look up datasheets, so `10uF 0805` on its own is **not** enough — it needs e.g. `GRM21BR61C106KE15L`.
**Recommended**
@@ -34,7 +34,7 @@ CSV and XLSX both work. For XLSX, the first worksheet is used.
## The Netlist
-Pinscope accepts either a **PADS-PCB ASCII netlist** or an **EDIF 2.0.0 netlist** — the upload form auto-detects which one you sent based on the file's first bytes, so you don't have to pick a format.
+Periscope accepts either a **PADS-PCB ASCII netlist** or an **EDIF 2.0.0 netlist** — the upload form auto-detects which one you sent based on the file's first bytes, so you don't have to pick a format.
### PADS-PCB ASCII
@@ -55,18 +55,18 @@ U1.24 C1.1
*END*
```
-If your file starts with `*PADS-PCB*` and ends with `*END*`, you're good. Reference designators may contain spaces (e.g. `CV GND`) — Pinscope resolves them against your BOM.
+If your file starts with `*PADS-PCB*` and ends with `*END*`, you're good. Reference designators may contain spaces (e.g. `CV GND`) — Periscope resolves them against your BOM.
**Heads up — two different `.asc` files exist.** PADS (and tools that interop with PADS, like Xpedition) use the `.asc` extension for two unrelated things:
-- The **schematic-exported netlist** starts with `*PADS-PCB*` and lists `*PART*` / `*NET*` sections. **This is what Pinscope wants.**
-- The **full PCB layout dump** starts with `!PADS-POWERPCB-V…` and contains routing/footprint geometry. Pinscope cannot parse this.
+- The **schematic-exported netlist** starts with `*PADS-PCB*` and lists `*PART*` / `*NET*` sections. **This is what Periscope wants.**
+- The **full PCB layout dump** starts with `!PADS-POWERPCB-V…` and contains routing/footprint geometry. Periscope cannot parse this.
If your upload errors with "No components found", check the first line of the file.
### EDIF 2.0.0
-EDIF is a vendor-neutral s-expression format. The first form is `(edif …`, with an `(edifVersion 2 0 0)` declaration near the top, libraries that define each cell's pin list, and a design library that lists `(instance …)` and `(net …)` forms. Pinscope has been verified against **Siemens xDX Designer / DxDesigner** exports; other EDIF 2.0.0 exporters (OrCAD, Altium, KiCad, Eagle) follow the same grammar and should work, but haven't been broadly tested. If your EDIF file doesn't parse, [contact us](/contact) and send a snippet — most fixes are small.
+EDIF is a vendor-neutral s-expression format. The first form is `(edif …`, with an `(edifVersion 2 0 0)` declaration near the top, libraries that define each cell's pin list, and a design library that lists `(instance …)` and `(net …)` forms. Periscope has been verified against **Siemens xDX Designer / DxDesigner** exports; other EDIF 2.0.0 exporters (OrCAD, Altium, KiCad, Eagle) follow the same grammar and should work, but haven't been broadly tested. If your EDIF file doesn't parse, [contact us](/contact) and send a snippet — most fixes are small.
If your tool exports both PADS-PCB and EDIF, PADS-PCB is the path more users have validated; use EDIF when it's the only option.
@@ -127,17 +127,17 @@ Works in **Xpedition Designer / DxDesigner** (VX.2.x, including VX.2.14). Xpedit
3. **Tools → PCB Interface** → choose the **PADS** template (`pads2007.cfg` or equivalent).
4. Run the export. The output's first line should be `*PADS-PCB*` with `*PART*` and `*NET*` sections below. The file extension (`.txt`, `.net`, or `.asc`) doesn't matter — upload it as-is.
-If your project is locked to the integrated Xpedition flow, **export EDIF instead** — DxDesigner's EDIF Exporter runs against any project type and Pinscope accepts the resulting `.edn` as an equivalent input. See **Netlist (EDIF alternative)** below.
+If your project is locked to the integrated Xpedition flow, **export EDIF instead** — DxDesigner's EDIF Exporter runs against any project type and Periscope accepts the resulting `.edn` as an equivalent input. See **Netlist (EDIF alternative)** below.
**Netlist (EDIF alternative)**
1. Open the schematic in Xpedition Designer / DxDesigner.
2. **File → Export → EDIF…** (older builds: **Tools → Run Tool → Edif Exporter**).
3. In the export dialog:
- - **EDIF version**: **2.0.0** (Pinscope only supports 2.0.0)
+ - **EDIF version**: **2.0.0** (Periscope only supports 2.0.0)
- **EDIF level**: **0** (the default)
- **Output format**: **Netlist view** — make sure cells, instances, nets, and the `viewMap` back-annotation block are all included
- - **Designator source**: include back-annotated designators (otherwise instances export as templates like `U?` / `R?` and Pinscope drops them)
+ - **Designator source**: include back-annotated designators (otherwise instances export as templates like `U?` / `R?` and Periscope drops them)
4. Save as `.edn` and upload it as the netlist. The file should start with `(edif …)` and contain `(edifVersion 2 0 0)` near the top.
If neither PADS nor EDIF works for your project setup, send us the BOM and schematic PDF via [Contact](/contact) — we can usually help unblock the export.
@@ -178,10 +178,10 @@ For both EasyEDA Standard and EasyEDA Pro.
- **"No components found"** — your netlist is missing the `*PART*` section. Re-export specifically in PADS-PCB format (not Spice, Protel, or a generic text netlist). If the first line is `!PADS-POWERPCB-V…`, you uploaded the PCB layout dump instead of the schematic netlist — re-export from the schematic side.
- **"No ground net found"** — your netlist has no net named `GND`, `VSS`, `AGND`, `DGND`, or similar. If you exported a sub-sheet, re-export the top sheet instead.
-- **Unresolved parts after the pipeline runs** — a BOM row has no MPN, or the MPN wasn't found on DigiKey. Add the MPN, or rely on Pinscope's value fallback (fills in from the `Value` / `Comment` column).
+- **Unresolved parts after the pipeline runs** — a BOM row has no MPN, or the MPN wasn't found on DigiKey. Add the MPN, or rely on Periscope's value fallback (fills in from the `Value` / `Comment` column).
## The KiCad PCB (optional)
-Drop the KiCad **project folder** (or a zip of it) on Schematic — Pinscope takes the sheets and the `.kicad_pcb` if it is there. You can also add the board later.
+Drop the KiCad **project folder** (or a zip of it) on Schematic — Periscope takes the sheets and the `.kicad_pcb` if it is there. You can also add the board later.
Still stuck? [Contact us](/contact) with your netlist and BOM attached and we'll take a look.
diff --git a/frontend/content/privacy.md b/frontend/content/privacy.md
index a93c051..817f2f8 100644
--- a/frontend/content/privacy.md
+++ b/frontend/content/privacy.md
@@ -1,8 +1,8 @@
-# Pinscope Privacy Policy
+# Periscope Privacy Policy
**Last updated: April 5, 2026**
-This Privacy Policy explains how Faradworks, Inc. ("Faradworks," "we," "us," and "our") collects, uses, and discloses information in connection with the Pinscope website (pinscope.ai), platform, and related services (the "Service").
+This Privacy Policy explains how Faradworks, Inc. ("Faradworks," "we," "us," and "our") collects, uses, and discloses information in connection with the Periscope website (periscope.michelebigi.it), platform, and related services (the "Service").
This Privacy Policy is intended for free users and self-serve paid users. Enterprise customers typically use the Service under a separate agreement and (if applicable) a data processing agreement ("DPA"), which may include additional privacy and security terms.
diff --git a/frontend/content/terms.md b/frontend/content/terms.md
index bb742c3..a8fad1c 100644
--- a/frontend/content/terms.md
+++ b/frontend/content/terms.md
@@ -1,8 +1,8 @@
-# Pinscope Terms of Service
+# Periscope Terms of Service
**Last updated: April 5, 2026**
-These Terms of Service ("Terms") govern access to and use of the Pinscope platform (pinscope.ai) and related services (the "Service"). These Terms apply to free users and self-serve paid users. If you have a separate written agreement signed by Faradworks, Inc. (for example, an enterprise agreement), that agreement governs your use of the Service to the extent it conflicts with these Terms.
+These Terms of Service ("Terms") govern access to and use of the Periscope platform (periscope.michelebigi.it) and related services (the "Service"). These Terms apply to free users and self-serve paid users. If you have a separate written agreement signed by Faradworks, Inc. (for example, an enterprise agreement), that agreement governs your use of the Service to the extent it conflicts with these Terms.
These Terms incorporate Faradworks' [Privacy Policy](/privacy) and any policies referenced in the Service.
@@ -16,7 +16,7 @@ By creating an account, clicking to accept these Terms (for example, by clicking
**"Derived Data"** means technical artifacts generated solely to operate the Service, such as parsed text, indexes, embeddings, summaries, or extracted metadata.
-**"Service"** means the Pinscope platform (pinscope.ai), including all features, APIs, and related services.
+**"Service"** means the Periscope platform (periscope.michelebigi.it), including all features, APIs, and related services.
**"Content"** refers collectively to Customer Content, Outputs, and Derived Data.
@@ -170,15 +170,15 @@ Plans may include limits on reviews, tokens, files, API spend, usage allocations
### 11.2 Prepaid service credits.
-Certain Services, plans, or features may allow or require you to prepay for future eligible Pinscope review services for professional, business, or organizational use by purchasing prepaid service credits ("Usage Credits"). Usage Credits represent a prepaid, limited, revocable, non-transferable license to access eligible Pinscope review services up to the applicable credited amount and may be used only for eligible Pinscope review charges as described in the Service. Faradworks may also, in its sole discretion, provide free or promotional credits ("Promotional Credits"), which may be subject to additional restrictions or expiration dates stated when issued.
+Certain Services, plans, or features may allow or require you to prepay for future eligible Periscope review services for professional, business, or organizational use by purchasing prepaid service credits ("Usage Credits"). Usage Credits represent a prepaid, limited, revocable, non-transferable license to access eligible Periscope review services up to the applicable credited amount and may be used only for eligible Periscope review charges as described in the Service. Faradworks may also, in its sole discretion, provide free or promotional credits ("Promotional Credits"), which may be subject to additional restrictions or expiration dates stated when issued.
### 11.3 Credit characteristics and workspace scope.
-Credits may be used only for eligible Pinscope review charges and may not be used for any other product or service unless Faradworks expressly states otherwise in the Service. Credits are not legal tender, are not currency, are not redeemable for cash, are not refundable except as required by law or expressly stated by Faradworks, do not constitute or confer any personal property right, and do not constitute a bank account, deposit account, stored-value account, digital wallet, payment instrument, or other monetary account. Credits are an internal service accounting mechanism that measures the amount of eligible Pinscope review services you have prepaid and are licensed to use. Any credit balance or similar amount displayed in the Service reflects only our record of remaining prepaid eligibility for future eligible review charges and does not represent money held on your behalf. Credits are non-transferable, may not be sold, assigned, gifted, or sublicensed, and may be used only by the workspace or account to which they are issued. If credits are issued to an organization or workspace, they belong to that workspace and may be consumed by authorized users acting within that workspace.
+Credits may be used only for eligible Periscope review charges and may not be used for any other product or service unless Faradworks expressly states otherwise in the Service. Credits are not legal tender, are not currency, are not redeemable for cash, are not refundable except as required by law or expressly stated by Faradworks, do not constitute or confer any personal property right, and do not constitute a bank account, deposit account, stored-value account, digital wallet, payment instrument, or other monetary account. Credits are an internal service accounting mechanism that measures the amount of eligible Periscope review services you have prepaid and are licensed to use. Any credit balance or similar amount displayed in the Service reflects only our record of remaining prepaid eligibility for future eligible review charges and does not represent money held on your behalf. Credits are non-transferable, may not be sold, assigned, gifted, or sublicensed, and may be used only by the workspace or account to which they are issued. If credits are issued to an organization or workspace, they belong to that workspace and may be consumed by authorized users acting within that workspace.
### 11.4 Credit purchases and application to charges.
-Your order for Usage Credits constitutes an offer to purchase those Usage Credits. Faradworks may accept or reject any purchase request in its discretion. Credits are issued when Faradworks confirms the purchase or otherwise makes the credits available in your account or workspace. Credits are applied to eligible Pinscope review charges in the manner described in the Service. Faradworks may reserve, deduct, reverse, release, or adjust credits to reflect quoted charges, completed usage, failed runs, duplicate requests, fraud checks, refunds, chargebacks, or billing corrections. Credit pricing, minimum purchase amounts, maximum purchase amounts, and applicable taxes will be shown in the Service or at checkout. Fees are exclusive of taxes unless stated otherwise.
+Your order for Usage Credits constitutes an offer to purchase those Usage Credits. Faradworks may accept or reject any purchase request in its discretion. Credits are issued when Faradworks confirms the purchase or otherwise makes the credits available in your account or workspace. Credits are applied to eligible Periscope review charges in the manner described in the Service. Faradworks may reserve, deduct, reverse, release, or adjust credits to reflect quoted charges, completed usage, failed runs, duplicate requests, fraud checks, refunds, chargebacks, or billing corrections. Credit pricing, minimum purchase amounts, maximum purchase amounts, and applicable taxes will be shown in the Service or at checkout. Fees are exclusive of taxes unless stated otherwise.
### 11.5 Credit expiration, forfeiture, and promotional credits.
diff --git a/frontend/public/favicon_io/site.webmanifest b/frontend/public/favicon_io/site.webmanifest
index 57ec2d5..dce5d73 100644
--- a/frontend/public/favicon_io/site.webmanifest
+++ b/frontend/public/favicon_io/site.webmanifest
@@ -1,6 +1,6 @@
{
- "name": "Pinscope",
- "short_name": "Pinscope",
+ "name": "Periscope",
+ "short_name": "Periscope",
"icons": [
{
"src": "/favicon_io/android-chrome-192x192.png",
diff --git a/frontend/src/app/(app)/admin/page.tsx b/frontend/src/app/(app)/admin/page.tsx
index 64fd853..eb304c1 100644
--- a/frontend/src/app/(app)/admin/page.tsx
+++ b/frontend/src/app/(app)/admin/page.tsx
@@ -234,7 +234,7 @@ function ProjectsPanel() {
// Stash the source project id and hand off to the dashboard, which
// mounts the create-project dialog. The dialog fetches the full
// Project on its end so we don't have to pass the entire object here.
- window.sessionStorage.setItem("pinscopex:cloneAsNewProjectId", projectId);
+ window.sessionStorage.setItem("periscopex:cloneAsNewProjectId", projectId);
router.push("/dashboard");
}
diff --git a/frontend/src/app/(app)/dashboard/page.tsx b/frontend/src/app/(app)/dashboard/page.tsx
index d402525..5490b03 100644
--- a/frontend/src/app/(app)/dashboard/page.tsx
+++ b/frontend/src/app/(app)/dashboard/page.tsx
@@ -20,7 +20,7 @@ import { useCredits } from "@/components/billing/credits-context";
import { OnboardingSurvey } from "@/components/dashboard/onboarding-survey";
type ViewMode = "cards" | "table";
-const VIEW_STORAGE_KEY = "pinscopex:dashboard:view";
+const VIEW_STORAGE_KEY = "periscopex:dashboard:view";
export default function DashboardPage() {
return (
@@ -79,10 +79,10 @@ function DashboardContent() {
// Admin handoff: "Rerun as new project" stashes a project ID here.
const cloneId =
typeof window !== "undefined"
- ? window.sessionStorage.getItem("pinscopex:cloneAsNewProjectId")
+ ? window.sessionStorage.getItem("periscopex:cloneAsNewProjectId")
: null;
if (cloneId) {
- window.sessionStorage.removeItem("pinscopex:cloneAsNewProjectId");
+ window.sessionStorage.removeItem("periscopex:cloneAsNewProjectId");
fetchProject(cloneId)
.then(setCloneAsNewProject)
.catch(() => {
diff --git a/frontend/src/app/(app)/feedback/page.tsx b/frontend/src/app/(app)/feedback/page.tsx
index 31b57aa..b77436b 100644
--- a/frontend/src/app/(app)/feedback/page.tsx
+++ b/frontend/src/app/(app)/feedback/page.tsx
@@ -104,7 +104,7 @@ export default function FeedbackPage() {
)}
{expanded === t.ticket_id && t.admin_notes && (