Overlay live legal and changelog markdown from periscope/src.

privacy, terms, file-guide, and changelog stay Periscope operator text at 2.56.0. Docker copies src changelog last; the frontend image keeps content/ at runtime.
This commit is contained in:
2026-09-20 20:37:11 +02:00
parent 6b1fe5be00
commit 7a377889ae
8 changed files with 1088 additions and 2 deletions
+1
View File
@@ -18,6 +18,7 @@ COPY periscope/src/taxonomy/ /app/taxonomy/
COPY periscope/dependency/skills/ /app/skills/
COPY periscope/src/skills/ /app/skills/
COPY periscope/dependency/frontend/content/changelog.md /app/changelog.md
COPY periscope/src/frontend/content/changelog.md /app/changelog.md
COPY vendor/ /app/vendor/
EXPOSE 8080
+3 -2
View File
@@ -4,7 +4,8 @@ Physical split:
periscope/src native Periscope
periscope/dependency inherited PinScope (in-tree dependency)
Docker copies dependency then src for backend, taxonomy, and skills.
Docker copies dependency then src for backend, taxonomy, skills, and
frontend content (changelog / legal / file-guide).
"""
from __future__ import annotations
@@ -83,9 +84,9 @@ def changelog_paths() -> list[Path]:
dep = dependency_root()
src = src_root()
return [
src / "frontend" / "content" / "changelog.md",
Path("/app/changelog.md"),
dep / "frontend" / "content" / "changelog.md",
src / "frontend" / "content" / "changelog.md",
repo_root() / "frontend" / "content" / "changelog.md",
]
+683
View File
@@ -0,0 +1,683 @@
# Changelog
What's new in Periscope.
## 2.56.0 — 2026-09-20 — Native taxonomy JSON and local skills overlay
Component taxonomy JSON and DeepSeek `skills/*/SKILL.md` (plus local `validate.py` / schema) live under `periscope/src`. `repo_paths` prefers src; Docker copies dependency then src; compose mounts `src/taxonomy`. Manifest `1.14.0`. App `favicon.ico` matches the Periscope mark. Auth/JWT, package.json, landing png/gif unchanged. Pad≠Via unchanged.
- [New] Periscope taxonomy catalog in `periscope/src/taxonomy` (same subtype keys and spec names).
- [New] Local extraction skill prompts rewritten; no Anthropic Console upload.
- [Changed] Compose no longer bind-mounts `dependency/taxonomy` over `/app/taxonomy`.
## 2.55.0 — 2026-09-20 — Native Next overlay config and CSP seam
`next.config.ts`, ESLint/PostCSS/tsconfig, and `lib/csp-hosts.ts` overlay from `periscope/src/frontend`. `backend/middleware/__init__.py` is a native package marker. JWT stays in inherited `middleware/auth.py`. `package.json` unchanged. Legal markdown already Periscope; changelog kept. Pad≠Via unchanged.
- [New] Next CSP/redirect/pdfjs overlay rewrite; login still maps to `/sign-in`.
- [New] CSP host lists stay empty for Clerk/Stripe on self-host.
- [New] Middleware package marker rewrite; auth module not re-exported.
## 2.54.0 — 2026-09-20 — Native leftover frontend widgets and tokens
PDF sheet, theme provider/toggle, file drop zone, reviewed-finding hooks, and `globals.css` are original Periscope code in `periscope/src/frontend`. Inherited copies stay on disk. shadcn, Clerk, billing, Reddit pixels, `proxy.ts`, and auth/JWT are unchanged. Pad≠Via unchanged.
- [New] Datasheet sheet, theme chrome, and upload drop-zone rewrite.
- [New] Reviewed-finding localStorage hooks rewrite (legacy key migrate kept).
- [New] App token stylesheet rewrite; light/dark oklch values unchanged.
## 2.53.0 — 2026-09-20 — Native site, legal shells, OG, and Next BFF
`lib/site.ts`, `version.ts`, `report-export.ts`, legal page chrome, Open Graph images, and the local `/api/projects` `/api/report/[id]` `/api/graph/[id]` BFF routes are original Periscope code in `periscope/src/frontend`. Inherited copies stay on disk. Clerk `proxy.ts`, shadcn `components/ui/*`, billing, Reddit pixels, and auth/JWT are unchanged. Pad≠Via unchanged.
- [New] Site identity, version stamp, and findings Excel export rewrite.
- [New] Legal/changelog/file-guide shells and OG image rewrite.
- [New] Local Next BFF JSON routes rewrite; Clerk proxy stays the open-core seam.
## 2.52.0 — 2026-09-20 — Native leftover frontend overlay
Dashboard, project, report, progress, admin, feedback, marketing, and the HTTP client (`lib/api.ts`, `types.ts`) are original Periscope UI in `periscope/src/frontend`. Docker still copies OSS then src. Routes, API contracts, and layout are unchanged. Billing/Clerk/analytics stay imported seams. Auth/JWT untouched. Pad≠Via unchanged.
- [New] Frontend types + API client rewrite (export names preserved).
- [New] App and marketing pages rewrite so overlay wins without a product restyle.
## 2.51.0 — 2026-09-20 — Native Settings, FastAPI app, leftover routers
`config.py`, `main.py`, `pipeline_worker.py`, and leftover HTTP routers (`admin`, `contact`, `deps`, `feedback`, `pipeline`, `projects`, `reports`, `survey`) are original Periscope code in `periscope/src`. Inherited copies stay on disk. `routers/auth.py` is unchanged. JWT middleware remains leftover. Frontend product chrome is still the Docker overlay (src pages already win for library/PCB/placement/sign-in). Pad≠Via unchanged.
- [New] Settings rewrite: DeepSeek-only routing; Anthropic env names coerced or ignored.
- [New] FastAPI entry + worker rewrite; worker still must not import `backend.main`.
- [New] Project/pipeline/report/admin HTTP surface rewrite.
## 2.50.0 — 2026-09-20 — Native analysis pipeline and leftover checks
`pipeline.py`, `projects.py`, `email.py`, `purple_parts.py`, and leftover analysis modules (`resolve_passives`, `derating`, `bom_summary`, `pin_mux_check`, `led_current_check`, `pin_function_tokens`, plus `validate`/`validation_tools` aliases) are original Periscope code in `periscope/src`. Inherited copies stay on disk. Live path is still `job_workspace` / `datasheet_extract` / `review_session`. Auth untouched. Pad≠Via unchanged.
- [New] Analysis job rewrite: BOM parse, extract, graph, review; LCSC column backstop only.
- [New] Project meta writes follow `owner_user_id` (Emmaforo JWT prefix).
- [New] Exact-MPN LCSC reverse lookup; prefix hits rejected.
## 2.49.0 — 2026-09-20 — Native normalize, cross-IC dedupe, billing seam
`normalize_findings.py`, `dedupe_findings.py`, and `billing_hook.py` are original Periscope code in `periscope/src`. Inherited copies stay on disk. Self-host `get_billing()` is NullBilling (no Stripe at import). Downgrade-only clamp unchanged (U2-001 / Emmaforo UART).
- [New] Per-IC normalize builder + tool schema rewrite; fail-soft to originals.
- [New] Cross-IC dedupe rewrite (CH340E↔MCU interface collapse).
- [New] Billing seam rewrite; credits stay lazy behind `billing_enabled`.
## 2.48.0 — 2026-09-20 — Native models, schematic parsers, taxonomy loader
`models.py`, `parsers.py`, `parsers_edif.py`, and `taxonomy.py` are original Periscope code in `periscope/src` (not overlay stamps). Inherited copies stay on disk. Taxonomy JSON is still `repo_paths.taxonomy_dir()``periscope/dependency/taxonomy`. Native `parsers_kicad.py` / `parsers_kicad_pcb.py` (via≠pad) unchanged.
- [New] Periscope Pydantic contracts: disjoint LayoutPad / LayoutVia / LayoutSegment / LayoutZone.
- [New] PADS/BOM dispatch and EDIF 2.0.0 ingest rewrite; KiCad schematic still native.
- [New] Taxonomy loader rewrite: `TAXONOMY_DIR = taxonomy_dir()`.
## 2.47.0 — 2026-09-20 — Native DesignGraph builder
`backend.periscopex.graph` is original Periscope code in `periscope/src` (not an overlay stamp). Inherited `dependency/.../graph.py` stays on disk. PCB via≠pad remains in native `parsers_kicad_pcb.py`.
- [New] Periscope `graph.py`: classify refs, infer net type/voltage, BOM+netlist+optional `.kicad_pcb` nets → `DesignGraph`.
## 2.46.0 — 2026-09-20 — Remove PinScope overlay copies from periscope/src
Stamped near-identical copies of inherited modules are gone from `periscope/src`. Docker still copies `dependency/` then native `src`. Tests load graph/parsers/models/pipeline from `periscope/dependency` again. Native work stays: finding engine, PCB/placement, review session, job workspace, datasheet extract, KiCad PCB via≠pad parser, Periscope mark, local JWT, `pinscope_compat`.
- [Changed] Deleted overlay copies of graph, models, parsers, leftover services, main/routers, skills JSON, taxonomy JSON from `periscope/src`.
- [Changed] Overlay stamp tests removed. Inherited files remain in `periscope/dependency/` (not empty-deleted).
## 2.45.0 — 2026-09-20 — Overlay leftover app entry, routers, and jobs
`main.py`, `config.py`, `middleware/auth.py`, leftover `routers/*` (not native `auth`/`impedance`), `pipeline_worker.py`, `job_runner`, `event_bridge`, `projects`, and `admin_settings` resolve from `periscope/src`. Inherited copies stay on disk. Auth users and `AUTH_JWT_SECRET` unchanged.
- [New] src overlay of the leftover FastAPI entrypoint, settings, JWT middleware, and HTTP routers the live app still imported from `dependency/`.
- [New] src overlay of `pipeline_worker` and job/project settings services.
## 2.44.0 — 2026-09-20 — Overlay leftover services src still imported
Pipeline helpers still pulled from `dependency/` (`api_logs`, `normalize_findings`, `dedupe_findings`, `billing_hook`, `datasheet_store`, `cost_estimator`, `storage`, `purple_parts`, `email`, `digikey`) plus LLM `factory` / `types` / `base` now resolve from `periscope/src`. Inherited copies stay on disk.
- [New] src overlay of leftover `backend/services` modules the live pipeline/review path still imports.
- [New] src overlay of `services/llm/{factory,types,base}.py` (DeepSeek provider stays native; Anthropic/Gemini files stay in dependency).
## 2.43.0 — 2026-09-20 — Overlay leftover PinScope modules src still imported
`utils`, `resolve_passives`, `derating`, `bom_summary`, `pin_mux_check`, `led_current_check`, `pin_function_tokens`, plus live analysis `validate` / `validation_tools` / `pipeline` / `validation` / `extraction` (fallback), resolve from `periscope/src`. Skills (`extract-*`) and taxonomy JSON are copied to `periscope/src/{skills,taxonomy}`; Docker overlays them after `dependency/`. Inherited files stay on disk.
- [New] src overlay of the leftover `periscopex` helpers analysis/PCB still import.
- [New] src overlay of `services/{pipeline,validation,extraction}.py` (re-exports `job_workspace` unchanged).
- [New] `periscope/src/taxonomy/*.json` and `periscope/src/skills/extract-*`.
- [Changed] `repo_paths` prefers src (then `/app`, then dependency) for taxonomy and skills.
## 2.42.0 — 2026-09-20 — Native graph/parsers/models/taxonomy overlay + Periscope mark
Graph builder, PADS/EDIF/BOM parsers, Pydantic models, and taxonomy loader resolve from `periscope/src`. Inherited copies stay in `periscope/dependency/` (not deleted). KiCad schematic parser was already native. Taxonomy JSON still lives in the inherited `taxonomy/` tree. New Periscope mark (periscope/lens, not the PinScope CPU glyph) at the existing favicon and PWA pixel sizes.
- [New] `periscope/src/backend/periscopex/{graph,models,parsers,parsers_edif,taxonomy}.py` overlay Docker/src-first imports.
- [Changed] `taxonomy.TAXONOMY_DIR` uses `repo_paths` so src overlay still reads `dependency/taxonomy`.
- [New] Brand mark: `public/brand/periscope-mark.svg`, favicon_io 16/32/180/192/512, header `PeriscopeMark`.
## 2.41.0 — 2026-09-20 — Native job workspace for PCB and placement
`pcb_pipeline` and `placement_pipeline` no longer import PinScope `pipeline.py`. Workspace download/upload and the in-memory event broker live in `periscope/src` (`job_workspace.py`). Analysis `run_pipeline` still lives in `dependency/` and re-exports the same singleton so `pipeline_worker.set_broker` keeps PCB SSE on the GCS event log. Parsers/graph unchanged. `pipeline.py` not deleted.
- [New] `backend.services.job_workspace`: `PipelineWorkspace`, `EventBroker`, `set_broker`.
- [Changed] PCB/placement import `job_workspace`; `pipeline.py` re-exports the native types.
## 2.40.0 — 2026-09-20 — Fase C3: native datasheet extraction
Pipeline pintable/pattern/specs/auto-resolve run from `periscope/src` (`datasheet_extract.py`): DeepSeek + local SKILL.md, no Anthropic Console skill ids. Inherited `extraction.py` stays in `dependency/`.
- [Changed] `pipeline.py` and project LCSC resolve import `datasheet_extract`.
- [Changed] Extract stages pin the DeepSeek provider.
## 2.39.1 — 2026-09-20 — src no longer imports PinScope review loop
After 2.39.0 live smoke (Emmaforo ERROR 2 / WARNING 12 / INFO 18), native modules are the only review path from `periscope/src`. PinScope `validate.py` / `validation_tools.py` remain on disk.
- [Changed] PCB review and deterministic checks import `review_parse` / `constraints_lookup` / `review_session`, not `validate.py`.
- [Changed] Tests exercise the native parser and graph tools.
## 2.39.0 — 2026-09-20 — Fase C2: native DeepSeek review loop
Live per-IC review runs from `periscope/src` (`review_session.py`, parse, graph tools). PinScope `validate.py` / `validation_tools.py` stay in `dependency/` as fallback; they are not deleted. LLM findings stay REVIEW; recommended never ERROR. AGPL unchanged. No parser/graph rewrite. No auto-place.
- [Changed] `review_ic_async` is native (`backend.services.review_session`); inherited copy kept as `_inherited_review_ic_async`.
- [New] `review_parse.py` (FACT/REQUIREMENT/INFERENCE + `complete_findings` ids), `review_tools.py`, `review_context.py`, `constraints_lookup.py`.
- [Changed] `finding_engine.complete_finding` remains the clamp after submit_review.
## 2.38.0 — 2026-09-20 — Physical split: periscope/src vs periscope/dependency
Split the checkout so native Periscope and inherited PinScope are separate trees. No finding-engine rewrite. AGPL `LICENSE` stays at the repo root. The GitHub fork is not detached.
- [Changed] Native modules live under `periscope/src/` (PCB, placement, finding clamp, DeepSeek, local auth, plugin, Dockerfiles).
- [Changed] PinScope-derived modules live under `periscope/dependency/` (graph, parsers, `validate.py`, pipeline, OSS Next shell, skills, taxonomy, `simple_project`).
- [Changed] Docker overlays dependency then src. Local `backend` package merges both trees. Do not delete the inherited tree; Fase C replaces modules incrementally.
## 2.37.1 — 2026-09-20 — PCB not_reviewed is board ICs, not the shared library
Emmaforo showed 20 “components not reviewed” that were other projects 1.51.7 extracts (LAN8720A, W25Q, …). BOM ICs were already 1.13.0 and *were* reviewed.
- [Fixed] `si_extract_needed_skips` only flags ICs on this graph. Shared `library/extracted` cache is not `not_reviewed`.
## 2.37.0 — 2026-09-20 — SI bus gate (EN RC ≠ USB series R)
Datasheet SI facts apply only to the bus in the quote. ESP32 EN RC 10 kΩ / 1 µF is not a USB D+/D series resistor. USB without a library Z number stays measurement-only (88 Ω), not a 90 Ω FAIL. Coverage: PHY+RJ45 MDI, MACPHY RGMII/SGMII, DDR3, USB-C SuperSpeed.
- [Fixed] `PE-SI-009` (and other SI kinds) require `net_class` / quote bus match; strap/EN RC is skipped.
- [Changed] `bus_class`: `usb2` / `usb3` / `eth_mdi` / `rgmii` / `sgmii` / `ddr3_*`.
- [Changed] `PE-SI-010` is per bus with no impedance FACT (not silenced by an unrelated SI kind).
- [Changed] Pintable skill `1.13.0`: SI `net_class` required; EN/CHIP_PU RC is not `series_resistor`.
## 2.36.0 — 2026-09-20 — Fase A: operator identity + PinScope fence
This instance is operated by Michele Bigi. Faradworks, Inc. is not the operator. PinScope identifiers are dual-read only. PCB BOM/SPOF/EMI/θJA slices from 2.35.0 stay on this branch.
- [Changed] Terms, privacy, contact, footers, and HTML metadata no longer claim Faradworks, Inc.
- [Changed] Attribution to Faradworks/Pinscope remains for AGPL lineage (`LICENSE` unchanged).
- [Changed] `pinscope-compat` / `pinscope_compat.py`: write `periscope_*` / `periscopex:`; migrate `pinscope_*` on read.
- [New] `docs/development/PINSCOPE_FENCE.md` — inherited tree stays in place; GitHub fork is not detached.
## 2.35.0 — 2026-09-20 — BOM↔PCB↔datasheet, SPOF, EMI FACT, gated Tj
Remaining Fase B slices except auto-place and thermal FEM / OpenEMS.
- [New] `PE-BOM-010``014` package / pinout / voltage / temp / current when BOM, PCB, and datasheet all have numbers.
- [New] `PE-SPOF-001` single regulator or shared crystal — REVIEW, never ERROR.
- [New] `PE-EMI-001` only if `layout_rules` quotes EMI/common-mode/ferrite/shield (not IEC 61000).
- [New] `PE-THM-002` Tj = Ta + P·θJA when P, θJA, copper area, and via count exist; else INSUFFICIENT.
- [Changed] Pintable skill `1.12.0`: SI+EMI kinds; empty SI on older extracts triggers schematic re-extract (`si_extract_needed.json` on PCB). ImpedenceFinder license remains UNKNOWN (no upstream LICENSE).
## 2.34.0 — 2026-09-20 — ImpedenceFinder SI checks (not a dump table)
USB/HDMI/PCIe/ETH/LVDS/DDR layout_rules (diff/SE Z, skew, max length, spacing, ref plane, vias, layer, return, series R) are compared to ImpedenceFinder + copper. One PASS/FAIL/MARGIN finding per requirement. I2C/GPIO/CC/REGN are not 50 Ω.
- [New] `PE-SI-002``PE-SI-010`; ImpedenceFinder `z0_avg/min/max` in `run_pcb_checks`.
- [Changed] `PE-SI-001` only on impedance-controlled buses matching the rule `net_class`.
- [Changed] extract-pintable `layout_rules` kinds for SI; `default_model_version` 1.11.0.
## 2.33.2 — 2026-09-20 — PCB complete UI, not a raw SSE error
A finished PCB exam no longer shows the raw watcher string `pcb_status=complete (terminal)`. The SSE hatch emits `pcb_complete`; the PCB page then opens `/report?domain=layout` (expand-on-click tree).
- [Fixed] Synthetic PCB SSE for `pcb_status=complete` is `pcb_complete`, not `pcb_error`.
- [Fixed] `/project/[id]/pcb` navigates to the layout report when the run finishes.
- [Fixed] Restored changelog heading for 2.33.0 (version history).
## 2.33.1 — 2026-09-20 — ESD/PI filters, ERROR-first sort, findings tree
PE-ESD-001 only on J* ∩ IC nets (no NC / unconnected / VSYS/GND/3V3 spam). PE-PI-001 treats any capacitor on the rail, including KiCad `/` names, as decoupling. Report API and UI sort ERROR then WARNING then INFO, then RULE > RISK > REVIEW > INFO. The report left list is an expand-on-click tree (IC → pin/net → cards), not a dump of open groups.
- [Fixed] Connector GPIO / onboard rails no longer flood PE-ESD-001.
- [Fixed] Bulk or slash-prefixed caps (C68 `/VBUS`, C71C75 VSYS) suppress “no local cap”.
- [Changed] GET `/report` and `complete_findings` order by severity then class.
- [Changed] Findings sidebar is a collapsed tree; PCB exam cards live in the same tree.
## 2.33.0 — 2026-09-20 — Fase B: hierarchy, derating bands, timing, PI, ESD/return
Usable slices (not a platform): component→pin→net→block inventory, capacitor PASS/MARGIN/RISK from Vop/Vrated, RC/strap timing only with numbers, local vs bulk PI, ESD and HS return as REVIEW.
- [Fixed] `complete_finding` clamps ERROR unless `finding_class=RULE` and `provenance=MANDATORY` (LLM REVIEW cannot stay ERROR).
- [New] `hierarchy.json` + `PE-HIER-001` dangling IC pins.
- [New] Derating table `stress` PASS/MARGIN/RISK; findings `PE-DRT-001/002/003`.
- [New] `PE-TIM-001/002` skip without R/C and t_reset or Vih/Vil.
- [New] `PE-PI-001/002` local vs bulk; distance only with `layout_rules` mm.
- [New] `PE-ESD-001`, `PE-RET-001` are REVIEW, never RULE ERROR.
## 2.32.1 — 2026-09-20 — PCB Action on card + parsed geometry in AI context
Finding cards always show an **Action.** sentence (`action`, else `recommendation`, else a default). PCB AI context now includes vias under the footprint, copper thickness, nearby trace widths, courtyard, and keepout polygons from `.kicad_pcb`.
- [Fixed] Layout/AI findings no longer show an empty Action chip; the sentence is on the card.
- [Changed] PCB exam injects parsed via counts/drills, stackup thickness, pin-net widths, courtyard/keepout. Insufficient evidence only when those extracts are empty.
- [Changed] submit_review `recommendation` is required for INFO as well as ERROR/WARNING.
## 2.32.0 — 2026-09-20 — Finding engine (schema + PCB)
Same finding object on schematic analysis and PCB exam: FACT / REQUIREMENT / INFERENCE, datasheet provenance in the rule DB, RULE vs REVIEW, designer decisions, independent severity/confidence/evidence.
- [New] `docs/motore-finding.md` — engine spec. `finding_engine.py` + `decisions.json`.
- [New] Report cards show Fact / Requirement / Inference, class, provenance, insufficient evidence.
- [Changed] Recommended datasheet notes cannot stay ERROR. LLM findings are REVIEW, never RULE.
- [Changed] Schematic and PCB share `library/extracted`; PCB does not re-read the PDF.
## 2.31.0 — 2026-09-20 — Shared library PCB exam (no second PDF pass)
Schematic and PCB share `library/extracted`. PCB AI consumes that cache (pintable required; no PDF attach). Report **PCB exam** section plus power-trace/thermal checks.
- [New] Report section **PCB exam** (`source=pcb_review` and PE-PWR/THM/VIA/KEL/STCH).
- [New] `PE-PWR-001` width × copper vs I_load (IPC-2221 only with stackup + Tjmax).
- [New] `PE-THM-001` dissipation vs courtyard vias/pour; `PE-VIA-001` via share of I_load (INFO).
- [New] `PE-KEL-001` Kelvin/sense on a shared load net; `PE-STCH-001` GND stitch in a signal bbox over a pour.
- [Changed] PCB review skips ICs without library pintable (“run schematic review first”).
## 2.30.0 — 2026-09-19 — PCB review pipeline (exam, not auto-place)
Parallel `MODE=pcb` job examines an uploaded `.kicad_pcb`: domains/groups, trace inventory, deterministic layout checks, and per-IC AI datasheet review. Findings merge into the report with a Layout filter. No packing or pcbnew write-back.
- [New] `POST /api/pipeline/{id}/pcb/start` — PCB exam job (`pcb_status`).
- [New] Deterministic `PE-LAY-*`, `PE-PLC-*`, `PE-SI-001`, `PE-DRT-001` with required recommendations.
- [New] AI PCB review (same loop as schematic) with layout/domain context.
- [Changed] Schematic review no longer seeds placement/SI checks.
## 2.29.1 — 2026-09-13 — Fix sign-in /login 404 after rebrand
Landing CTAs pointed at `/login`, which did not exist (page is `/sign-in`). Added redirects, accepted legacy JWT issuer `pinscope-local`, and CORS for both Periscope and Pinscope hosts.
- [Fixed] `/login``/sign-in`, `/register``/sign-up`; marketing links updated.
- [Fixed] JWT decode accepts `pinscope-local` issuer from pre-rebrand sessions.
- [Fixed] Deploy script CORS includes both public hostnames.
## 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
Progetta antenna now returns a full parametric drawing: polyline segments (mm), SVG preview, and a downloadable KiCad footprint. Choose IFA, meander, or stub; geometry scales to the `antenna` zone when present. Still a routing template — no EM/VSWR invented.
- [New] `antenna_geometry.py` (IFA / meander / stub → segments + SVG + `.kicad_mod`).
- [Changed] `POST /antenna/design` accepts `template`; recipe includes `geometry`.
- [Changed] RF / Impedance Progetta UI: template select, SVG preview, download footprint.
## 2.28.11 — 2026-09-13 — RF / Impedance: verify antenna + design recipe
Project tab renamed **RF / Impedance**. Verify matching from IC ANT/RF pins toward an `ANT*` / `ANT_FEED` marker; Progetta returns microstrip `w` for 50 Ω (stackup or UI h/εr), optional λ/4 length from `f0`, and antenna-zone bbox. Auto-draw copper comes later — no EM/VSWR invented.
- [New] `antenna_rf.py` + `GET/POST /api/projects/{id}/antenna`.
- [New] Verify + Progetta cards on the impedance panel.
- [Changed] Sidebar label **RF / Impedance**.
## 2.28.10 — 2026-09-13 — Classify strap/bootstrap satellites; hide other
Caps on EN/BOOT/REGN/PMID and bootstrap caps between IC pins get real roles. Unclassified `other` parts are dropped from satellites and assemble_order.
- [Changed] `_role_hint` classifies GND bypass on bias/strap nets and flying bootstrap caps.
- [Changed] Assemble / IC groups omit `role_hint=other`.
## 2.28.9 — 2026-09-13 — Cleaner Domains / Power rails membership
Power rails follow primary domain only. IC satellites for decoupling/bulk/filter/pullup stay on the ICs primary supply rail (LDO 3V3 no longer inherits VSYS input caps). Connectors/switches drop out of noisy “other” satellites.
- [Fixed] Rails view membership = domain primary rail (not transitive satellite nets).
- [Fixed] `_group_for_ic` primary-rail filter for power-role satellites; skip connector/switch “other”.
## 2.28.8 — 2026-09-13 — Stronger layout_rules extraction skill
Pintable skill now treats PCB / typical-application layout guidance as a first-class extract. Page trim keeps layout keywords; IC cache re-extracts once when `layout_rules` are empty under an older `model_version` (1.10.0+).
- [Changed] `skills/extract-pintable` — dedicated `layout_rules` step, examples, hard negatives; tighter `validate.py`.
- [Changed] Datasheet page trim includes PCB layout / decoupling / typical-application keywords.
- [Changed] Analysis IC extraction refreshes empties when `model_version` < default (1.10.0).
- [Changed] `default_model_version``1.10.0`.
## 2.28.7 — 2026-09-13 — Placement F2 pack skeleton (gated)
Placement pipeline can propose satellite xy only when a `.kicad_pcb` layout exists and a `decoupling_proximity` rule has numeric `max_distance_mm`. No millimetres invented; otherwise `placement_pack.json` is skipped with an explicit reason.
- [New] `placement_pack.py` + `placement_pack.json` after F1 plan.
- [New] `GET /api/pipeline/{id}/placement/pack` and Pack section on the placement page.
- [Changed] Placement stepper adds a **Pack satellites** stage.
## 2.28.6 — 2026-09-13 — Split Domains by primary supply rail
Domains no longer merge the whole board when a charger/LDO bridges VBUS→VSYS→3V3. Each IC is clustered by its primary supply rail (output for power ICs, regulated digital rail for consumers).
- [Fixed] `_build_domains` groups by primary rail instead of transitive POWER union-find.
- [Changed] Domain ids use the rail name (e.g. `domain_3V3_DIGITAL`).
## 2.28.5 — 2026-09-13 — Domains & power-rail topology views
Browse routing-first functional groups from the project sidebar: Domains (power-net islands) and Power rails, each highlighting IC groups and satellite roles.
- [New] Sidebar tabs **Domains** and **Power rails** (from `placement_plan` / `functional_groups`).
- [Changed] Analysis `graph_build` also writes `placement_plan.json`.
- [Fixed] Heal stuck Placement `queued`/`running` when the worker is dead.
## 2.28.4 — 2026-09-13 — Unstick zombie running pipelines
A finished run whose worker died before flipping meta stayed `running`, so Progress showed Review spinning forever. Heal those projects when the event log already ends with `pipeline_complete`.
- [Fixed] `heal_if_pipeline_finished` on project get / pipeline status.
- [Fixed] Progress stepper marks all stages complete on `pipeline_complete`.
- [Fixed] Admin sweeper also covers local zombies without `execution_name`.
## 2.28.3 — 2026-09-13 — Stop progress→report bounce on finished projects
Opening a finished project no longer flashes Processing and dumps you on the report. Progress only auto-opens the report after a live run on that visit.
- [Fixed] `/progress` checks project status before SSE; finished projects go to the hub.
- [Fixed] Analysis SSE ignores placement events and does not treat already-terminal status as a crash.
## 2.28.2 — 2026-09-13 — Fix project sidebar navigation
Opening a finished project no longer dumps you on the report with a stuck left menu. Hub first; Report stays in the sidebar. Leaving `/report` for BOM/tabs uses a full navigation so the page actually changes.
- [Fixed] Dashboard project cards/table open `/project/{id}` (not `/report`).
- [Fixed] Sidebar Dashboard / Library / BOM tabs escape nested report/progress/placement routes.
## 2.28.1 — 2026-09-12 — Placement pipeline (parallel)
Dedicated Placement job builds the routing-first topology plan without touching the analysis pipeline status or spending credits. No millimetres — domains, IC groups, satellites only.
- [New] `POST /api/pipeline/{id}/placement/start` (+ cancel, events SSE, get plan).
- [New] Project page **Build placement plan**`/project/{id}/placement` progress + topology viewer.
- [New] Worker `MODE=placement` writes `placement_plan.json` (and refreshes `functional_groups.json`).
## 2.28.0 — 2026-09-12 — Layout F1 topology + crystal/NC checks
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` (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
Close the open P0/P2 items from the growth plan: offline smoke on `simple_project`, reviewer `shortest_path`, library promotion gate, PDF drop logging, per-stage cache hit-rate helper. IC placement stays Layout-product backlog.
- [New] `scripts/smoke_simple_project.py` + `simple_project/smoke_baseline.json`.
- [New] Graph tool `shortest_path`; `library_gate` before shared extracted/.
- [New] `cache_stats_by_stage()`; PDF truncate/omit page logs.
- [Test] `tests/test_roadmap_integrations.py`.
## 2.27.0 — 2026-09-11 — Local multi-user auth
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.
- [Changed] Collaborator lookup works for local users; Clerk still used when its keys are set.
## 2.26.5 — 2026-09-11 — Prefer PCB nets for connectivity
When a `.kicad_pcb` is present, pad nets from the board drive the design graph. Schematic geometry alone was inventing swapped rails (GND↔3V3) and floating pins on good KiCad 10 designs.
- [Fixed] KiCad 10 `(net "Name")` pad form.
- [Fixed] Graph build overrides sch nets with board connectivity; `/NET``NET`.
- [Test] PCB nets beat a misleading schematic.
## 2.26.4 — 2026-09-11 — KiCad power nets merge
`.kicad_sch` connectivity treated every `GND` power flag as its own island (`GND_`, `GND__`…), so the reviewer invented swapped rails and floating pins on good designs.
- [Fixed] Same-name power symbols and global/local labels merge within a sheet (KiCad semantics).
- [Fixed] Pins sitting mid-wire attach to that net; `mirror x`/`y` handled correctly.
- [Test] Disconnected GND flags, local labels, mid-segment wire.
## 2.26.3 — 2026-09-11 — KiCad project zip upload
One zip (or every `.kicad_sch`) covers hierarchical sheets. If the zip has `bom.csv` and `.kicad_pcb`, those are taken too. Pipeline re-reads companions from storage.
- [New] Zip → sheets + optional BOM/PCB. Nested `Sheetfile` paths kept.
- [Test] Upload → workspace re-parse finds child sheet parts.
## 2.26.2 — 2026-09-11 — Three file boxes
New project: BOM, schematic, and PCB on the first screen. PCB is optional.
- [Changed] No extra wizard step for the board.
## 2.26.1 — 2026-09-11 — Ask for the KiCad board
The create-project wizard has a dedicated PCB step (skip still allowed). Report load errors show the API detail instead of a generic fetch failure.
- [New] Wizard step for `.kicad_pcb`. Upload later from Settings or Impedance.
- [Improved] Report page retries briefly and links back to pipeline progress when `report.json` is missing.
- [New] Drop the KiCad project folder (or a zip). Extra library junk is ignored; the board is picked up if present.
## 2.26.0 — 2026-09-11 — ImpedenceFinder Z0 on PCB nets
A pipeline run with `.kicad_pcb` + stackup samples routed **signal** nets (power/ground skipped). Extra net names can be analyzed from the Impedance tab. Z0 is ImpedenceFinder net_walk/zsolver; no invented εr.
- [New] `impedance_nets.json` after graph build. GET/POST `/api/projects/{id}/impedance/nets`.
## 2.25.0 — 2026-09-10 — Keepout courtyard
`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] `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 `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] `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] `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] `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 `PE-PLC-001`. A null millimetre skips — no 3 mm default.
- [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
Findings can be accepted, marked false-positive, or wontfix with a required reason. Accepted rows export as ECO; OpenEMS-style layout SI is still not this.
- [New] Review state on the report (`open` / `accepted` / `false_positive` / `wontfix`). Empty reason is rejected except when returning to open.
- [New] ECO CSV/JSON of accepted findings only. False-positive and wontfix stay off the ECO.
- [New] Release signature: SHA-256 of findings + user + timestamp. Needs-review filter `?review=open`.
## 2.18.0 — 2026-09-10 — ImpedenceFinder calculator
The Impedance tab uses the closed-form engine from ImpedenceFinder (HammerstadJensen / Cohn), not a second formula set and not OpenEMS.
- [New] Project tab Impedance: microstrip, stripline, coupled-diff Z0, stackup → 50/90/100 Ω widths, download `.kicad_dru` advice.
- [New] Vendored ImpedenceFinder core under `vendor/impedancefinder/` (no gerber2ems, no pcbnew).
- CPWG stays unimplemented — no invented number.
## 2.17.0 — 2026-09-10 — Lifecycle and datasheet extras
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] `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
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).
- [New] Action plugin in `plugins/kicad/` reads the JSON beside `.kicad_pro` / `.kicad_pcb`.
## 2.15.0 — 2026-09-10 — Power margin, sequencing, DNP enable
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] `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 `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×(VinVout)` 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
The derating table now shows an effective capacitance under DC bias for C0G/X7R/X5R ceramics. It is labelled *stima* — not a vendor lot curve.
- [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 `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 (`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
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.
- [Improved] API cost uses V4.1 Flash peak rates and is shown on the report, logs tab, and run estimate (not only credits).
## 2.10.0 — 2026-08-27 — Deeper datasheet review
Each IC review now sees more of the datasheet and starts from a structured abs-max table, so voltage, decoupling, and interface checks are less likely to stop at "Unverified".
- [Improved] Extraction pulls Absolute Maximum Ratings (supplies, pin voltages, current, temperature) alongside the pin table, and the reviewer gets those numbers in context.
- [Improved] DeepSeek ingest uses PyMuPDF text (tables survive better than pypdf) and, on vision models, renders pin / abs-max / electrical / application pages instead of always the first 24.
- [Improved] Review checklist now includes recommended operating conditions, VIH/VIL, crystal load caps, and datasheet-named external parts. Turn budget 16, more excerpt pages, three follow-ups per concern.
## 2.9.0 — 2026-08-27 — Component library
Chips and passives stay in a shared library after the first look, so the next board does not re-download datasheets or re-extract pin tables.
- [New] Library page in the sidebar: ICs (pin tables), passive series, discrete specs, and saved PDFs.
- [New] Datasheets are written to the library as soon as they are fetched or uploaded, not only after a full review.
- [Improved] The create-project wizard already skipped parts that were extracted once; that reuse now covers the PDF itself as well.
## 2.8.0 — 2026-08-27 — Automatic datasheets
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.
- [New] Pipeline fallback: if a datasheet was not uploaded in the wizard, extraction and review try the same lookup before skipping the IC.
- [Improved] DigiKey remains an optional third source when `DIGIKEY_CLIENT_ID` / `DIGIKEY_CLIENT_SECRET` are set.
## 2.7.0 — 2026-08-27 — DeepSeek API
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.
- [New] PDF ingest for DeepSeek: pypdf text extraction plus optional PyMuPDF page renders on vision models.
- [Improved] Anthropic and Gemini remain optional fallbacks via `PROVIDER_*` / `FALLBACK_PROVIDER_*`.
## 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 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.
## 2.5.1 — 2026-07-04 — More Thorough Reviews
Schematic review now works through every functional area of a component before finishing, so a part with several independent issues has all of them surfaced in one pass instead of just the first.
- [Improved] For each IC, the review covers power and decoupling, every signal interface, absolute-maximum ratings, and reset/boot/configuration and unused pins before reporting — catching multiple issues on the same component that could previously be missed.
## 2.5.0 — 2026-07-02 — Light Mode
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.
- [Improved] The sign-in page and account menu now follow the app theme instead of always rendering light.
## 2.4.0 — 2026-07-01 — Automatic Pin & LED Current Checks
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. 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 — 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.
## 2.3.2 — 2026-05-26 — Smarter RF Topology Review
Schematic review now reasons about *what each external part is for* before flagging it — catching valid bias, coupling, and matching circuits that previously looked like errors.
- [Improved] The reviewer states the role of every external part on an IC pin (choke, blocking cap, divider, decoupling, matching) before judging the connection. Common RF topologies like bias-T (DC injected onto a coax through a choke, with the chip protected by an internal DC block and a downstream load doing the actual draw) are no longer flagged as errors against the chip.
- [Improved] Stricter absolute-maximum-rating checks: the cited limit must come from the same pin under stress (a Vdd abs-max no longer counts against an RF or signal pin), and the inequality must be a strict exceed — equal-to-abs-max is at most a Warning.
- [Improved] Single-concern deep dives are capped at two follow-up queries; concerns that can't be resolved in that budget are reported as Warnings with the unresolved question stated, so one suspect finding can't starve the rest of the IC review.
- [Improved] Inferred rail voltages from the power-tree pass are no longer treated as ground truth by the schematic reviewer. Voltages set by net name (`+5V`, `+3V3`) or by user power-source hints are trusted as before; voltages the power-tree LLM guessed for an adjustable regulator output or propagated through inference are kept on the power-tree view for reference but excluded from review reasoning, so a single misread rail can't anchor a false-positive Error.
- [Improved] After each IC's review, a second pass normalizes findings against a fixed Error/Warning/Info rubric and merges any two findings that share a single root cause (e.g. "series resistor drops VIN" and "VOUT setpoint exceeds available VIN" are one defect, not two). Cuts run-to-run severity drift and avoids inflating the error count when one defect can be described from multiple angles.
## 2.3.1 — 2026-05-25 — EDIF Netlist Support
EDIF 2.0.0 netlists upload alongside PADS-PCB, with a sub-design picker for files that contain more than one design. Schematic review is also more cautious about polarity / direction-control findings.
- [New] Upload EDIF 2.0.0 (`.edn`) netlists directly. Format is auto-detected from the file contents — no need to convert to PADS-PCB first. Verified against Siemens xDX Designer exports.
- [New] When an EDIF file contains multiple sub-designs, project setup shows a picker so you can choose which one to review. The picker auto-confirms when there's a single clean match against your BOM and only asks when it's ambiguous; unselected sub-designs are filtered out of the design graph.
- [Improved] Stronger verification of differential and polarity pin assignments (USB D+/D, TX/RX, IN+/IN, anode/cathode) directly against the datasheet.
- [Improved] Improved support for bidirectional buffers and level translators (74xx245 and friends) — direction-control truth tables are factored into bus-contention analysis.
- [Improved] Findings that share a single root cause on the same chip are grouped into one combined finding.
## 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 — 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.
- [New] Passive specs (value, voltage, tolerance, dielectric, package) are resolved from the LCSC catalog during project setup, with per-row progress and status — you see what's resolved before spending credits on the full pipeline.
- [Improved] Datasheet auto-fetch hit rate is dramatically higher on LCSC BOMs, because DigiKey now sees real MPNs instead of `C…` ids.
## 2.2.1 — 2026-05-22 — Easier Netlist Uploads & Xpedition Support
Tabbed file upload guide with per-tool instructions, Xpedition coverage, and direct `.net` / `.txt` uploads.
- [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 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
The reviewer now reads neighbor-chip datasheets to verify cross-chip constraints, with fewer false errors when a spec can't be confirmed.
- [Improved] Schematic review now cross-references connected chips: when an issue depends on a neighbor's spec (5V tolerance, absolute-max, drive strength), the reviewer pulls the relevant pages from that chip's datasheet before flagging it.
- [Improved] Fewer false errors on cross-chip findings: if a counterpart spec can't be confirmed from the datasheet, the issue is reported as a Warning with the unverified assumption stated — instead of being overstated as an Error.
- [Fixed] Some review findings could occasionally fail to appear in the report.
## 2.1.0 — 2026-05-19 — Datasheet Reference Highlighting
Datasheet citations now highlight the exact supporting sentence on the PDF page, with more reliable page numbers on large datasheets.
- [Improved] Datasheet references now highlight the exact supporting sentence on the PDF page, not just the page number.
- [Fixed] Datasheet citations landing on the wrong page for large (multi-hundred-page) datasheets.
- [Fixed] Reviewed findings losing their checked state on page refresh.
## 2.0.1 — 2026-05-01 — Flagging & Onboarding
One-click flags on finding cards, an onboarding survey for new users, and small UI polish.
- [New] Report findings with one click via the flag button on any finding card.
- [New] Onboarding survey for new users to help us improve the product.
- [Improved] Comment input box now fills available width.
## 2.0.0 — 2026-04-29 — Public Changelog
- [New] Initial public changelog.
@@ -0,0 +1,187 @@
# File Upload Guide
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. 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 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; Periscope doesn't need this)
The BOM below shows the shape Periscope is looking for.
## The BOM
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 (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**
- **Value** or **Comment** — passive values (`10uF`, `4.7k`, `8MHz`). Used for passive value resolution and mismatch detection when the MPN can't be matched.
- **Footprint** — used to enrich the design graph.
CSV and XLSX both work. For XLSX, the first worksheet is used.
## The Netlist
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
This is the default format most EDA tools can export. It looks like this:
```text
*PADS-PCB*
*PART*
U1 LQFP48
C1 0402
...
*NET*
*SIGNAL* GND
U1.1 U1.48 C1.2
*SIGNAL* VCC
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`) — 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 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. 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.
## Exporting from your EDA tool
### KiCad
Works with KiCad 7.x, 8.x, and 9.x.
**Netlist**
1. Open the schematic in eeschema.
2. **File → Export → Netlist…**
3. Select the **PADS** tab → **Export Netlist**.
4. Save the resulting `.net` file and upload it directly.
**BOM**
1. In eeschema: **Tools → Generate BOM…**
2. Use the built-in `bom_csv_grouped_by_value_with_fp` plugin (or similar) — it produces a CSV with Reference, Value, Footprint, and any custom MPN field on your symbols.
3. If your symbols don't have an MPN field yet, add one via **Edit → Edit Symbol Fields** before generating the BOM.
### Altium Designer
**Netlist**
1. In the Schematic Editor: **Design → Netlist for Project → PADS**.
2. The `.NET` file lands in `Project Outputs for …`. Upload it directly.
**BOM**
1. **Reports → Bill of Materials**.
2. In the template, include `Designator`, `Manufacturer Part Number` (or your equivalent parameter), `Comment` (= value), and `Footprint`.
3. **Export** → CSV or Excel.
### OrCAD / Allegro
For OrCAD Capture and Allegro Design Entry.
**Netlist**
1. **Tools → Create Netlist** → format **PADS-PCB (.asc)**.
2. Save to the project output directory.
**BOM**
1. **Tools → Bill of Materials** → configure columns to include `Reference`, `Manufacturer Part Number`, and `Value`.
2. Export CSV.
### Xpedition
Works in **Xpedition Designer / DxDesigner** (VX.2.x, including VX.2.14). Xpedition and PADS are both Siemens EDA tools and share a netlist exchange format — but the PADS netlist export is only available in projects created with the **Netlist** project type. Projects on the integrated Xpedition flow (which forward directly into Xpedition Layout) don't expose PADS as a layout target.
**Netlist**
1. Open the schematic in Xpedition Designer / DxDesigner.
2. **Setup → Settings → Layout Tool** → confirm **PADS** (or PADS Professional) is selected. If this option isn't available, the project was created as an Integrated/Expedition project — create a new Netlist-type project and import the schematics into it.
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 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** (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 Periscope drops them)
4. Save as `<design>.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.
**BOM**
1. **Reports → Bill of Materials** in Xpedition Designer, or run the Variant Manager BOM export.
2. Include `Reference Designator`, `Part Number` (manufacturer part), `Value`, and `Footprint` columns.
3. Export CSV or XLSX.
### EasyEDA
For both EasyEDA Standard and EasyEDA Pro.
**Netlist**
1. EasyEDA Std: **File → Export → PADS-PCB Netlist (.asc)**.
2. EasyEDA Pro: **Design → Output → Netlist → PADS-PCB**.
**BOM**
1. **Fabrication → BOM** → export CSV.
2. Confirm the Manufacturer Part Number column is populated — it comes from the LCSC supplier data or from a custom MPN attribute you've set on each part.
### Autodesk Eagle
**Netlist**
1. In the schematic editor: **File → Run ULP → `pads-pcb.ulp`** (or **File → Export → Netlist → PADS** in newer builds).
2. Save and upload as-is.
**BOM**
1. **File → Export → Bill of Materials** → CSV.
2. Ensure your parts carry an `MPN` or `MFR_PART` attribute so the column ends up in the export.
## Troubleshooting
- **"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 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 — 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.
+67
View File
@@ -0,0 +1,67 @@
# Periscope Privacy Policy
**Last updated: 20 September 2026**
This Privacy Policy explains how Michele Bigi ("Operator," "we," "us") collects, uses, and discloses information in connection with the Periscope instance at [periscope.michelebigi.it](https://periscope.michelebigi.it) (the "Service").
Faradworks, Inc. does **not** operate this instance and is not the controller for Personal Data processed here.
## 1. Roles
The Operator is the controller for Personal Data used to run this instance (for example account email, authentication identifiers, and server logs).
If you upload Personal Data inside design files on behalf of a business, you may be the controller of that data and the Operator may process it as a processor. A DPA can be discussed at [mikbigi@gmail.com](mailto:mikbigi@gmail.com).
## 2. Information we collect
Only what is reasonably necessary:
- **Account:** name, email, password hashes / session tokens, optional organization
- **Customer Content:** netlists, schematics, datasheets, PCB files, chat context
- **Usage:** request logs, pipeline progress, API cost estimates for your runs
- **Contact form:** name, email, message
If billing or Stripe is not enabled on this instance, we do not collect payment card data. We do not store card numbers.
## 3. How we use information
To operate the Service: authenticate you, run reviews, store projects in instance storage, send transactional mail if configured, debug failures you report, and comply with law.
We do **not** use Customer Content to train general-purpose or foundation AI models unless you explicitly agree.
Reviews may send excerpts of datasheets and circuit context to a configured model provider (for example DeepSeek). That provider processes the excerpt under its own terms.
## 4. Sharing
We share information with subprocessors only as needed to host and run the Service (compute, storage, email, AI APIs). We may disclose information if required by law or to protect the Operator and users from abuse or security incidents.
We do not sell Personal Data. We do not share it for cross-context behavioral advertising.
## 5. Retention
Project files and accounts remain until you delete them or the Operator deletes the instance data. Residual backups may persist for a limited operational window. You may request deletion at [mikbigi@gmail.com](mailto:mikbigi@gmail.com).
## 6. Security
TLS in transit, access isolation per user when authentication is enabled, and credential hashing for local accounts. No system is perfectly secure.
## 7. International transfers
This instance and its subprocessors may process data in the European Union and in other regions where providers operate (including outside the EEA). Where required, appropriate safeguards will be used.
## 8. Your rights
Depending on your jurisdiction (including GDPR/UK GDPR and, where applicable, California law), you may have rights to access, correct, delete, restrict, object, and port Personal Data, and to withdraw consent.
Privacy requests: [mikbigi@gmail.com](mailto:mikbigi@gmail.com) — subject line "Privacy Request".
The Service is not directed to children under 13 (or the applicable local threshold).
## 9. Changes
Material changes will be posted on the Service or emailed before they take effect.
## 10. Contact
Michele Bigi
[mikbigi@gmail.com](mailto:mikbigi@gmail.com)
+103
View File
@@ -0,0 +1,103 @@
# Periscope Terms of Service
**Last updated: 20 September 2026**
These Terms of Service ("Terms") govern access to and use of the Periscope instance at [periscope.michelebigi.it](https://periscope.michelebigi.it) and related services operated by Michele Bigi (the "Service").
They incorporate the [Privacy Policy](/privacy).
By creating an account, clicking to accept these Terms, or otherwise accessing or using the Service, you agree to be bound by these Terms. If you do not agree, do not use the Service.
## 1. Operator and software lineage
**"Operator," "we," and "us"** means Michele Bigi, operator of this Periscope instance.
This Service is **not** operated by Faradworks, Inc. Faradworks has no obligation, warranty, or support duty for this instance.
The software is derived from the open-source [Faradworks/Pinscope](https://github.com/Faradworks/Pinscope) project and is licensed under the **GNU Affero General Public License v3.0** (see the `LICENSE` file in the source repository). That AGPL license is unchanged. Attribution to Faradworks/Pinscope does not transfer operation, trademarks, or commercial licensing of the upstream project to the Operator. For a commercial license of **upstream Pinscope**, contact Faradworks as they publish it (historically `dev@faradworks.com`).
## 2. Definitions
**"Customer Content"** means files, data, netlists, schematics, datasheets, design files, chat inputs, or other materials you upload or submit.
**"Outputs"** means analyses, checks, summaries, recommendations, or responses generated by the Service from Customer Content.
**"Derived Data"** means technical artifacts generated solely to operate the Service (parsed text, indexes, extracted metadata).
## 3. Informational use; no professional advice
The Service analyzes submitted electrical design files and datasheets using automated systems, including machine learning models. Outputs are **informational**, provided "AS IS" and "AS AVAILABLE".
The Service does **not** provide professional engineering, safety, regulatory, certification, legal, or compliance advice. You are solely responsible for independently reviewing and validating Outputs before fabrication, procurement, or deployment.
AI-generated Outputs are probabilistic and may be inaccurate, incomplete, or misleading.
## 4. Accounts
You are responsible for credentials and activity under your account. Notify the Operator at [mikbigi@gmail.com](mailto:mikbigi@gmail.com) if you suspect unauthorized access.
If the instance is configured without authentication, anyone who can reach it may have full access — do not expose that mode to the public internet.
## 5. Ownership; licenses
You retain all right, title, and interest in your Customer Content. To the extent permitted by law, you own Outputs generated from your Customer Content. That does not confer ownership of the Service software, models, or methods.
The Operator (and upstream licensors under AGPL) retain their rights in the Service software.
You grant the Operator a limited, non-exclusive license to host, store, process, and transmit Customer Content and Derived Data **solely** to provide, maintain, secure, and support this instance.
The Operator will not use Customer Content to train, fine-tune, or improve any general-purpose or foundation AI model unless you separately and explicitly agree.
If you opt in through a support or feedback flow, submitted materials may be used to debug and improve **this** instance. You may withdraw that consent prospectively by emailing [mikbigi@gmail.com](mailto:mikbigi@gmail.com).
Feedback may be used without restriction or obligation.
## 6. Prohibited data and acceptable use
Do not upload: protected health information; payment card data; bank secrets; government ID numbers; special-category personal data under GDPR; malware; content you lack rights to use; or material intended to disrupt the Service.
Do not circumvent security, access others' data, scrape the Service to build a competing product, or use the Service unlawfully.
The Operator may suspend or terminate access for violations, abuse, legal constraints, or security reasons.
## 7. High-risk applications
The Service is not designed for life-critical or safety-critical systems (medical devices, automotive safety, aerospace, nuclear, weapons). If you use it in connection with such systems, you do so at your own risk.
## 8. Third parties
The Service may call third-party providers (including AI model APIs, hosting, and storage). Their terms apply to their services. The Operator is not responsible for third-party outages.
## 9. Confidentiality and security
Customer Content is treated as confidential and is not disclosed except to operate the Service (including subprocessors), as required by law, or with your consent.
No system is perfectly secure. The Operator does not guarantee that unauthorized access or data loss will never occur.
If you file a support request, the Operator may access the related project and diagnostics to investigate.
## 10. Disclaimers and liability
TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SERVICE AND OUTPUTS ARE PROVIDED "AS IS" WITHOUT WARRANTIES OF ANY KIND, INCLUDING MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
TO THE FULLEST EXTENT PERMITTED BY LAW, THE OPERATOR IS NOT LIABLE FOR INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, OR PUNITIVE DAMAGES, OR FOR HARDWARE FAILURES OR SAFETY INCIDENTS RELATED TO DESIGNS REVIEWED WITH THE SERVICE.
THE OPERATOR'S TOTAL LIABILITY FOR ALL CLAIMS RELATING TO THE SERVICE WILL NOT EXCEED THE FEES YOU PAID THE OPERATOR FOR THE SERVICE IN THE TWELVE MONTHS BEFORE THE CLAIM, OR ONE HUNDRED EUROS (€100) IF YOU PAID NO FEES.
Nothing excludes liability that cannot be limited under applicable law (including mandatory Italian consumer protections, where they apply).
## 11. Governing law
These Terms are governed by the laws of Italy, without regard to conflict-of-law rules. Courts of Italy have jurisdiction, subject to mandatory consumer venue rules.
Before filing a claim (other than for injunctive relief), contact [mikbigi@gmail.com](mailto:mikbigi@gmail.com) and allow 30 days for an informal resolution attempt.
## 12. Changes
Material changes will be announced in the Service or by email. Continued use after the effective date constitutes acceptance. If you do not agree, stop using the Service.
## 13. Contact
Michele Bigi — Periscope operator
[mikbigi@gmail.com](mailto:mikbigi@gmail.com)
[https://periscope.michelebigi.it](https://periscope.michelebigi.it)
+1
View File
@@ -36,6 +36,7 @@ RUN npm ci --omit=dev
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/src ./src
COPY --from=builder /app/content ./content
COPY --from=builder /app/package.json ./package.json
COPY --from=builder /app/next.config.ts ./next.config.ts
@@ -0,0 +1,43 @@
"""Live-served markdown overlays from periscope/src/frontend/content."""
from __future__ import annotations
from pathlib import Path
from backend.repo_paths import changelog_paths
ROOT = Path(__file__).resolve().parents[1]
SRC = ROOT / "periscope" / "src" / "frontend" / "content"
def test_content_markdown_is_src():
for name in ("changelog.md", "privacy.md", "terms.md", "file-guide.md"):
path = SRC / name
assert path.is_file(), name
text = path.read_text(encoding="utf-8")
assert "Native Periscope overlay" not in text[:400]
assert "Periscope" in text
def test_legal_pages_are_operator_not_faradworks_controller():
privacy = (SRC / "privacy.md").read_text(encoding="utf-8")
terms = (SRC / "terms.md").read_text(encoding="utf-8")
assert "Michele Bigi" in privacy
assert "Michele Bigi" in terms
assert "Faradworks, Inc. does **not** operate this instance" in privacy
assert "This Service is **not** operated by Faradworks, Inc." in terms
def test_changelog_stamp_is_2_56_0_and_src_wins():
text = (SRC / "changelog.md").read_text(encoding="utf-8")
assert "## 2.56.0 — 2026-09-20" in text
first = changelog_paths()[0]
assert first.parts[-3:] == ("src", "frontend", "content") or first.name == "changelog.md"
assert first == SRC / "changelog.md"
def test_docker_copies_src_changelog_last():
docker = (ROOT / "periscope" / "src" / "backend" / "Dockerfile").read_text(encoding="utf-8")
assert "COPY periscope/src/frontend/content/changelog.md /app/changelog.md" in docker
fe = (ROOT / "periscope" / "src" / "frontend" / "dockerfile").read_text(encoding="utf-8")
assert "COPY --from=builder /app/content ./content" in fe